1 /* NetHack 3.6 cmd.c $NHDT-Date: 1575245052 2019/12/02 00:04:12 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.350 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2013. */
4 /* NetHack may be freely redistributed. See license for details. */
5
6 #include "hack.h"
7 #include "lev.h"
8 #include "func_tab.h"
9
10 /* Macros for meta and ctrl modifiers:
11 * M and C return the meta/ctrl code for the given character;
12 * e.g., (C('c') is ctrl-c
13 */
14 #ifndef M
15 #ifndef NHSTDC
16 #define M(c) (0x80 | (c))
17 #else
18 #define M(c) ((c) - 128)
19 #endif /* NHSTDC */
20 #endif
21
22 #ifndef C
23 #define C(c) (0x1f & (c))
24 #endif
25
26 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
27 #define unmeta(c) (0x7f & (c))
28
29 #ifdef ALTMETA
30 STATIC_VAR boolean alt_esc = FALSE;
31 #endif
32
33 struct cmd Cmd = { 0 }; /* flag.h */
34
35 extern const char *hu_stat[]; /* hunger status from eat.c */
36 extern const char *enc_stat[]; /* encumbrance status from botl.c */
37
38 #ifdef UNIX
39 /*
40 * Some systems may have getchar() return EOF for various reasons, and
41 * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
42 */
43 #if defined(SYSV) || defined(DGUX) || defined(HPUX)
44 #define NR_OF_EOFS 20
45 #endif
46 #endif
47
48 #define CMD_TRAVEL (char) 0x90
49 #define CMD_CLICKLOOK (char) 0x8F
50
51 #ifdef DEBUG
52 extern int NDECL(wiz_debug_cmd_bury);
53 #endif
54
55 #ifdef DUMB /* stuff commented out in extern.h, but needed here */
56 extern int NDECL(doapply); /**/
57 extern int NDECL(dorub); /**/
58 extern int NDECL(dojump); /**/
59 extern int NDECL(doextlist); /**/
60 extern int NDECL(enter_explore_mode); /**/
61 extern int NDECL(dodrop); /**/
62 extern int NDECL(doddrop); /**/
63 extern int NDECL(dodown); /**/
64 extern int NDECL(doup); /**/
65 extern int NDECL(donull); /**/
66 extern int NDECL(dowipe); /**/
67 extern int NDECL(docallcnd); /**/
68 extern int NDECL(dotakeoff); /**/
69 extern int NDECL(doremring); /**/
70 extern int NDECL(dowear); /**/
71 extern int NDECL(doputon); /**/
72 extern int NDECL(doddoremarm); /**/
73 extern int NDECL(dokick); /**/
74 extern int NDECL(dofire); /**/
75 extern int NDECL(dothrow); /**/
76 extern int NDECL(doeat); /**/
77 extern int NDECL(done2); /**/
78 extern int NDECL(vanquished); /**/
79 extern int NDECL(doengrave); /**/
80 extern int NDECL(dopickup); /**/
81 extern int NDECL(ddoinv); /**/
82 extern int NDECL(dotypeinv); /**/
83 extern int NDECL(dolook); /**/
84 extern int NDECL(doprgold); /**/
85 extern int NDECL(doprwep); /**/
86 extern int NDECL(doprarm); /**/
87 extern int NDECL(doprring); /**/
88 extern int NDECL(dopramulet); /**/
89 extern int NDECL(doprtool); /**/
90 extern int NDECL(dosuspend); /**/
91 extern int NDECL(doforce); /**/
92 extern int NDECL(doopen); /**/
93 extern int NDECL(doclose); /**/
94 extern int NDECL(dosh); /**/
95 extern int NDECL(dodiscovered); /**/
96 extern int NDECL(doclassdisco); /**/
97 extern int NDECL(doset); /**/
98 extern int NDECL(dotogglepickup); /**/
99 extern int NDECL(dowhatis); /**/
100 extern int NDECL(doquickwhatis); /**/
101 extern int NDECL(dowhatdoes); /**/
102 extern int NDECL(dohelp); /**/
103 extern int NDECL(dohistory); /**/
104 extern int NDECL(doloot); /**/
105 extern int NDECL(dodrink); /**/
106 extern int NDECL(dodip); /**/
107 extern int NDECL(dosacrifice); /**/
108 extern int NDECL(dopray); /**/
109 extern int NDECL(dotip); /**/
110 extern int NDECL(doturn); /**/
111 extern int NDECL(doredraw); /**/
112 extern int NDECL(doread); /**/
113 extern int NDECL(dosave); /**/
114 extern int NDECL(dosearch); /**/
115 extern int NDECL(doidtrap); /**/
116 extern int NDECL(dopay); /**/
117 extern int NDECL(dosit); /**/
118 extern int NDECL(dotalk); /**/
119 extern int NDECL(docast); /**/
120 extern int NDECL(dovspell); /**/
121 extern int NDECL(dotelecmd); /**/
122 extern int NDECL(dountrap); /**/
123 extern int NDECL(doversion); /**/
124 extern int NDECL(doextversion); /**/
125 extern int NDECL(doswapweapon); /**/
126 extern int NDECL(dowield); /**/
127 extern int NDECL(dowieldquiver); /**/
128 extern int NDECL(dozap); /**/
129 extern int NDECL(doorganize); /**/
130 #endif /* DUMB */
131
132 static int NDECL((*timed_occ_fn));
133
134 STATIC_PTR int NDECL(dosuspend_core);
135 STATIC_PTR int NDECL(dosh_core);
136 STATIC_PTR int NDECL(doherecmdmenu);
137 STATIC_PTR int NDECL(dotherecmdmenu);
138 STATIC_PTR int NDECL(doprev_message);
139 STATIC_PTR int NDECL(timed_occupation);
140 STATIC_PTR int NDECL(doextcmd);
141 STATIC_PTR int NDECL(dotravel);
142 STATIC_PTR int NDECL(doterrain);
143 STATIC_PTR int NDECL(wiz_wish);
144 STATIC_PTR int NDECL(wiz_identify);
145 STATIC_PTR int NDECL(wiz_intrinsic);
146 STATIC_PTR int NDECL(wiz_map);
147 STATIC_PTR int NDECL(wiz_makemap);
148 STATIC_PTR int NDECL(wiz_genesis);
149 STATIC_PTR int NDECL(wiz_where);
150 STATIC_PTR int NDECL(wiz_detect);
151 STATIC_PTR int NDECL(wiz_panic);
152 STATIC_PTR int NDECL(wiz_polyself);
153 STATIC_PTR int NDECL(wiz_level_tele);
154 STATIC_PTR int NDECL(wiz_level_change);
155 STATIC_PTR int NDECL(wiz_show_seenv);
156 STATIC_PTR int NDECL(wiz_show_vision);
157 STATIC_PTR int NDECL(wiz_smell);
158 STATIC_PTR int NDECL(wiz_show_wmodes);
159 STATIC_DCL void NDECL(wiz_map_levltyp);
160 STATIC_DCL void NDECL(wiz_levltyp_legend);
161 #if defined(__BORLANDC__) && !defined(_WIN32)
162 extern void FDECL(show_borlandc_stats, (winid));
163 #endif
164 #ifdef DEBUG_MIGRATING_MONS
165 STATIC_PTR int NDECL(wiz_migrate_mons);
166 #endif
167 STATIC_DCL int FDECL(size_monst, (struct monst *, BOOLEAN_P));
168 STATIC_DCL int FDECL(size_obj, (struct obj *));
169 STATIC_DCL void FDECL(count_obj, (struct obj *, long *, long *,
170 BOOLEAN_P, BOOLEAN_P));
171 STATIC_DCL void FDECL(obj_chain, (winid, const char *, struct obj *,
172 BOOLEAN_P, long *, long *));
173 STATIC_DCL void FDECL(mon_invent_chain, (winid, const char *, struct monst *,
174 long *, long *));
175 STATIC_DCL void FDECL(mon_chain, (winid, const char *, struct monst *,
176 BOOLEAN_P, long *, long *));
177 STATIC_DCL void FDECL(contained_stats, (winid, const char *, long *, long *));
178 STATIC_DCL void FDECL(misc_stats, (winid, long *, long *));
179 STATIC_PTR int NDECL(wiz_show_stats);
180 STATIC_DCL boolean FDECL(accept_menu_prefix, (int NDECL((*))));
181 STATIC_PTR int NDECL(wiz_rumor_check);
182 STATIC_PTR int NDECL(doattributes);
183
184 STATIC_DCL void FDECL(enlght_out, (const char *));
185 STATIC_DCL void FDECL(enlght_line, (const char *, const char *, const char *,
186 const char *));
187 STATIC_DCL char *FDECL(enlght_combatinc, (const char *, int, int, char *));
188 STATIC_DCL void FDECL(enlght_halfdmg, (int, int));
189 STATIC_DCL boolean NDECL(walking_on_water);
190 STATIC_DCL boolean FDECL(cause_known, (int));
191 STATIC_DCL char *FDECL(attrval, (int, int, char *));
192 STATIC_DCL void FDECL(background_enlightenment, (int, int));
193 STATIC_DCL void FDECL(basics_enlightenment, (int, int));
194 STATIC_DCL void FDECL(characteristics_enlightenment, (int, int));
195 STATIC_DCL void FDECL(one_characteristic, (int, int, int));
196 STATIC_DCL void FDECL(status_enlightenment, (int, int));
197 STATIC_DCL void FDECL(attributes_enlightenment, (int, int));
198
199 STATIC_DCL void FDECL(add_herecmd_menuitem, (winid, int NDECL((*)),
200 const char *));
201 STATIC_DCL char FDECL(here_cmd_menu, (BOOLEAN_P));
202 STATIC_DCL char FDECL(there_cmd_menu, (BOOLEAN_P, int, int));
203 STATIC_DCL char *NDECL(parse);
204 STATIC_DCL void FDECL(show_direction_keys, (winid, CHAR_P, BOOLEAN_P));
205 STATIC_DCL boolean FDECL(help_dir, (CHAR_P, int, const char *));
206
207 static const char *readchar_queue = "";
208 static coord clicklook_cc;
209 /* for rejecting attempts to use wizard mode commands */
210 static const char unavailcmd[] = "Unavailable command '%s'.";
211 /* for rejecting #if !SHELL, !SUSPEND */
212 static const char cmdnotavail[] = "'%s' command not available.";
213
214 STATIC_PTR int
doprev_message(VOID_ARGS)215 doprev_message(VOID_ARGS)
216 {
217 return nh_doprev_message();
218 }
219
220 /* Count down by decrementing multi */
221 STATIC_PTR int
timed_occupation(VOID_ARGS)222 timed_occupation(VOID_ARGS)
223 {
224 (*timed_occ_fn)();
225 if (multi > 0)
226 multi--;
227 return multi > 0;
228 }
229
230 /* If you have moved since initially setting some occupations, they
231 * now shouldn't be able to restart.
232 *
233 * The basic rule is that if you are carrying it, you can continue
234 * since it is with you. If you are acting on something at a distance,
235 * your orientation to it must have changed when you moved.
236 *
237 * The exception to this is taking off items, since they can be taken
238 * off in a number of ways in the intervening time, screwing up ordering.
239 *
240 * Currently: Take off all armor.
241 * Picking Locks / Forcing Chests.
242 * Setting traps.
243 */
244 void
reset_occupations()245 reset_occupations()
246 {
247 reset_remarm();
248 reset_pick();
249 reset_trapset();
250 }
251
252 /* If a time is given, use it to timeout this function, otherwise the
253 * function times out by its own means.
254 */
255 void
256 set_occupation(fn, txt, xtime)
257 int NDECL((*fn));
258 const char *txt;
259 int xtime;
260 {
261 if (xtime) {
262 occupation = timed_occupation;
263 timed_occ_fn = fn;
264 } else
265 occupation = fn;
266 occtxt = txt;
267 occtime = 0;
268 return;
269 }
270
271 STATIC_DCL char NDECL(popch);
272
273 /* Provide a means to redo the last command. The flag `in_doagain' is set
274 * to true while redoing the command. This flag is tested in commands that
275 * require additional input (like `throw' which requires a thing and a
276 * direction), and the input prompt is not shown. Also, while in_doagain is
277 * TRUE, no keystrokes can be saved into the saveq.
278 */
279 #define BSIZE 20
280 static char pushq[BSIZE], saveq[BSIZE];
281 static NEARDATA int phead, ptail, shead, stail;
282
283 STATIC_OVL char
popch()284 popch()
285 {
286 /* If occupied, return '\0', letting tgetch know a character should
287 * be read from the keyboard. If the character read is not the
288 * ABORT character (as checked in pcmain.c), that character will be
289 * pushed back on the pushq.
290 */
291 if (occupation)
292 return '\0';
293 if (in_doagain)
294 return (char) ((shead != stail) ? saveq[stail++] : '\0');
295 else
296 return (char) ((phead != ptail) ? pushq[ptail++] : '\0');
297 }
298
299 char
pgetchar()300 pgetchar() /* courtesy of aeb@cwi.nl */
301 {
302 register int ch;
303
304 if (iflags.debug_fuzzer)
305 return randomkey();
306 if (!(ch = popch()))
307 ch = nhgetch();
308 return (char) ch;
309 }
310
311 /* A ch == 0 resets the pushq */
312 void
pushch(ch)313 pushch(ch)
314 char ch;
315 {
316 if (!ch)
317 phead = ptail = 0;
318 if (phead < BSIZE)
319 pushq[phead++] = ch;
320 return;
321 }
322
323 /* A ch == 0 resets the saveq. Only save keystrokes when not
324 * replaying a previous command.
325 */
326 void
savech(ch)327 savech(ch)
328 char ch;
329 {
330 if (!in_doagain) {
331 if (!ch)
332 phead = ptail = shead = stail = 0;
333 else if (shead < BSIZE)
334 saveq[shead++] = ch;
335 }
336 return;
337 }
338
339 /* here after # - now read a full-word command */
340 STATIC_PTR int
doextcmd(VOID_ARGS)341 doextcmd(VOID_ARGS)
342 {
343 int idx, retval;
344 int NDECL((*func));
345
346 /* keep repeating until we don't run help or quit */
347 do {
348 idx = get_ext_cmd();
349 if (idx < 0)
350 return 0; /* quit */
351
352 func = extcmdlist[idx].ef_funct;
353 if (!wizard && (extcmdlist[idx].flags & WIZMODECMD)) {
354 You("can't do that.");
355 return 0;
356 }
357 if (iflags.menu_requested && !accept_menu_prefix(func)) {
358 pline("'%s' prefix has no effect for the %s command.",
359 visctrl(Cmd.spkeys[NHKF_REQMENU]),
360 extcmdlist[idx].ef_txt);
361 iflags.menu_requested = FALSE;
362 }
363 retval = (*func)();
364 } while (func == doextlist);
365
366 return retval;
367 }
368
369 /* here after #? - now list all full-word commands and provid
370 some navigation capability through the long list */
371 int
doextlist(VOID_ARGS)372 doextlist(VOID_ARGS)
373 {
374 register const struct ext_func_tab *efp;
375 char buf[BUFSZ], searchbuf[BUFSZ], promptbuf[QBUFSZ];
376 winid menuwin;
377 anything any;
378 menu_item *selected;
379 int n, pass;
380 int menumode = 0, menushown[2], onelist = 0;
381 boolean redisplay = TRUE, search = FALSE;
382 static const char *headings[] = { "Extended commands",
383 "Debugging Extended Commands" };
384
385 searchbuf[0] = '\0';
386 menuwin = create_nhwindow(NHW_MENU);
387
388 while (redisplay) {
389 redisplay = FALSE;
390 any = zeroany;
391 start_menu(menuwin);
392 add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
393 "Extended Commands List", MENU_UNSELECTED);
394 add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
395 "", MENU_UNSELECTED);
396
397 Strcpy(buf, menumode ? "Show" : "Hide");
398 Strcat(buf, " commands that don't autocomplete");
399 if (!menumode)
400 Strcat(buf, " (those not marked with [A])");
401 any.a_int = 1;
402 add_menu(menuwin, NO_GLYPH, &any, 'a', 0, ATR_NONE, buf,
403 MENU_UNSELECTED);
404
405 if (!*searchbuf) {
406 any.a_int = 2;
407 /* was 's', but then using ':' handling within the interface
408 would only examine the two or three meta entries, not the
409 actual list of extended commands shown via separator lines;
410 having ':' as an explicit selector overrides the default
411 menu behavior for it; we retain 's' as a group accelerator */
412 add_menu(menuwin, NO_GLYPH, &any, ':', 's', ATR_NONE,
413 "Search extended commands", MENU_UNSELECTED);
414 } else {
415 Strcpy(buf, "Show all, clear search");
416 if (strlen(buf) + strlen(searchbuf) + strlen(" (\"\")") < QBUFSZ)
417 Sprintf(eos(buf), " (\"%s\")", searchbuf);
418 any.a_int = 3;
419 /* specifying ':' as a group accelerator here is mostly a
420 statement of intent (we'd like to accept it as a synonym but
421 also want to hide it from general menu use) because it won't
422 work for interfaces which support ':' to search; use as a
423 general menu command takes precedence over group accelerator */
424 add_menu(menuwin, NO_GLYPH, &any, 's', ':', ATR_NONE,
425 buf, MENU_UNSELECTED);
426 }
427 if (wizard) {
428 any.a_int = 4;
429 add_menu(menuwin, NO_GLYPH, &any, 'z', 0, ATR_NONE,
430 onelist ? "Show debugging commands in separate section"
431 : "Show all alphabetically, including debugging commands",
432 MENU_UNSELECTED);
433 }
434 any = zeroany;
435 add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
436 "", MENU_UNSELECTED);
437 menushown[0] = menushown[1] = 0;
438 n = 0;
439 for (pass = 0; pass <= 1; ++pass) {
440 /* skip second pass if not in wizard mode or wizard mode
441 commands are being integrated into a single list */
442 if (pass == 1 && (onelist || !wizard))
443 break;
444 for (efp = extcmdlist; efp->ef_txt; efp++) {
445 int wizc;
446
447 if ((efp->flags & CMD_NOT_AVAILABLE) != 0)
448 continue;
449 /* if hiding non-autocomplete commands, skip such */
450 if (menumode == 1 && (efp->flags & AUTOCOMPLETE) == 0)
451 continue;
452 /* if searching, skip this command if it doesn't match */
453 if (*searchbuf
454 /* first try case-insensitive substring match */
455 && !strstri(efp->ef_txt, searchbuf)
456 && !strstri(efp->ef_desc, searchbuf)
457 /* wildcard support; most interfaces use case-insensitve
458 pmatch rather than regexp for menu searching */
459 && !pmatchi(searchbuf, efp->ef_txt)
460 && !pmatchi(searchbuf, efp->ef_desc))
461 continue;
462 /* skip wizard mode commands if not in wizard mode;
463 when showing two sections, skip wizard mode commands
464 in pass==0 and skip other commands in pass==1 */
465 wizc = (efp->flags & WIZMODECMD) != 0;
466 if (wizc && !wizard)
467 continue;
468 if (!onelist && pass != wizc)
469 continue;
470
471 /* We're about to show an item, have we shown the menu yet?
472 Doing menu in inner loop like this on demand avoids a
473 heading with no subordinate entries on the search
474 results menu. */
475 if (!menushown[pass]) {
476 Strcpy(buf, headings[pass]);
477 add_menu(menuwin, NO_GLYPH, &any, 0, 0,
478 iflags.menu_headings, buf, MENU_UNSELECTED);
479 menushown[pass] = 1;
480 }
481 Sprintf(buf, " %-14s %-3s %s",
482 efp->ef_txt,
483 (efp->flags & AUTOCOMPLETE) ? "[A]" : " ",
484 efp->ef_desc);
485 add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
486 buf, MENU_UNSELECTED);
487 ++n;
488 }
489 if (n)
490 add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
491 "", MENU_UNSELECTED);
492 }
493 if (*searchbuf && !n)
494 add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
495 "no matches", MENU_UNSELECTED);
496
497 end_menu(menuwin, (char *) 0);
498 n = select_menu(menuwin, PICK_ONE, &selected);
499 if (n > 0) {
500 switch (selected[0].item.a_int) {
501 case 1: /* 'a': toggle show/hide non-autocomplete */
502 menumode = 1 - menumode; /* toggle 0 -> 1, 1 -> 0 */
503 redisplay = TRUE;
504 break;
505 case 2: /* ':' when not searching yet: enable search */
506 search = TRUE;
507 break;
508 case 3: /* 's' when already searching: disable search */
509 search = FALSE;
510 searchbuf[0] = '\0';
511 redisplay = TRUE;
512 break;
513 case 4: /* 'z': toggle showing wizard mode commands separately */
514 search = FALSE;
515 searchbuf[0] = '\0';
516 onelist = 1 - onelist; /* toggle 0 -> 1, 1 -> 0 */
517 redisplay = TRUE;
518 break;
519 }
520 free((genericptr_t) selected);
521 } else {
522 search = FALSE;
523 searchbuf[0] = '\0';
524 }
525 if (search) {
526 Strcpy(promptbuf, "Extended command list search phrase");
527 Strcat(promptbuf, "?");
528 getlin(promptbuf, searchbuf);
529 (void) mungspaces(searchbuf);
530 if (searchbuf[0] == '\033')
531 searchbuf[0] = '\0';
532 if (*searchbuf)
533 redisplay = TRUE;
534 search = FALSE;
535 }
536 }
537 destroy_nhwindow(menuwin);
538 return 0;
539 }
540
541 #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
542 #define MAX_EXT_CMD 200 /* Change if we ever have more ext cmds */
543
544 /*
545 * This is currently used only by the tty interface and is
546 * controlled via runtime option 'extmenu'. (Most other interfaces
547 * already use a menu all the time for extended commands.)
548 *
549 * ``# ?'' is counted towards the limit of the number of commands,
550 * so we actually support MAX_EXT_CMD-1 "real" extended commands.
551 *
552 * Here after # - now show pick-list of possible commands.
553 */
554 int
extcmd_via_menu()555 extcmd_via_menu()
556 {
557 const struct ext_func_tab *efp;
558 menu_item *pick_list = (menu_item *) 0;
559 winid win;
560 anything any;
561 const struct ext_func_tab *choices[MAX_EXT_CMD + 1];
562 char buf[BUFSZ];
563 char cbuf[QBUFSZ], prompt[QBUFSZ], fmtstr[20];
564 int i, n, nchoices, acount;
565 int ret, len, biggest;
566 int accelerator, prevaccelerator;
567 int matchlevel = 0;
568 boolean wastoolong, one_per_line;
569
570 ret = 0;
571 cbuf[0] = '\0';
572 biggest = 0;
573 while (!ret) {
574 i = n = 0;
575 any = zeroany;
576 /* populate choices */
577 for (efp = extcmdlist; efp->ef_txt; efp++) {
578 if ((efp->flags & CMD_NOT_AVAILABLE)
579 || !(efp->flags & AUTOCOMPLETE)
580 || (!wizard && (efp->flags & WIZMODECMD)))
581 continue;
582 if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) {
583 choices[i] = efp;
584 if ((len = (int) strlen(efp->ef_desc)) > biggest)
585 biggest = len;
586 if (++i > MAX_EXT_CMD) {
587 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
588 impossible(
589 "Exceeded %d extended commands in doextcmd() menu; 'extmenu' disabled.",
590 MAX_EXT_CMD);
591 #endif /* NH_DEVEL_STATUS != NH_STATUS_RELEASED */
592 iflags.extmenu = 0;
593 return -1;
594 }
595 }
596 }
597 choices[i] = (struct ext_func_tab *) 0;
598 nchoices = i;
599 /* if we're down to one, we have our selection so get out of here */
600 if (nchoices <= 1) {
601 ret = (nchoices == 1) ? (int) (choices[0] - extcmdlist) : -1;
602 break;
603 }
604
605 /* otherwise... */
606 win = create_nhwindow(NHW_MENU);
607 start_menu(win);
608 Sprintf(fmtstr, "%%-%ds", biggest + 15);
609 prompt[0] = '\0';
610 wastoolong = FALSE; /* True => had to wrap due to line width
611 * ('w' in wizard mode) */
612 /* -3: two line menu header, 1 line menu footer (for prompt) */
613 one_per_line = (nchoices < ROWNO - 3);
614 accelerator = prevaccelerator = 0;
615 acount = 0;
616 for (i = 0; choices[i]; ++i) {
617 accelerator = choices[i]->ef_txt[matchlevel];
618 if (accelerator != prevaccelerator || one_per_line)
619 wastoolong = FALSE;
620 if (accelerator != prevaccelerator || one_per_line
621 || (acount >= 2
622 /* +4: + sizeof " or " - sizeof "" */
623 && (strlen(prompt) + 4 + strlen(choices[i]->ef_txt)
624 /* -6: enough room for 1 space left margin
625 * + "%c - " menu selector + 1 space right margin */
626 >= min(sizeof prompt, COLNO - 6)))) {
627 if (acount) {
628 /* flush extended cmds for that letter already in buf */
629 Sprintf(buf, fmtstr, prompt);
630 any.a_char = prevaccelerator;
631 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE,
632 buf, FALSE);
633 acount = 0;
634 if (!(accelerator != prevaccelerator || one_per_line))
635 wastoolong = TRUE;
636 }
637 }
638 prevaccelerator = accelerator;
639 if (!acount || one_per_line) {
640 Sprintf(prompt, "%s%s [%s]", wastoolong ? "or " : "",
641 choices[i]->ef_txt, choices[i]->ef_desc);
642 } else if (acount == 1) {
643 Sprintf(prompt, "%s%s or %s", wastoolong ? "or " : "",
644 choices[i - 1]->ef_txt, choices[i]->ef_txt);
645 } else {
646 Strcat(prompt, " or ");
647 Strcat(prompt, choices[i]->ef_txt);
648 }
649 ++acount;
650 }
651 if (acount) {
652 /* flush buf */
653 Sprintf(buf, fmtstr, prompt);
654 any.a_char = prevaccelerator;
655 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE, buf,
656 FALSE);
657 }
658 Sprintf(prompt, "Extended Command: %s", cbuf);
659 end_menu(win, prompt);
660 n = select_menu(win, PICK_ONE, &pick_list);
661 destroy_nhwindow(win);
662 if (n == 1) {
663 if (matchlevel > (QBUFSZ - 2)) {
664 free((genericptr_t) pick_list);
665 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
666 impossible("Too many chars (%d) entered in extcmd_via_menu()",
667 matchlevel);
668 #endif
669 ret = -1;
670 } else {
671 cbuf[matchlevel++] = pick_list[0].item.a_char;
672 cbuf[matchlevel] = '\0';
673 free((genericptr_t) pick_list);
674 }
675 } else {
676 if (matchlevel) {
677 ret = 0;
678 matchlevel = 0;
679 } else
680 ret = -1;
681 }
682 }
683 return ret;
684 }
685 #endif /* TTY_GRAPHICS */
686
687 /* #monster command - use special monster ability while polymorphed */
688 int
domonability(VOID_ARGS)689 domonability(VOID_ARGS)
690 {
691 if (can_breathe(youmonst.data))
692 return dobreathe();
693 else if (attacktype(youmonst.data, AT_SPIT))
694 return dospit();
695 else if (youmonst.data->mlet == S_NYMPH)
696 return doremove();
697 else if (attacktype(youmonst.data, AT_GAZE))
698 return dogaze();
699 else if (is_were(youmonst.data))
700 return dosummon();
701 else if (webmaker(youmonst.data))
702 return dospinweb();
703 else if (is_hider(youmonst.data))
704 return dohide();
705 else if (is_mind_flayer(youmonst.data))
706 return domindblast();
707 else if (u.umonnum == PM_GREMLIN) {
708 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
709 if (split_mon(&youmonst, (struct monst *) 0))
710 dryup(u.ux, u.uy, TRUE);
711 } else
712 There("is no fountain here.");
713 } else if (is_unicorn(youmonst.data)) {
714 use_unicorn_horn((struct obj *) 0);
715 return 1;
716 } else if (youmonst.data->msound == MS_SHRIEK) {
717 You("shriek.");
718 if (u.uburied)
719 pline("Unfortunately sound does not carry well through rock.");
720 else
721 aggravate();
722 } else if (youmonst.data->mlet == S_VAMPIRE)
723 return dopoly();
724 else if (Upolyd)
725 pline("Any special ability you may have is purely reflexive.");
726 else
727 You("don't have a special ability in your normal form!");
728 return 0;
729 }
730
731 int
enter_explore_mode(VOID_ARGS)732 enter_explore_mode(VOID_ARGS)
733 {
734 if (wizard) {
735 You("are in debug mode.");
736 } else if (discover) {
737 You("are already in explore mode.");
738 } else {
739 #ifdef SYSCF
740 #if defined(UNIX)
741 if (!sysopt.explorers || !sysopt.explorers[0]
742 || !check_user_string(sysopt.explorers)) {
743 You("cannot access explore mode.");
744 return 0;
745 }
746 #endif
747 #endif
748 pline(
749 "Beware! From explore mode there will be no return to normal game.");
750 if (paranoid_query(ParanoidQuit,
751 "Do you want to enter explore mode?")) {
752 clear_nhwindow(WIN_MESSAGE);
753 You("are now in non-scoring explore mode.");
754 discover = TRUE;
755 } else {
756 clear_nhwindow(WIN_MESSAGE);
757 pline("Resuming normal game.");
758 }
759 }
760 return 0;
761 }
762
763 /* ^W command - wish for something */
764 STATIC_PTR int
wiz_wish(VOID_ARGS)765 wiz_wish(VOID_ARGS) /* Unlimited wishes for debug mode by Paul Polderman */
766 {
767 if (wizard) {
768 boolean save_verbose = flags.verbose;
769
770 flags.verbose = FALSE;
771 makewish();
772 flags.verbose = save_verbose;
773 (void) encumber_msg();
774 } else
775 pline(unavailcmd, visctrl((int) cmd_from_func(wiz_wish)));
776 return 0;
777 }
778
779 /* ^I command - reveal and optionally identify hero's inventory */
780 STATIC_PTR int
wiz_identify(VOID_ARGS)781 wiz_identify(VOID_ARGS)
782 {
783 if (wizard) {
784 iflags.override_ID = (int) cmd_from_func(wiz_identify);
785 /* command remapping might leave #wizidentify as the only way
786 to invoke us, in which case cmd_from_func() will yield NUL;
787 it won't matter to display_inventory()/display_pickinv()
788 if ^I invokes some other command--what matters is that
789 display_pickinv() and xname() see override_ID as nonzero */
790 if (!iflags.override_ID)
791 iflags.override_ID = C('I');
792 (void) display_inventory((char *) 0, FALSE);
793 iflags.override_ID = 0;
794 } else
795 pline(unavailcmd, visctrl((int) cmd_from_func(wiz_identify)));
796 return 0;
797 }
798
799 /* #wizmakemap - discard current dungeon level and replace with a new one */
800 STATIC_PTR int
wiz_makemap(VOID_ARGS)801 wiz_makemap(VOID_ARGS)
802 {
803 if (wizard) {
804 struct monst *mtmp;
805 boolean was_in_W_tower = In_W_tower(u.ux, u.uy, &u.uz);
806
807 rm_mapseen(ledger_no(&u.uz));
808 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
809 if (mtmp->isgd) { /* vault is going away; get rid of guard */
810 mtmp->isgd = 0;
811 mongone(mtmp);
812 }
813 if (DEADMONSTER(mtmp))
814 continue;
815 if (mtmp->isshk)
816 setpaid(mtmp);
817 /* TODO?
818 * Reduce 'born' tally for each monster about to be discarded
819 * by savelev(), otherwise replacing heavily populated levels
820 * tends to make their inhabitants become extinct.
821 */
822 }
823 if (Punished) {
824 ballrelease(FALSE);
825 unplacebc();
826 }
827 /* reset lock picking unless it's for a carried container */
828 maybe_reset_pick((struct obj *) 0);
829 /* reset interrupted digging if it was taking place on this level */
830 if (on_level(&context.digging.level, &u.uz))
831 (void) memset((genericptr_t) &context.digging, 0,
832 sizeof (struct dig_info));
833 /* reset cached targets */
834 iflags.travelcc.x = iflags.travelcc.y = 0; /* travel destination */
835 context.polearm.hitmon = (struct monst *) 0; /* polearm target */
836 /* escape from trap */
837 reset_utrap(FALSE);
838 check_special_room(TRUE); /* room exit */
839 u.ustuck = (struct monst *) 0;
840 u.uswallow = 0;
841 u.uinwater = 0;
842 u.uundetected = 0; /* not hidden, even if means are available */
843 dmonsfree(); /* purge dead monsters from 'fmon' */
844 /* keep steed and other adjacent pets after releasing them
845 from traps, stopping eating, &c as if hero were ascending */
846 keepdogs(TRUE); /* (pets-only; normally we'd be using 'FALSE' here) */
847
848 /* discard current level; "saving" is used to release dynamic data */
849 savelev(-1, ledger_no(&u.uz), FREE_SAVE);
850 /* create a new level; various things like bestowing a guardian
851 angel on Astral or setting off alarm on Ft.Ludios are handled
852 by goto_level(do.c) so won't occur for replacement levels */
853 mklev();
854
855 vision_reset();
856 vision_full_recalc = 1;
857 cls();
858 /* was using safe_teleds() but that doesn't honor arrival region
859 on levels which have such; we don't force stairs, just area */
860 u_on_rndspot((u.uhave.amulet ? 1 : 0) /* 'going up' flag */
861 | (was_in_W_tower ? 2 : 0));
862 losedogs();
863 kill_genocided_monsters();
864 /* u_on_rndspot() might pick a spot that has a monster, or losedogs()
865 might pick the hero's spot (only if there isn't already a monster
866 there), so we might have to move hero or the co-located monster */
867 if ((mtmp = m_at(u.ux, u.uy)) != 0)
868 u_collide_m(mtmp);
869 initrack();
870 if (Punished) {
871 unplacebc();
872 placebc();
873 }
874 docrt();
875 flush_screen(1);
876 deliver_splev_message(); /* level entry */
877 check_special_room(FALSE); /* room entry */
878 #ifdef INSURANCE
879 save_currentstate();
880 #endif
881 } else {
882 pline(unavailcmd, "#wizmakemap");
883 }
884 return 0;
885 }
886
887 /* ^F command - reveal the level map and any traps on it */
888 STATIC_PTR int
wiz_map(VOID_ARGS)889 wiz_map(VOID_ARGS)
890 {
891 if (wizard) {
892 struct trap *t;
893 long save_Hconf = HConfusion, save_Hhallu = HHallucination;
894
895 HConfusion = HHallucination = 0L;
896 for (t = ftrap; t != 0; t = t->ntrap) {
897 t->tseen = 1;
898 map_trap(t, TRUE);
899 }
900 do_mapping();
901 HConfusion = save_Hconf;
902 HHallucination = save_Hhallu;
903 } else
904 pline(unavailcmd, visctrl((int) cmd_from_func(wiz_map)));
905 return 0;
906 }
907
908 /* ^G command - generate monster(s); a count prefix will be honored */
909 STATIC_PTR int
wiz_genesis(VOID_ARGS)910 wiz_genesis(VOID_ARGS)
911 {
912 if (wizard)
913 (void) create_particular();
914 else
915 pline(unavailcmd, visctrl((int) cmd_from_func(wiz_genesis)));
916 return 0;
917 }
918
919 /* ^O command - display dungeon layout */
920 STATIC_PTR int
wiz_where(VOID_ARGS)921 wiz_where(VOID_ARGS)
922 {
923 if (wizard)
924 (void) print_dungeon(FALSE, (schar *) 0, (xchar *) 0);
925 else
926 pline(unavailcmd, visctrl((int) cmd_from_func(wiz_where)));
927 return 0;
928 }
929
930 /* ^E command - detect unseen (secret doors, traps, hidden monsters) */
931 STATIC_PTR int
wiz_detect(VOID_ARGS)932 wiz_detect(VOID_ARGS)
933 {
934 if (wizard)
935 (void) findit();
936 else
937 pline(unavailcmd, visctrl((int) cmd_from_func(wiz_detect)));
938 return 0;
939 }
940
941 /* ^V command - level teleport */
942 STATIC_PTR int
wiz_level_tele(VOID_ARGS)943 wiz_level_tele(VOID_ARGS)
944 {
945 if (wizard)
946 level_tele();
947 else
948 pline(unavailcmd, visctrl((int) cmd_from_func(wiz_level_tele)));
949 return 0;
950 }
951
952 /* #levelchange command - adjust hero's experience level */
953 STATIC_PTR int
wiz_level_change(VOID_ARGS)954 wiz_level_change(VOID_ARGS)
955 {
956 char buf[BUFSZ] = DUMMY;
957 int newlevel = 0;
958 int ret;
959
960 getlin("To what experience level do you want to be set?", buf);
961 (void) mungspaces(buf);
962 if (buf[0] == '\033' || buf[0] == '\0')
963 ret = 0;
964 else
965 ret = sscanf(buf, "%d", &newlevel);
966
967 if (ret != 1) {
968 pline1(Never_mind);
969 return 0;
970 }
971 if (newlevel == u.ulevel) {
972 You("are already that experienced.");
973 } else if (newlevel < u.ulevel) {
974 if (u.ulevel == 1) {
975 You("are already as inexperienced as you can get.");
976 return 0;
977 }
978 if (newlevel < 1)
979 newlevel = 1;
980 while (u.ulevel > newlevel)
981 losexp("#levelchange");
982 } else {
983 if (u.ulevel >= MAXULEV) {
984 You("are already as experienced as you can get.");
985 return 0;
986 }
987 if (newlevel > MAXULEV)
988 newlevel = MAXULEV;
989 while (u.ulevel < newlevel)
990 pluslvl(FALSE);
991 }
992 u.ulevelmax = u.ulevel;
993 return 0;
994 }
995
996 /* #panic command - test program's panic handling */
997 STATIC_PTR int
wiz_panic(VOID_ARGS)998 wiz_panic(VOID_ARGS)
999 {
1000 if (iflags.debug_fuzzer) {
1001 u.uhp = u.uhpmax = 1000;
1002 u.uen = u.uenmax = 1000;
1003 return 0;
1004 }
1005 if (paranoid_query(ParanoidQuit,
1006 "Do you want to call panic() and end your game?"))
1007 panic("Crash test.");
1008 return 0;
1009 }
1010
1011 /* #polyself command - change hero's form */
1012 STATIC_PTR int
wiz_polyself(VOID_ARGS)1013 wiz_polyself(VOID_ARGS)
1014 {
1015 polyself(1);
1016 return 0;
1017 }
1018
1019 /* #seenv command */
1020 STATIC_PTR int
wiz_show_seenv(VOID_ARGS)1021 wiz_show_seenv(VOID_ARGS)
1022 {
1023 winid win;
1024 int x, y, v, startx, stopx, curx;
1025 char row[COLNO + 1];
1026
1027 win = create_nhwindow(NHW_TEXT);
1028 /*
1029 * Each seenv description takes up 2 characters, so center
1030 * the seenv display around the hero.
1031 */
1032 startx = max(1, u.ux - (COLNO / 4));
1033 stopx = min(startx + (COLNO / 2), COLNO);
1034 /* can't have a line exactly 80 chars long */
1035 if (stopx - startx == COLNO / 2)
1036 startx++;
1037
1038 for (y = 0; y < ROWNO; y++) {
1039 for (x = startx, curx = 0; x < stopx; x++, curx += 2) {
1040 if (x == u.ux && y == u.uy) {
1041 row[curx] = row[curx + 1] = '@';
1042 } else {
1043 v = levl[x][y].seenv & 0xff;
1044 if (v == 0)
1045 row[curx] = row[curx + 1] = ' ';
1046 else
1047 Sprintf(&row[curx], "%02x", v);
1048 }
1049 }
1050 /* remove trailing spaces */
1051 for (x = curx - 1; x >= 0; x--)
1052 if (row[x] != ' ')
1053 break;
1054 row[x + 1] = '\0';
1055
1056 putstr(win, 0, row);
1057 }
1058 display_nhwindow(win, TRUE);
1059 destroy_nhwindow(win);
1060 return 0;
1061 }
1062
1063 /* #vision command */
1064 STATIC_PTR int
wiz_show_vision(VOID_ARGS)1065 wiz_show_vision(VOID_ARGS)
1066 {
1067 winid win;
1068 int x, y, v;
1069 char row[COLNO + 1];
1070
1071 win = create_nhwindow(NHW_TEXT);
1072 Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
1073 COULD_SEE, IN_SIGHT, TEMP_LIT);
1074 putstr(win, 0, row);
1075 putstr(win, 0, "");
1076 for (y = 0; y < ROWNO; y++) {
1077 for (x = 1; x < COLNO; x++) {
1078 if (x == u.ux && y == u.uy)
1079 row[x] = '@';
1080 else {
1081 v = viz_array[y][x]; /* data access should be hidden */
1082 if (v == 0)
1083 row[x] = ' ';
1084 else
1085 row[x] = '0' + viz_array[y][x];
1086 }
1087 }
1088 /* remove trailing spaces */
1089 for (x = COLNO - 1; x >= 1; x--)
1090 if (row[x] != ' ')
1091 break;
1092 row[x + 1] = '\0';
1093
1094 putstr(win, 0, &row[1]);
1095 }
1096 display_nhwindow(win, TRUE);
1097 destroy_nhwindow(win);
1098 return 0;
1099 }
1100
1101 /* #wmode command */
1102 STATIC_PTR int
wiz_show_wmodes(VOID_ARGS)1103 wiz_show_wmodes(VOID_ARGS)
1104 {
1105 winid win;
1106 int x, y;
1107 char row[COLNO + 1];
1108 struct rm *lev;
1109 boolean istty = WINDOWPORT("tty");
1110
1111 win = create_nhwindow(NHW_TEXT);
1112 if (istty)
1113 putstr(win, 0, ""); /* tty only: blank top line */
1114 for (y = 0; y < ROWNO; y++) {
1115 for (x = 0; x < COLNO; x++) {
1116 lev = &levl[x][y];
1117 if (x == u.ux && y == u.uy)
1118 row[x] = '@';
1119 else if (IS_WALL(lev->typ) || lev->typ == SDOOR)
1120 row[x] = '0' + (lev->wall_info & WM_MASK);
1121 else if (lev->typ == CORR)
1122 row[x] = '#';
1123 else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ))
1124 row[x] = '.';
1125 else
1126 row[x] = 'x';
1127 }
1128 row[COLNO] = '\0';
1129 /* map column 0, levl[0][], is off the left edge of the screen */
1130 putstr(win, 0, &row[1]);
1131 }
1132 display_nhwindow(win, TRUE);
1133 destroy_nhwindow(win);
1134 return 0;
1135 }
1136
1137 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
1138 STATIC_OVL void
wiz_map_levltyp(VOID_ARGS)1139 wiz_map_levltyp(VOID_ARGS)
1140 {
1141 winid win;
1142 int x, y, terrain;
1143 char row[COLNO + 1];
1144 boolean istty = !strcmp(windowprocs.name, "tty");
1145
1146 win = create_nhwindow(NHW_TEXT);
1147 /* map row 0, levl[][0], is drawn on the second line of tty screen */
1148 if (istty)
1149 putstr(win, 0, ""); /* tty only: blank top line */
1150 for (y = 0; y < ROWNO; y++) {
1151 /* map column 0, levl[0][], is off the left edge of the screen;
1152 it should always have terrain type "undiggable stone" */
1153 for (x = 1; x < COLNO; x++) {
1154 terrain = levl[x][y].typ;
1155 /* assumes there aren't more than 10+26+26 terrain types */
1156 row[x - 1] = (char) ((terrain == STONE && !may_dig(x, y))
1157 ? '*'
1158 : (terrain < 10)
1159 ? '0' + terrain
1160 : (terrain < 36)
1161 ? 'a' + terrain - 10
1162 : 'A' + terrain - 36);
1163 }
1164 x--;
1165 if (levl[0][y].typ != STONE || may_dig(0, y))
1166 row[x++] = '!';
1167 row[x] = '\0';
1168 putstr(win, 0, row);
1169 }
1170
1171 {
1172 char dsc[BUFSZ];
1173 s_level *slev = Is_special(&u.uz);
1174
1175 Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel);
1176 /* [dungeon branch features currently omitted] */
1177 /* special level features */
1178 if (slev) {
1179 Sprintf(eos(dsc), " \"%s\"", slev->proto);
1180 /* special level flags (note: dungeon.def doesn't set `maze'
1181 or `hell' for any specific levels so those never show up) */
1182 if (slev->flags.maze_like)
1183 Strcat(dsc, " mazelike");
1184 if (slev->flags.hellish)
1185 Strcat(dsc, " hellish");
1186 if (slev->flags.town)
1187 Strcat(dsc, " town");
1188 if (slev->flags.rogue_like)
1189 Strcat(dsc, " roguelike");
1190 /* alignment currently omitted to save space */
1191 }
1192 /* level features */
1193 if (level.flags.nfountains)
1194 Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym,
1195 (int) level.flags.nfountains);
1196 if (level.flags.nsinks)
1197 Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym,
1198 (int) level.flags.nsinks);
1199 if (level.flags.has_vault)
1200 Strcat(dsc, " vault");
1201 if (level.flags.has_shop)
1202 Strcat(dsc, " shop");
1203 if (level.flags.has_temple)
1204 Strcat(dsc, " temple");
1205 if (level.flags.has_court)
1206 Strcat(dsc, " throne");
1207 if (level.flags.has_zoo)
1208 Strcat(dsc, " zoo");
1209 if (level.flags.has_morgue)
1210 Strcat(dsc, " morgue");
1211 if (level.flags.has_barracks)
1212 Strcat(dsc, " barracks");
1213 if (level.flags.has_beehive)
1214 Strcat(dsc, " hive");
1215 if (level.flags.has_swamp)
1216 Strcat(dsc, " swamp");
1217 /* level flags */
1218 if (level.flags.noteleport)
1219 Strcat(dsc, " noTport");
1220 if (level.flags.hardfloor)
1221 Strcat(dsc, " noDig");
1222 if (level.flags.nommap)
1223 Strcat(dsc, " noMMap");
1224 if (!level.flags.hero_memory)
1225 Strcat(dsc, " noMem");
1226 if (level.flags.shortsighted)
1227 Strcat(dsc, " shortsight");
1228 if (level.flags.graveyard)
1229 Strcat(dsc, " graveyard");
1230 if (level.flags.is_maze_lev)
1231 Strcat(dsc, " maze");
1232 if (level.flags.is_cavernous_lev)
1233 Strcat(dsc, " cave");
1234 if (level.flags.arboreal)
1235 Strcat(dsc, " tree");
1236 if (Sokoban)
1237 Strcat(dsc, " sokoban-rules");
1238 /* non-flag info; probably should include dungeon branching
1239 checks (extra stairs and magic portals) here */
1240 if (Invocation_lev(&u.uz))
1241 Strcat(dsc, " invoke");
1242 if (On_W_tower_level(&u.uz))
1243 Strcat(dsc, " tower");
1244 /* append a branch identifier for completeness' sake */
1245 if (u.uz.dnum == 0)
1246 Strcat(dsc, " dungeon");
1247 else if (u.uz.dnum == mines_dnum)
1248 Strcat(dsc, " mines");
1249 else if (In_sokoban(&u.uz))
1250 Strcat(dsc, " sokoban");
1251 else if (u.uz.dnum == quest_dnum)
1252 Strcat(dsc, " quest");
1253 else if (Is_knox(&u.uz))
1254 Strcat(dsc, " ludios");
1255 else if (u.uz.dnum == 1)
1256 Strcat(dsc, " gehennom");
1257 else if (u.uz.dnum == tower_dnum)
1258 Strcat(dsc, " vlad");
1259 else if (In_endgame(&u.uz))
1260 Strcat(dsc, " endgame");
1261 else {
1262 /* somebody's added a dungeon branch we're not expecting */
1263 const char *brname = dungeons[u.uz.dnum].dname;
1264
1265 if (!brname || !*brname)
1266 brname = "unknown";
1267 if (!strncmpi(brname, "the ", 4))
1268 brname += 4;
1269 Sprintf(eos(dsc), " %s", brname);
1270 }
1271 /* limit the line length to map width */
1272 if (strlen(dsc) >= COLNO)
1273 dsc[COLNO - 1] = '\0'; /* truncate */
1274 putstr(win, 0, dsc);
1275 }
1276
1277 display_nhwindow(win, TRUE);
1278 destroy_nhwindow(win);
1279 return;
1280 }
1281
1282 /* temporary? hack, since level type codes aren't the same as screen
1283 symbols and only the latter have easily accessible descriptions */
1284 static const char *levltyp[] = {
1285 "stone", "vertical wall", "horizontal wall", "top-left corner wall",
1286 "top-right corner wall", "bottom-left corner wall",
1287 "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall",
1288 "tee-left wall", "tee-right wall", "drawbridge wall", "tree",
1289 "secret door", "secret corridor", "pool", "moat", "water",
1290 "drawbridge up", "lava pool", "iron bars", "door", "corridor", "room",
1291 "stairs", "ladder", "fountain", "throne", "sink", "grave", "altar", "ice",
1292 "drawbridge down", "air", "cloud",
1293 /* not a real terrain type, but used for undiggable stone
1294 by wiz_map_levltyp() */
1295 "unreachable/undiggable",
1296 /* padding in case the number of entries above is odd */
1297 ""
1298 };
1299
1300 /* explanation of base-36 output from wiz_map_levltyp() */
1301 STATIC_OVL void
wiz_levltyp_legend(VOID_ARGS)1302 wiz_levltyp_legend(VOID_ARGS)
1303 {
1304 winid win;
1305 int i, j, last, c;
1306 const char *dsc, *fmt;
1307 char buf[BUFSZ];
1308
1309 win = create_nhwindow(NHW_TEXT);
1310 putstr(win, 0, "#terrain encodings:");
1311 putstr(win, 0, "");
1312 fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
1313 *buf = '\0';
1314 /* output in pairs, left hand column holds [0],[1],...,[N/2-1]
1315 and right hand column holds [N/2],[N/2+1],...,[N-1];
1316 N ('last') will always be even, and may or may not include
1317 the empty string entry to pad out the final pair, depending
1318 upon how many other entries are present in levltyp[] */
1319 last = SIZE(levltyp) & ~1;
1320 for (i = 0; i < last / 2; ++i)
1321 for (j = i; j < last; j += last / 2) {
1322 dsc = levltyp[j];
1323 c = !*dsc ? ' '
1324 : !strncmp(dsc, "unreachable", 11) ? '*'
1325 /* same int-to-char conversion as wiz_map_levltyp() */
1326 : (j < 10) ? '0' + j
1327 : (j < 36) ? 'a' + j - 10
1328 : 'A' + j - 36;
1329 Sprintf(eos(buf), fmt, c, dsc);
1330 if (j > i) {
1331 putstr(win, 0, buf);
1332 *buf = '\0';
1333 }
1334 }
1335 display_nhwindow(win, TRUE);
1336 destroy_nhwindow(win);
1337 return;
1338 }
1339
1340 /* #wizsmell command - test usmellmon(). */
1341 STATIC_PTR int
wiz_smell(VOID_ARGS)1342 wiz_smell(VOID_ARGS)
1343 {
1344 int ans = 0;
1345 int mndx; /* monster index */
1346 coord cc; /* screen pos of unknown glyph */
1347 int glyph; /* glyph at selected position */
1348
1349 cc.x = u.ux;
1350 cc.y = u.uy;
1351 mndx = 0; /* gcc -Wall lint */
1352 if (!olfaction(youmonst.data)) {
1353 You("are incapable of detecting odors in your present form.");
1354 return 0;
1355 }
1356
1357 pline("You can move the cursor to a monster that you want to smell.");
1358 do {
1359 pline("Pick a monster to smell.");
1360 ans = getpos(&cc, TRUE, "a monster");
1361 if (ans < 0 || cc.x < 0) {
1362 return 0; /* done */
1363 }
1364 /* Convert the glyph at the selected position to a mndxbol. */
1365 glyph = glyph_at(cc.x, cc.y);
1366 if (glyph_is_monster(glyph))
1367 mndx = glyph_to_mon(glyph);
1368 else
1369 mndx = 0;
1370 /* Is it a monster? */
1371 if (mndx) {
1372 if (!usmellmon(&mons[mndx]))
1373 pline("That monster seems to give off no smell.");
1374 } else
1375 pline("That is not a monster.");
1376 } while (TRUE);
1377 return 0;
1378 }
1379
1380 /* #wizinstrinsic command to set some intrinsics for testing */
1381 STATIC_PTR int
wiz_intrinsic(VOID_ARGS)1382 wiz_intrinsic(VOID_ARGS)
1383 {
1384 if (wizard) {
1385 extern const struct propname {
1386 int prop_num;
1387 const char *prop_name;
1388 } propertynames[]; /* timeout.c */
1389 static const char wizintrinsic[] = "#wizintrinsic";
1390 static const char fmt[] = "You are%s %s.";
1391 winid win;
1392 anything any;
1393 char buf[BUFSZ];
1394 int i, j, n, p, amt, typ;
1395 long oldtimeout, newtimeout;
1396 const char *propname;
1397 menu_item *pick_list = (menu_item *) 0;
1398
1399 any = zeroany;
1400 win = create_nhwindow(NHW_MENU);
1401 start_menu(win);
1402 for (i = 0; (propname = propertynames[i].prop_name) != 0; ++i) {
1403 p = propertynames[i].prop_num;
1404 if (p == HALLUC_RES) {
1405 /* Grayswandir vs hallucination; ought to be redone to
1406 use u.uprops[HALLUC].blocked instead of being treated
1407 as a separate property; letting in be manually toggled
1408 even only in wizard mode would be asking for trouble... */
1409 continue;
1410 }
1411 if (p == FIRE_RES) {
1412 any.a_int = 0;
1413 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "--", FALSE);
1414 }
1415 any.a_int = i + 1; /* +1: avoid 0 */
1416 oldtimeout = u.uprops[p].intrinsic & TIMEOUT;
1417 if (oldtimeout)
1418 Sprintf(buf, "%-27s [%li]", propname, oldtimeout);
1419 else
1420 Sprintf(buf, "%s", propname);
1421 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1422 }
1423 end_menu(win, "Which intrinsics?");
1424 n = select_menu(win, PICK_ANY, &pick_list);
1425 destroy_nhwindow(win);
1426
1427 amt = 30; /* TODO: prompt for duration */
1428 for (j = 0; j < n; ++j) {
1429 i = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */
1430 p = propertynames[i].prop_num;
1431 oldtimeout = u.uprops[p].intrinsic & TIMEOUT;
1432 newtimeout = oldtimeout + (long) amt;
1433 switch (p) {
1434 case SICK:
1435 case SLIMED:
1436 case STONED:
1437 if (oldtimeout > 0L && newtimeout > oldtimeout)
1438 newtimeout = oldtimeout;
1439 break;
1440 }
1441
1442 switch (p) {
1443 case BLINDED:
1444 make_blinded(newtimeout, TRUE);
1445 break;
1446 #if 0 /* make_confused() only gives feedback when confusion is
1447 * ending so use the 'default' case for it instead */
1448 case CONFUSION:
1449 make_confused(newtimeout, TRUE);
1450 break;
1451 #endif /*0*/
1452 case DEAF:
1453 make_deaf(newtimeout, TRUE);
1454 break;
1455 case HALLUC:
1456 make_hallucinated(newtimeout, TRUE, 0L);
1457 break;
1458 case SICK:
1459 typ = !rn2(2) ? SICK_VOMITABLE : SICK_NONVOMITABLE;
1460 make_sick(newtimeout, wizintrinsic, TRUE, typ);
1461 break;
1462 case SLIMED:
1463 Sprintf(buf, fmt,
1464 !Slimed ? "" : " still", "turning into slime");
1465 make_slimed(newtimeout, buf);
1466 break;
1467 case STONED:
1468 Sprintf(buf, fmt,
1469 !Stoned ? "" : " still", "turning into stone");
1470 make_stoned(newtimeout, buf, KILLED_BY, wizintrinsic);
1471 break;
1472 case STUNNED:
1473 make_stunned(newtimeout, TRUE);
1474 break;
1475 case VOMITING:
1476 Sprintf(buf, fmt, !Vomiting ? "" : " still", "vomiting");
1477 make_vomiting(newtimeout, FALSE);
1478 pline1(buf);
1479 break;
1480 case WARN_OF_MON:
1481 if (!Warn_of_mon) {
1482 context.warntype.speciesidx = PM_GRID_BUG;
1483 context.warntype.species
1484 = &mons[context.warntype.speciesidx];
1485 }
1486 goto def_feedback;
1487 case GLIB:
1488 /* slippery fingers applies to gloves if worn at the time
1489 so persistent inventory might need updating */
1490 make_glib((int) newtimeout);
1491 goto def_feedback;
1492 case LEVITATION:
1493 case FLYING:
1494 float_vs_flight();
1495 /*FALLTHRU*/
1496 default:
1497 def_feedback:
1498 pline("Timeout for %s %s %d.", propertynames[i].prop_name,
1499 oldtimeout ? "increased by" : "set to", amt);
1500 if (p != GLIB)
1501 incr_itimeout(&u.uprops[p].intrinsic, amt);
1502 break;
1503 }
1504 context.botl = 1; /* probably not necessary... */
1505 }
1506 if (n >= 1)
1507 free((genericptr_t) pick_list);
1508 doredraw();
1509 } else
1510 pline(unavailcmd, visctrl((int) cmd_from_func(wiz_intrinsic)));
1511 return 0;
1512 }
1513
1514 /* #wizrumorcheck command - verify each rumor access */
1515 STATIC_PTR int
wiz_rumor_check(VOID_ARGS)1516 wiz_rumor_check(VOID_ARGS)
1517 {
1518 rumor_check();
1519 return 0;
1520 }
1521
1522 /* #terrain command -- show known map, inspired by crawl's '|' command */
1523 STATIC_PTR int
doterrain(VOID_ARGS)1524 doterrain(VOID_ARGS)
1525 {
1526 winid men;
1527 menu_item *sel;
1528 anything any;
1529 int n;
1530 int which;
1531
1532 /*
1533 * normal play: choose between known map without mons, obj, and traps
1534 * (to see underlying terrain only), or
1535 * known map without mons and objs (to see traps under mons and objs), or
1536 * known map without mons (to see objects under monsters);
1537 * explore mode: normal choices plus full map (w/o mons, objs, traps);
1538 * wizard mode: normal and explore choices plus
1539 * a dump of the internal levl[][].typ codes w/ level flags, or
1540 * a legend for the levl[][].typ codes dump
1541 */
1542 men = create_nhwindow(NHW_MENU);
1543 start_menu(men);
1544 any = zeroany;
1545 any.a_int = 1;
1546 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1547 "known map without monsters, objects, and traps",
1548 MENU_SELECTED);
1549 any.a_int = 2;
1550 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1551 "known map without monsters and objects",
1552 MENU_UNSELECTED);
1553 any.a_int = 3;
1554 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1555 "known map without monsters",
1556 MENU_UNSELECTED);
1557 if (discover || wizard) {
1558 any.a_int = 4;
1559 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1560 "full map without monsters, objects, and traps",
1561 MENU_UNSELECTED);
1562 if (wizard) {
1563 any.a_int = 5;
1564 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1565 "internal levl[][].typ codes in base-36",
1566 MENU_UNSELECTED);
1567 any.a_int = 6;
1568 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1569 "legend of base-36 levl[][].typ codes",
1570 MENU_UNSELECTED);
1571 }
1572 }
1573 end_menu(men, "View which?");
1574
1575 n = select_menu(men, PICK_ONE, &sel);
1576 destroy_nhwindow(men);
1577 /*
1578 * n < 0: player used ESC to cancel;
1579 * n == 0: preselected entry was explicitly chosen and got toggled off;
1580 * n == 1: preselected entry was implicitly chosen via <space>|<enter>;
1581 * n == 2: another entry was explicitly chosen, so skip preselected one.
1582 */
1583 which = (n < 0) ? -1 : (n == 0) ? 1 : sel[0].item.a_int;
1584 if (n > 1 && which == 1)
1585 which = sel[1].item.a_int;
1586 if (n > 0)
1587 free((genericptr_t) sel);
1588
1589 switch (which) {
1590 case 1: /* known map */
1591 reveal_terrain(0, TER_MAP);
1592 break;
1593 case 2: /* known map with known traps */
1594 reveal_terrain(0, TER_MAP | TER_TRP);
1595 break;
1596 case 3: /* known map with known traps and objects */
1597 reveal_terrain(0, TER_MAP | TER_TRP | TER_OBJ);
1598 break;
1599 case 4: /* full map */
1600 reveal_terrain(1, TER_MAP);
1601 break;
1602 case 5: /* map internals */
1603 wiz_map_levltyp();
1604 break;
1605 case 6: /* internal details */
1606 wiz_levltyp_legend();
1607 break;
1608 default:
1609 break;
1610 }
1611 return 0; /* no time elapses */
1612 }
1613
1614 /* -enlightenment and conduct- */
1615 static winid en_win = WIN_ERR;
1616 static boolean en_via_menu = FALSE;
1617 static const char You_[] = "You ", are[] = "are ", were[] = "were ",
1618 have[] = "have ", had[] = "had ", can[] = "can ",
1619 could[] = "could ";
1620 static const char have_been[] = "have been ", have_never[] = "have never ",
1621 never[] = "never ";
1622
1623 #define enl_msg(prefix, present, past, suffix, ps) \
1624 enlght_line(prefix, final ? past : present, suffix, ps)
1625 #define you_are(attr, ps) enl_msg(You_, are, were, attr, ps)
1626 #define you_have(attr, ps) enl_msg(You_, have, had, attr, ps)
1627 #define you_can(attr, ps) enl_msg(You_, can, could, attr, ps)
1628 #define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "")
1629 #define you_have_never(badthing) \
1630 enl_msg(You_, have_never, never, badthing, "")
1631 #define you_have_X(something) \
1632 enl_msg(You_, have, (const char *) "", something, "")
1633
1634 static void
enlght_out(buf)1635 enlght_out(buf)
1636 const char *buf;
1637 {
1638 if (en_via_menu) {
1639 anything any;
1640
1641 any = zeroany;
1642 add_menu(en_win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1643 } else
1644 putstr(en_win, 0, buf);
1645 }
1646
1647 static void
enlght_line(start,middle,end,ps)1648 enlght_line(start, middle, end, ps)
1649 const char *start, *middle, *end, *ps;
1650 {
1651 char buf[BUFSZ];
1652
1653 Sprintf(buf, " %s%s%s%s.", start, middle, end, ps);
1654 enlght_out(buf);
1655 }
1656
1657 /* format increased chance to hit or damage or defense (Protection) */
1658 static char *
enlght_combatinc(inctyp,incamt,final,outbuf)1659 enlght_combatinc(inctyp, incamt, final, outbuf)
1660 const char *inctyp;
1661 int incamt, final;
1662 char *outbuf;
1663 {
1664 const char *modif, *bonus;
1665 boolean invrt;
1666 int absamt;
1667
1668 absamt = abs(incamt);
1669 /* Protection amount is typically larger than damage or to-hit;
1670 reduce magnitude by a third in order to stretch modifier ranges
1671 (small:1..5, moderate:6..10, large:11..19, huge:20+) */
1672 if (!strcmp(inctyp, "defense"))
1673 absamt = (absamt * 2) / 3;
1674
1675 if (absamt <= 3)
1676 modif = "small";
1677 else if (absamt <= 6)
1678 modif = "moderate";
1679 else if (absamt <= 12)
1680 modif = "large";
1681 else
1682 modif = "huge";
1683
1684 modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */
1685 bonus = (incamt >= 0) ? "bonus" : "penalty";
1686 /* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
1687 invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE;
1688
1689 Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus,
1690 invrt ? bonus : inctyp);
1691 if (final || wizard)
1692 Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt);
1693
1694 return outbuf;
1695 }
1696
1697 /* report half physical or half spell damage */
1698 STATIC_OVL void
enlght_halfdmg(category,final)1699 enlght_halfdmg(category, final)
1700 int category;
1701 int final;
1702 {
1703 const char *category_name;
1704 char buf[BUFSZ];
1705
1706 switch (category) {
1707 case HALF_PHDAM:
1708 category_name = "physical";
1709 break;
1710 case HALF_SPDAM:
1711 category_name = "spell";
1712 break;
1713 default:
1714 category_name = "unknown";
1715 break;
1716 }
1717 Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced",
1718 category_name);
1719 enl_msg(You_, "take", "took", buf, from_what(category));
1720 }
1721
1722 /* is hero actively using water walking capability on water (or lava)? */
1723 STATIC_OVL boolean
walking_on_water()1724 walking_on_water()
1725 {
1726 if (u.uinwater || Levitation || Flying)
1727 return FALSE;
1728 return (boolean) (Wwalking
1729 && (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)));
1730 }
1731
1732 /* check whether hero is wearing something that player definitely knows
1733 confers the target property; item must have been seen and its type
1734 discovered but it doesn't necessarily have to be fully identified */
1735 STATIC_OVL boolean
cause_known(propindx)1736 cause_known(propindx)
1737 int propindx; /* index of a property which can be conveyed by worn item */
1738 {
1739 register struct obj *o;
1740 long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL;
1741
1742 /* simpler than from_what()/what_gives(); we don't attempt to
1743 handle artifacts and we deliberately ignore wielded items */
1744 for (o = invent; o; o = o->nobj) {
1745 if (!(o->owornmask & mask))
1746 continue;
1747 if ((int) objects[o->otyp].oc_oprop == propindx
1748 && objects[o->otyp].oc_name_known && o->dknown)
1749 return TRUE;
1750 }
1751 return FALSE;
1752 }
1753
1754 /* format a characteristic value, accommodating Strength's strangeness */
1755 STATIC_OVL char *
attrval(attrindx,attrvalue,resultbuf)1756 attrval(attrindx, attrvalue, resultbuf)
1757 int attrindx, attrvalue;
1758 char resultbuf[]; /* should be at least [7] to hold "18/100\0" */
1759 {
1760 if (attrindx != A_STR || attrvalue <= 18)
1761 Sprintf(resultbuf, "%d", attrvalue);
1762 else if (attrvalue > STR18(100)) /* 19 to 25 */
1763 Sprintf(resultbuf, "%d", attrvalue - 100);
1764 else /* simplify "18/ **" to be "18/100" */
1765 Sprintf(resultbuf, "18/%02d", attrvalue - 18);
1766 return resultbuf;
1767 }
1768
1769 void
enlightenment(mode,final)1770 enlightenment(mode, final)
1771 int mode; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
1772 int final; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
1773 {
1774 char buf[BUFSZ], tmpbuf[BUFSZ];
1775
1776 en_win = create_nhwindow(NHW_MENU);
1777 en_via_menu = !final;
1778 if (en_via_menu)
1779 start_menu(en_win);
1780
1781 Strcpy(tmpbuf, plname);
1782 *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */
1783 /* as in background_enlightenment, when poly'd we need to use the saved
1784 gender in u.mfemale rather than the current you-as-monster gender */
1785 Sprintf(buf, "%s the %s's attributes:", tmpbuf,
1786 ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1787 ? urole.name.f
1788 : urole.name.m);
1789
1790 /* title */
1791 enlght_out(buf); /* "Conan the Archeologist's attributes:" */
1792 /* background and characteristics; ^X or end-of-game disclosure */
1793 if (mode & BASICENLIGHTENMENT) {
1794 /* role, race, alignment, deities, dungeon level, time, experience */
1795 background_enlightenment(mode, final);
1796 /* hit points, energy points, armor class, gold */
1797 basics_enlightenment(mode, final);
1798 /* strength, dexterity, &c */
1799 characteristics_enlightenment(mode, final);
1800 }
1801 /* expanded status line information, including things which aren't
1802 included there due to space considerations--such as obvious
1803 alternative movement indicators (riding, levitation, &c), and
1804 various troubles (turning to stone, trapped, confusion, &c);
1805 shown for both basic and magic enlightenment */
1806 status_enlightenment(mode, final);
1807 /* remaining attributes; shown for potion,&c or wizard mode and
1808 explore mode ^X or end of game disclosure */
1809 if (mode & MAGICENLIGHTENMENT) {
1810 /* intrinsics and other traditional enlightenment feedback */
1811 attributes_enlightenment(mode, final);
1812 }
1813
1814 if (!en_via_menu) {
1815 display_nhwindow(en_win, TRUE);
1816 } else {
1817 menu_item *selected = 0;
1818
1819 end_menu(en_win, (char *) 0);
1820 if (select_menu(en_win, PICK_NONE, &selected) > 0)
1821 free((genericptr_t) selected);
1822 en_via_menu = FALSE;
1823 }
1824 destroy_nhwindow(en_win);
1825 en_win = WIN_ERR;
1826 }
1827
1828 /*ARGSUSED*/
1829 /* display role, race, alignment and such to en_win */
1830 STATIC_OVL void
background_enlightenment(unused_mode,final)1831 background_enlightenment(unused_mode, final)
1832 int unused_mode UNUSED;
1833 int final;
1834 {
1835 const char *role_titl, *rank_titl;
1836 int innategend, difgend, difalgn;
1837 char buf[BUFSZ], tmpbuf[BUFSZ];
1838
1839 /* note that if poly'd, we need to use u.mfemale instead of flags.female
1840 to access hero's saved gender-as-human/elf/&c rather than current one */
1841 innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0;
1842 role_titl = (innategend && urole.name.f) ? urole.name.f : urole.name.m;
1843 rank_titl = rank_of(u.ulevel, Role_switch, innategend);
1844
1845 enlght_out(""); /* separator after title */
1846 enlght_out("Background:");
1847
1848 /* if polymorphed, report current shape before underlying role;
1849 will be repeated as first status: "you are transformed" and also
1850 among various attributes: "you are in beast form" (after being
1851 told about lycanthropy) or "you are polymorphed into <a foo>"
1852 (with countdown timer appended for wizard mode); we really want
1853 the player to know he's not a samurai at the moment... */
1854 if (Upolyd) {
1855 struct permonst *uasmon = youmonst.data;
1856
1857 tmpbuf[0] = '\0';
1858 /* here we always use current gender, not saved role gender */
1859 if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon))
1860 Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj);
1861 Sprintf(buf, "%sin %s%s form", !final ? "currently " : "", tmpbuf,
1862 uasmon->mname);
1863 you_are(buf, "");
1864 }
1865
1866 /* report role; omit gender if it's redundant (eg, "female priestess") */
1867 tmpbuf[0] = '\0';
1868 if (!urole.name.f
1869 && ((urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE)
1870 || innategend != flags.initgend))
1871 Sprintf(tmpbuf, "%s ", genders[innategend].adj);
1872 buf[0] = '\0';
1873 if (Upolyd)
1874 Strcpy(buf, "actually "); /* "You are actually a ..." */
1875 if (!strcmpi(rank_titl, role_titl)) {
1876 /* omit role when rank title matches it */
1877 Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel,
1878 tmpbuf, urace.noun);
1879 } else {
1880 Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel,
1881 tmpbuf, urace.adj, role_titl);
1882 }
1883 you_are(buf, "");
1884
1885 /* report alignment (bypass you_are() in order to omit ending period);
1886 adverb is used to distinguish between temporary change (helm of opp.
1887 alignment), permanent change (one-time conversion), and original */
1888 Sprintf(buf, " %s%s%s, %son a mission for %s",
1889 You_, !final ? are : were,
1890 align_str(u.ualign.type),
1891 /* helm of opposite alignment (might hide conversion) */
1892 (u.ualign.type != u.ualignbase[A_CURRENT])
1893 /* what's the past tense of "currently"? if we used "formerly"
1894 it would sound like a reference to the original alignment */
1895 ? (!final ? "currently " : "temporarily ")
1896 /* permanent conversion */
1897 : (u.ualign.type != u.ualignbase[A_ORIGINAL])
1898 /* and what's the past tense of "now"? certainly not "then"
1899 in a context like this...; "belatedly" == weren't that
1900 way sooner (in other words, didn't start that way) */
1901 ? (!final ? "now " : "belatedly ")
1902 /* atheist (ignored in very early game) */
1903 : (!u.uconduct.gnostic && moves > 1000L)
1904 ? "nominally "
1905 /* lastly, normal case */
1906 : "",
1907 u_gname());
1908 enlght_out(buf);
1909 /* show the rest of this game's pantheon (finishes previous sentence)
1910 [appending "also Moloch" at the end would allow for straightforward
1911 trailing "and" on all three aligned entries but looks too verbose] */
1912 Sprintf(buf, " who %s opposed by", !final ? "is" : "was");
1913 if (u.ualign.type != A_LAWFUL)
1914 Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL),
1915 align_str(A_LAWFUL));
1916 if (u.ualign.type != A_NEUTRAL)
1917 Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL),
1918 align_str(A_NEUTRAL),
1919 (u.ualign.type != A_CHAOTIC) ? " and" : "");
1920 if (u.ualign.type != A_CHAOTIC)
1921 Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC),
1922 align_str(A_CHAOTIC));
1923 Strcat(buf, "."); /* terminate sentence */
1924 enlght_out(buf);
1925
1926 /* show original alignment,gender,race,role if any have been changed;
1927 giving separate message for temporary alignment change bypasses need
1928 for tricky phrasing otherwise necessitated by possibility of having
1929 helm of opposite alignment mask a permanent alignment conversion */
1930 difgend = (innategend != flags.initgend);
1931 difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0)
1932 + ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL])
1933 ? 2 : 0));
1934 if (difalgn & 1) { /* have temporary alignment so report permanent one */
1935 Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT]));
1936 you_are(buf, "");
1937 difalgn &= ~1; /* suppress helm from "started out <foo>" message */
1938 }
1939 if (difgend || difalgn) { /* sex change or perm align change or both */
1940 Sprintf(buf, " You started out %s%s%s.",
1941 difgend ? genders[flags.initgend].adj : "",
1942 (difgend && difalgn) ? " and " : "",
1943 difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : "");
1944 enlght_out(buf);
1945 }
1946
1947 /* As of 3.6.2: dungeon level, so that ^X really has all status info as
1948 claimed by the comment below; this reveals more information than
1949 the basic status display, but that's one of the purposes of ^X;
1950 similar information is revealed by #overview; the "You died in
1951 <location>" given by really_done() is more rudimentary than this */
1952 *buf = *tmpbuf = '\0';
1953 if (In_endgame(&u.uz)) {
1954 int egdepth = observable_depth(&u.uz);
1955
1956 (void) endgamelevelname(tmpbuf, egdepth);
1957 Sprintf(buf, "in the endgame, on the %s%s",
1958 !strncmp(tmpbuf, "Plane", 5) ? "Elemental " : "", tmpbuf);
1959 } else if (Is_knox(&u.uz)) {
1960 /* this gives away the fact that the knox branch is only 1 level */
1961 Sprintf(buf, "on the %s level", dungeons[u.uz.dnum].dname);
1962 /* TODO? maybe phrase it differently when actually inside the fort,
1963 if we're able to determine that (not trivial) */
1964 } else {
1965 char dgnbuf[QBUFSZ];
1966
1967 Strcpy(dgnbuf, dungeons[u.uz.dnum].dname);
1968 if (!strncmpi(dgnbuf, "The ", 4))
1969 *dgnbuf = lowc(*dgnbuf);
1970 Sprintf(tmpbuf, "level %d",
1971 In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1972 /* TODO? maybe extend this bit to include various other automatic
1973 annotations from the dungeon overview code */
1974 if (Is_rogue_level(&u.uz))
1975 Strcat(tmpbuf, ", a primitive area");
1976 else if (Is_bigroom(&u.uz) && !Blind)
1977 Strcat(tmpbuf, ", a very big room");
1978 Sprintf(buf, "in %s, on %s", dgnbuf, tmpbuf);
1979 }
1980 you_are(buf, "");
1981
1982 /* this is shown even if the 'time' option is off */
1983 if (moves == 1L) {
1984 you_have("just started your adventure", "");
1985 } else {
1986 /* 'turns' grates on the nerves in this context... */
1987 Sprintf(buf, "the dungeon %ld turn%s ago", moves, plur(moves));
1988 /* same phrasing for current and final: "entered" is unconditional */
1989 enlght_line(You_, "entered ", buf, "");
1990 }
1991
1992 /* for gameover, these have been obtained in really_done() so that they
1993 won't vary if user leaves a disclosure prompt or --More-- unanswered
1994 long enough for the dynamic value to change between then and now */
1995 if (final ? iflags.at_midnight : midnight()) {
1996 enl_msg("It ", "is ", "was ", "the midnight hour", "");
1997 } else if (final ? iflags.at_night : night()) {
1998 enl_msg("It ", "is ", "was ", "nighttime", "");
1999 }
2000 /* other environmental factors */
2001 if (flags.moonphase == FULL_MOON || flags.moonphase == NEW_MOON) {
2002 /* [This had "tonight" but has been changed to "in effect".
2003 There is a similar issue to Friday the 13th--it's the value
2004 at the start of the current session but that session might
2005 have dragged on for an arbitrary amount of time. We want to
2006 report the values that currently affect play--or affected
2007 play when game ended--rather than actual outside situation.] */
2008 Sprintf(buf, "a %s moon in effect%s",
2009 (flags.moonphase == FULL_MOON) ? "full"
2010 : (flags.moonphase == NEW_MOON) ? "new"
2011 /* showing these would probably just lead to confusion
2012 since they have no effect on game play... */
2013 : (flags.moonphase < FULL_MOON) ? "first quarter"
2014 : "last quarter",
2015 /* we don't have access to 'how' here--aside from survived
2016 vs died--so settle for general platitude */
2017 final ? " when your adventure ended" : "");
2018 enl_msg("There ", "is ", "was ", buf, "");
2019 }
2020 if (flags.friday13) {
2021 /* let player know that friday13 penalty is/was in effect;
2022 we don't say "it is/was Friday the 13th" because that was at
2023 the start of the session and it might be past midnight (or
2024 days later if the game has been paused without save/restore),
2025 so phrase this similar to the start up message */
2026 Sprintf(buf, " Bad things %s on Friday the 13th.",
2027 !final ? "can happen"
2028 : (final == ENL_GAMEOVERALIVE) ? "could have happened"
2029 /* there's no may to tell whether -1 Luck made a
2030 difference but hero has died... */
2031 : "happened");
2032 enlght_out(buf);
2033 }
2034
2035 if (!Upolyd) {
2036 int ulvl = (int) u.ulevel;
2037 /* [flags.showexp currently does not matter; should it?] */
2038
2039 /* experience level is already shown above */
2040 Sprintf(buf, "%-1ld experience point%s", u.uexp, plur(u.uexp));
2041 /* TODO?
2042 * Remove wizard-mode restriction since patient players can
2043 * determine the numbers needed without resorting to spoilers
2044 * (even before this started being disclosed for 'final';
2045 * just enable 'showexp' and look at normal status lines
2046 * after drinking gain level potions or eating wraith corpses
2047 * or being level-drained by vampires).
2048 */
2049 if (ulvl < 30 && (final || wizard)) {
2050 long nxtlvl = newuexp(ulvl), delta = nxtlvl - u.uexp;
2051
2052 Sprintf(eos(buf), ", %ld %s%sneeded %s level %d",
2053 delta, (u.uexp > 0) ? "more " : "",
2054 /* present tense=="needed", past tense=="were needed" */
2055 !final ? "" : (delta == 1L) ? "was " : "were ",
2056 /* "for": grammatically iffy but less likely to wrap */
2057 (ulvl < 18) ? "to attain" : "for", (ulvl + 1));
2058 }
2059 you_have(buf, "");
2060 }
2061 #ifdef SCORE_ON_BOTL
2062 if (flags.showscore) {
2063 /* describes what's shown on status line, which is an approximation;
2064 only show it here if player has the 'showscore' option enabled */
2065 Sprintf(buf, "%ld%s", botl_score(),
2066 !final ? "" : " before end-of-game adjustments");
2067 enl_msg("Your score ", "is ", "was ", buf, "");
2068 }
2069 #endif
2070 }
2071
2072 /* hit points, energy points, armor class -- essential information which
2073 doesn't fit very well in other categories */
2074 /*ARGSUSED*/
2075 STATIC_OVL void
basics_enlightenment(mode,final)2076 basics_enlightenment(mode, final)
2077 int mode UNUSED;
2078 int final;
2079 {
2080 static char Power[] = "energy points (spell power)";
2081 char buf[BUFSZ];
2082 int pw = u.uen, hp = (Upolyd ? u.mh : u.uhp),
2083 pwmax = u.uenmax, hpmax = (Upolyd ? u.mhmax : u.uhpmax);
2084
2085 enlght_out(""); /* separator after background */
2086 enlght_out("Basics:");
2087
2088 if (hp < 0)
2089 hp = 0;
2090 /* "1 out of 1" rather than "all" if max is only 1; should never happen */
2091 if (hp == hpmax && hpmax > 1)
2092 Sprintf(buf, "all %d hit points", hpmax);
2093 else
2094 Sprintf(buf, "%d out of %d hit point%s", hp, hpmax, plur(hpmax));
2095 you_have(buf, "");
2096
2097 /* low max energy is feasible, so handle couple of extra special cases */
2098 if (pwmax == 0 || (pw == pwmax && pwmax == 2)) /* both: "all 2" is silly */
2099 Sprintf(buf, "%s %s", !pwmax ? "no" : "both", Power);
2100 else if (pw == pwmax && pwmax > 2)
2101 Sprintf(buf, "all %d %s", pwmax, Power);
2102 else
2103 Sprintf(buf, "%d out of %d %s", pw, pwmax, Power);
2104 you_have(buf, "");
2105
2106 if (Upolyd) {
2107 switch (mons[u.umonnum].mlevel) {
2108 case 0:
2109 /* status line currently being explained shows "HD:0" */
2110 Strcpy(buf, "0 hit dice (actually 1/2)");
2111 break;
2112 case 1:
2113 Strcpy(buf, "1 hit die");
2114 break;
2115 default:
2116 Sprintf(buf, "%d hit dice", mons[u.umonnum].mlevel);
2117 break;
2118 }
2119 you_have(buf, "");
2120 }
2121
2122 Sprintf(buf, "%d", u.uac);
2123 enl_msg("Your armor class ", "is ", "was ", buf, "");
2124
2125 /* gold; similar to doprgold(#seegold) but without shop billing info;
2126 same amount as shown on status line which ignores container contents */
2127 {
2128 static const char Your_wallet[] = "Your wallet ";
2129 long umoney = money_cnt(invent);
2130
2131 if (!umoney) {
2132 enl_msg(Your_wallet, "is ", "was ", "empty", "");
2133 } else {
2134 Sprintf(buf, "%ld %s", umoney, currency(umoney));
2135 enl_msg(Your_wallet, "contains ", "contained ", buf, "");
2136 }
2137 }
2138
2139 if (flags.pickup) {
2140 char ocl[MAXOCLASSES + 1];
2141
2142 Strcpy(buf, "on");
2143 oc_to_str(flags.pickup_types, ocl);
2144 Sprintf(eos(buf), " for %s%s%s",
2145 *ocl ? "'" : "", *ocl ? ocl : "all types", *ocl ? "'" : "");
2146 if (flags.pickup_thrown && *ocl) /* *ocl: don't show if 'all types' */
2147 Strcat(buf, " plus thrown");
2148 if (apelist)
2149 Strcat(buf, ", with exceptions");
2150 } else
2151 Strcpy(buf, "off");
2152 enl_msg("Autopickup ", "is ", "was ", buf, "");
2153 }
2154
2155 /* characteristics: expanded version of bottom line strength, dexterity, &c */
2156 STATIC_OVL void
characteristics_enlightenment(mode,final)2157 characteristics_enlightenment(mode, final)
2158 int mode;
2159 int final;
2160 {
2161 char buf[BUFSZ];
2162
2163 enlght_out("");
2164 Sprintf(buf, "%s Characteristics:", !final ? "Current" : "Final");
2165 enlght_out(buf);
2166
2167 /* bottom line order */
2168 one_characteristic(mode, final, A_STR); /* strength */
2169 one_characteristic(mode, final, A_DEX); /* dexterity */
2170 one_characteristic(mode, final, A_CON); /* constitution */
2171 one_characteristic(mode, final, A_INT); /* intelligence */
2172 one_characteristic(mode, final, A_WIS); /* wisdom */
2173 one_characteristic(mode, final, A_CHA); /* charisma */
2174 }
2175
2176 /* display one attribute value for characteristics_enlightenment() */
2177 STATIC_OVL void
one_characteristic(mode,final,attrindx)2178 one_characteristic(mode, final, attrindx)
2179 int mode, final, attrindx;
2180 {
2181 extern const char *const attrname[]; /* attrib.c */
2182 boolean hide_innate_value = FALSE, interesting_alimit;
2183 int acurrent, abase, apeak, alimit;
2184 const char *paren_pfx;
2185 char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32];
2186
2187 /* being polymorphed or wearing certain cursed items prevents
2188 hero from reliably tracking changes to characteristics so
2189 we don't show base & peak values then; when the items aren't
2190 cursed, hero could take them off to check underlying values
2191 and we show those in such case so that player doesn't need
2192 to actually resort to doing that */
2193 if (Upolyd) {
2194 hide_innate_value = TRUE;
2195 } else if (Fixed_abil) {
2196 if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY)
2197 || stuck_ring(uright, RIN_SUSTAIN_ABILITY))
2198 hide_innate_value = TRUE;
2199 }
2200 switch (attrindx) {
2201 case A_STR:
2202 if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed)
2203 hide_innate_value = TRUE;
2204 break;
2205 case A_DEX:
2206 break;
2207 case A_CON:
2208 if (uwep && uwep->oartifact == ART_OGRESMASHER && uwep->cursed)
2209 hide_innate_value = TRUE;
2210 break;
2211 case A_INT:
2212 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
2213 hide_innate_value = TRUE;
2214 break;
2215 case A_WIS:
2216 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
2217 hide_innate_value = TRUE;
2218 break;
2219 case A_CHA:
2220 break;
2221 default:
2222 return; /* impossible */
2223 };
2224 /* note: final disclosure includes MAGICENLIGHTENTMENT */
2225 if ((mode & MAGICENLIGHTENMENT) && !Upolyd)
2226 hide_innate_value = FALSE;
2227
2228 acurrent = ACURR(attrindx);
2229 (void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */
2230 Sprintf(subjbuf, "Your %s ", attrname[attrindx]);
2231
2232 if (!hide_innate_value) {
2233 /* show abase, amax, and/or attrmax if acurr doesn't match abase
2234 (a magic bonus or penalty is in effect) or abase doesn't match
2235 amax (some points have been lost to poison or exercise abuse
2236 and are restorable) or attrmax is different from normal human
2237 (while game is in progress; trying to reduce dependency on
2238 spoilers to keep track of such stuff) or attrmax was different
2239 from abase (at end of game; this attribute wasn't maxed out) */
2240 abase = ABASE(attrindx);
2241 apeak = AMAX(attrindx);
2242 alimit = ATTRMAX(attrindx);
2243 /* criterium for whether the limit is interesting varies */
2244 interesting_alimit =
2245 final ? TRUE /* was originally `(abase != alimit)' */
2246 : (alimit != (attrindx != A_STR ? 18 : STR18(100)));
2247 paren_pfx = final ? " (" : " (current; ";
2248 if (acurrent != abase) {
2249 Sprintf(eos(valubuf), "%sbase:%s", paren_pfx,
2250 attrval(attrindx, abase, valstring));
2251 paren_pfx = ", ";
2252 }
2253 if (abase != apeak) {
2254 Sprintf(eos(valubuf), "%speak:%s", paren_pfx,
2255 attrval(attrindx, apeak, valstring));
2256 paren_pfx = ", ";
2257 }
2258 if (interesting_alimit) {
2259 Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx,
2260 /* more verbose if exceeding 'limit' due to magic bonus */
2261 (acurrent > alimit) ? "innate " : "",
2262 attrval(attrindx, alimit, valstring));
2263 /* paren_pfx = ", "; */
2264 }
2265 if (acurrent != abase || abase != apeak || interesting_alimit)
2266 Strcat(valubuf, ")");
2267 }
2268 enl_msg(subjbuf, "is ", "was ", valubuf, "");
2269 }
2270
2271 /* status: selected obvious capabilities, assorted troubles */
2272 STATIC_OVL void
status_enlightenment(mode,final)2273 status_enlightenment(mode, final)
2274 int mode;
2275 int final;
2276 {
2277 boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE;
2278 int cap, wtype;
2279 char buf[BUFSZ], youtoo[BUFSZ];
2280 boolean Riding = (u.usteed
2281 /* if hero dies while dismounting, u.usteed will still
2282 be set; we want to ignore steed in that situation */
2283 && !(final == ENL_GAMEOVERDEAD
2284 && !strcmp(killer.name, "riding accident")));
2285 const char *steedname = (!Riding ? (char *) 0
2286 : x_monnam(u.usteed,
2287 u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE,
2288 (char *) 0,
2289 (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION),
2290 FALSE));
2291
2292 /*\
2293 * Status (many are abbreviated on bottom line; others are or
2294 * should be discernible to the hero hence to the player)
2295 \*/
2296 enlght_out(""); /* separator after title or characteristics */
2297 enlght_out(final ? "Final Status:" : "Current Status:");
2298
2299 Strcpy(youtoo, You_);
2300 /* not a traditional status but inherently obvious to player; more
2301 detail given below (attributes section) for magic enlightenment */
2302 if (Upolyd) {
2303 Strcpy(buf, "transformed");
2304 if (ugenocided())
2305 Sprintf(eos(buf), " and %s %s inside",
2306 final ? "felt" : "feel", udeadinside());
2307 you_are(buf, "");
2308 }
2309 /* not a trouble, but we want to display riding status before maybe
2310 reporting steed as trapped or hero stuck to cursed saddle */
2311 if (Riding) {
2312 Sprintf(buf, "riding %s", steedname);
2313 you_are(buf, "");
2314 Sprintf(eos(youtoo), "and %s ", steedname);
2315 }
2316 /* other movement situations that hero should always know */
2317 if (Levitation) {
2318 if (Lev_at_will && magic)
2319 you_are("levitating, at will", "");
2320 else
2321 enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION));
2322 } else if (Flying) { /* can only fly when not levitating */
2323 enl_msg(youtoo, are, were, "flying", from_what(FLYING));
2324 }
2325 if (Underwater) {
2326 you_are("underwater", "");
2327 } else if (u.uinwater) {
2328 you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING));
2329 } else if (walking_on_water()) {
2330 /* show active Wwalking here, potential Wwalking elsewhere */
2331 Sprintf(buf, "walking on %s",
2332 is_pool(u.ux, u.uy) ? "water"
2333 : is_lava(u.ux, u.uy) ? "lava"
2334 : surface(u.ux, u.uy)); /* catchall; shouldn't happen */
2335 you_are(buf, from_what(WWALKING));
2336 }
2337 if (Upolyd && (u.uundetected || U_AP_TYPE != M_AP_NOTHING))
2338 youhiding(TRUE, final);
2339
2340 /* internal troubles, mostly in the order that prayer ranks them */
2341 if (Stoned) {
2342 if (final && (Stoned & I_SPECIAL))
2343 enlght_out(" You turned into stone.");
2344 else
2345 you_are("turning to stone", "");
2346 }
2347 if (Slimed) {
2348 if (final && (Slimed & I_SPECIAL))
2349 enlght_out(" You turned into slime.");
2350 else
2351 you_are("turning into slime", "");
2352 }
2353 if (Strangled) {
2354 if (u.uburied) {
2355 you_are("buried", "");
2356 } else {
2357 if (final && (Strangled & I_SPECIAL)) {
2358 enlght_out(" You died from strangulation.");
2359 } else {
2360 Strcpy(buf, "being strangled");
2361 if (wizard)
2362 Sprintf(eos(buf), " (%ld)", (Strangled & TIMEOUT));
2363 you_are(buf, from_what(STRANGLED));
2364 }
2365 }
2366 }
2367 if (Sick) {
2368 /* the two types of sickness are lumped together; hero can be
2369 afflicted by both but there is only one timeout; botl status
2370 puts TermIll before FoodPois and death due to timeout reports
2371 terminal illness if both are in effect, so do the same here */
2372 if (final && (Sick & I_SPECIAL)) {
2373 Sprintf(buf, " %sdied from %s.", You_, /* has trailing space */
2374 (u.usick_type & SICK_NONVOMITABLE)
2375 ? "terminal illness" : "food poisoning");
2376 enlght_out(buf);
2377 } else {
2378 /* unlike death due to sickness, report the two cases separately
2379 because it is possible to cure one without curing the other */
2380 if (u.usick_type & SICK_NONVOMITABLE)
2381 you_are("terminally sick from illness", "");
2382 if (u.usick_type & SICK_VOMITABLE)
2383 you_are("terminally sick from food poisoning", "");
2384 }
2385 }
2386 if (Vomiting)
2387 you_are("nauseated", "");
2388 if (Stunned)
2389 you_are("stunned", "");
2390 if (Confusion)
2391 you_are("confused", "");
2392 if (Hallucination)
2393 you_are("hallucinating", "");
2394 if (Blind) {
2395 /* from_what() (currently wizard-mode only) checks !haseyes()
2396 before u.uroleplay.blind, so we should too */
2397 Sprintf(buf, "%s blind",
2398 !haseyes(youmonst.data) ? "innately"
2399 : u.uroleplay.blind ? "permanently"
2400 /* better phrasing desperately wanted... */
2401 : Blindfolded_only ? "deliberately"
2402 : "temporarily");
2403 if (wizard && (Blinded & TIMEOUT) != 0L
2404 && !u.uroleplay.blind && haseyes(youmonst.data))
2405 Sprintf(eos(buf), " (%ld)", (Blinded & TIMEOUT));
2406 /* !haseyes: avoid "you are innately blind innately" */
2407 you_are(buf, !haseyes(youmonst.data) ? "" : from_what(BLINDED));
2408 }
2409 if (Deaf)
2410 you_are("deaf", from_what(DEAF));
2411
2412 /* external troubles, more or less */
2413 if (Punished) {
2414 if (uball) {
2415 Sprintf(buf, "chained to %s", ansimpleoname(uball));
2416 } else {
2417 impossible("Punished without uball?");
2418 Strcpy(buf, "punished");
2419 }
2420 you_are(buf, "");
2421 }
2422 if (u.utrap) {
2423 char predicament[BUFSZ];
2424 struct trap *t;
2425 boolean anchored = (u.utraptype == TT_BURIEDBALL);
2426
2427 if (anchored) {
2428 Strcpy(predicament, "tethered to something buried");
2429 } else if (u.utraptype == TT_INFLOOR || u.utraptype == TT_LAVA) {
2430 Sprintf(predicament, "stuck in %s", the(surface(u.ux, u.uy)));
2431 } else {
2432 Strcpy(predicament, "trapped");
2433 if ((t = t_at(u.ux, u.uy)) != 0)
2434 Sprintf(eos(predicament), " in %s",
2435 an(defsyms[trap_to_defsym(t->ttyp)].explanation));
2436 }
2437 if (u.usteed) { /* not `Riding' here */
2438 Sprintf(buf, "%s%s ", anchored ? "you and " : "", steedname);
2439 *buf = highc(*buf);
2440 enl_msg(buf, (anchored ? "are " : "is "),
2441 (anchored ? "were " : "was "), predicament, "");
2442 } else
2443 you_are(predicament, "");
2444 } /* (u.utrap) */
2445 if (u.uswallow) {
2446 Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck));
2447 if (wizard)
2448 Sprintf(eos(buf), " (%u)", u.uswldtim);
2449 you_are(buf, "");
2450 } else if (u.ustuck) {
2451 Sprintf(buf, "%s %s",
2452 (Upolyd && sticks(youmonst.data)) ? "holding" : "held by",
2453 a_monnam(u.ustuck));
2454 you_are(buf, "");
2455 }
2456 if (Riding) {
2457 struct obj *saddle = which_armor(u.usteed, W_SADDLE);
2458
2459 if (saddle && saddle->cursed) {
2460 Sprintf(buf, "stuck to %s %s", s_suffix(steedname),
2461 simpleonames(saddle));
2462 you_are(buf, "");
2463 }
2464 }
2465 if (Wounded_legs) {
2466 /* when mounted, Wounded_legs applies to steed rather than to
2467 hero; we only report steed's wounded legs in wizard mode */
2468 if (u.usteed) { /* not `Riding' here */
2469 if (wizard && steedname) {
2470 Strcpy(buf, steedname);
2471 *buf = highc(*buf);
2472 enl_msg(buf, " has", " had", " wounded legs", "");
2473 }
2474 } else {
2475 Sprintf(buf, "wounded %s", makeplural(body_part(LEG)));
2476 you_have(buf, "");
2477 }
2478 }
2479 if (Glib) {
2480 Sprintf(buf, "slippery %s", fingers_or_gloves(TRUE));
2481 if (wizard)
2482 Sprintf(eos(buf), " (%ld)", (Glib & TIMEOUT));
2483 you_have(buf, "");
2484 }
2485 if (Fumbling) {
2486 if (magic || cause_known(FUMBLING))
2487 enl_msg(You_, "fumble", "fumbled", "", from_what(FUMBLING));
2488 }
2489 if (Sleepy) {
2490 if (magic || cause_known(SLEEPY)) {
2491 Strcpy(buf, from_what(SLEEPY));
2492 if (wizard)
2493 Sprintf(eos(buf), " (%ld)", (HSleepy & TIMEOUT));
2494 enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf);
2495 }
2496 }
2497 /* hunger/nutrition */
2498 if (Hunger) {
2499 if (magic || cause_known(HUNGER))
2500 enl_msg(You_, "hunger", "hungered", " rapidly",
2501 from_what(HUNGER));
2502 }
2503 Strcpy(buf, hu_stat[u.uhs]); /* hunger status; omitted if "normal" */
2504 mungspaces(buf); /* strip trailing spaces */
2505 if (*buf) {
2506 *buf = lowc(*buf); /* override capitalization */
2507 if (!strcmp(buf, "weak"))
2508 Strcat(buf, " from severe hunger");
2509 else if (!strncmp(buf, "faint", 5)) /* fainting, fainted */
2510 Strcat(buf, " due to starvation");
2511 you_are(buf, "");
2512 }
2513 /* encumbrance */
2514 if ((cap = near_capacity()) > UNENCUMBERED) {
2515 const char *adj = "?_?"; /* (should always get overridden) */
2516
2517 Strcpy(buf, enc_stat[cap]);
2518 *buf = lowc(*buf);
2519 switch (cap) {
2520 case SLT_ENCUMBER:
2521 adj = "slightly";
2522 break; /* burdened */
2523 case MOD_ENCUMBER:
2524 adj = "moderately";
2525 break; /* stressed */
2526 case HVY_ENCUMBER:
2527 adj = "very";
2528 break; /* strained */
2529 case EXT_ENCUMBER:
2530 adj = "extremely";
2531 break; /* overtaxed */
2532 case OVERLOADED:
2533 adj = "not possible";
2534 break;
2535 }
2536 Sprintf(eos(buf), "; movement %s %s%s", !final ? "is" : "was", adj,
2537 (cap < OVERLOADED) ? " slowed" : "");
2538 you_are(buf, "");
2539 } else {
2540 /* last resort entry, guarantees Status section is non-empty
2541 (no longer needed for that purpose since weapon status added;
2542 still useful though) */
2543 you_are("unencumbered", "");
2544 }
2545
2546 /* report being weaponless; distinguish whether gloves are worn */
2547 if (!uwep) {
2548 you_are(uarmg ? "empty handed" /* gloves imply hands */
2549 : humanoid(youmonst.data)
2550 /* hands but no weapon and no gloves */
2551 ? "bare handed"
2552 /* alternate phrasing for paws or lack of hands */
2553 : "not wielding anything",
2554 "");
2555 /* two-weaponing implies hands (can't be polymorphed) and
2556 a weapon or wep-tool (not other odd stuff) in each hand */
2557 } else if (u.twoweap) {
2558 you_are("wielding two weapons at once", "");
2559 /* report most weapons by their skill class (so a katana will be
2560 described as a long sword, for instance; mattock and hook are
2561 exceptions), or wielded non-weapon item by its object class */
2562 } else {
2563 const char *what = weapon_descr(uwep);
2564
2565 if (!strcmpi(what, "armor") || !strcmpi(what, "food")
2566 || !strcmpi(what, "venom"))
2567 Sprintf(buf, "wielding some %s", what);
2568 else
2569 Sprintf(buf, "wielding %s",
2570 (uwep->quan == 1L) ? an(what) : makeplural(what));
2571 you_are(buf, "");
2572 }
2573 /*
2574 * Skill with current weapon. Might help players who've never
2575 * noticed #enhance or decided that it was pointless.
2576 *
2577 * TODO? Maybe merge wielding line and skill line into one sentence.
2578 */
2579 if ((wtype = uwep_skill_type()) != P_NONE) {
2580 char sklvlbuf[20];
2581 int sklvl = P_SKILL(wtype);
2582 boolean hav = (sklvl != P_UNSKILLED && sklvl != P_SKILLED);
2583
2584 if (sklvl == P_ISRESTRICTED)
2585 Strcpy(sklvlbuf, "no");
2586 else
2587 (void) lcase(skill_level_name(wtype, sklvlbuf));
2588 /* "you have no/basic/expert/master/grand-master skill with <skill>"
2589 or "you are unskilled/skilled in <skill>" */
2590 Sprintf(buf, "%s %s %s", sklvlbuf,
2591 hav ? "skill with" : "in", skill_name(wtype));
2592 if (can_advance(wtype, FALSE))
2593 Sprintf(eos(buf), " and %s that",
2594 !final ? "can enhance" : "could have enhanced");
2595 if (hav)
2596 you_have(buf, "");
2597 else
2598 you_are(buf, "");
2599 }
2600 /* report 'nudity' */
2601 if (!uarm && !uarmu && !uarmc && !uarms && !uarmg && !uarmf && !uarmh) {
2602 if (u.uroleplay.nudist)
2603 enl_msg(You_, "do", "did", " not wear any armor", "");
2604 else
2605 you_are("not wearing any armor", "");
2606 }
2607 }
2608
2609 /* attributes: intrinsics and the like, other non-obvious capabilities */
2610 STATIC_OVL void
attributes_enlightenment(unused_mode,final)2611 attributes_enlightenment(unused_mode, final)
2612 int unused_mode UNUSED;
2613 int final;
2614 {
2615 static NEARDATA const char if_surroundings_permitted[] =
2616 " if surroundings permitted";
2617 int ltmp, armpro;
2618 char buf[BUFSZ];
2619
2620 /*\
2621 * Attributes
2622 \*/
2623 enlght_out("");
2624 enlght_out(final ? "Final Attributes:" : "Current Attributes:");
2625
2626 if (u.uevent.uhand_of_elbereth) {
2627 static const char *const hofe_titles[3] = { "the Hand of Elbereth",
2628 "the Envoy of Balance",
2629 "the Glory of Arioch" };
2630 you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1], "");
2631 }
2632
2633 Sprintf(buf, "%s", piousness(TRUE, "aligned"));
2634 if (u.ualign.record >= 0)
2635 you_are(buf, "");
2636 else
2637 you_have(buf, "");
2638
2639 if (wizard) {
2640 Sprintf(buf, " %d", u.ualign.record);
2641 enl_msg("Your alignment ", "is", "was", buf, "");
2642 }
2643
2644 /*** Resistances to troubles ***/
2645 if (Invulnerable)
2646 you_are("invulnerable", from_what(INVULNERABLE));
2647 if (Antimagic)
2648 you_are("magic-protected", from_what(ANTIMAGIC));
2649 if (Fire_resistance)
2650 you_are("fire resistant", from_what(FIRE_RES));
2651 if (Cold_resistance)
2652 you_are("cold resistant", from_what(COLD_RES));
2653 if (Sleep_resistance)
2654 you_are("sleep resistant", from_what(SLEEP_RES));
2655 if (Disint_resistance)
2656 you_are("disintegration-resistant", from_what(DISINT_RES));
2657 if (Shock_resistance)
2658 you_are("shock resistant", from_what(SHOCK_RES));
2659 if (Poison_resistance)
2660 you_are("poison resistant", from_what(POISON_RES));
2661 if (Acid_resistance)
2662 you_are("acid resistant", from_what(ACID_RES));
2663 if (Drain_resistance)
2664 you_are("level-drain resistant", from_what(DRAIN_RES));
2665 if (Sick_resistance)
2666 you_are("immune to sickness", from_what(SICK_RES));
2667 if (Stone_resistance)
2668 you_are("petrification resistant", from_what(STONE_RES));
2669 if (Halluc_resistance)
2670 enl_msg(You_, "resist", "resisted", " hallucinations",
2671 from_what(HALLUC_RES));
2672 if (u.uedibility)
2673 you_can("recognize detrimental food", "");
2674
2675 /*** Vision and senses ***/
2676 if (!Blind && (Blinded || !haseyes(youmonst.data)))
2677 you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */
2678 if (See_invisible) {
2679 if (!Blind)
2680 enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS));
2681 else
2682 enl_msg(You_, "will see", "would have seen",
2683 " invisible when not blind", from_what(SEE_INVIS));
2684 }
2685 if (Blind_telepat)
2686 you_are("telepathic", from_what(TELEPAT));
2687 if (Warning)
2688 you_are("warned", from_what(WARNING));
2689 if (Warn_of_mon && context.warntype.obj) {
2690 Sprintf(buf, "aware of the presence of %s",
2691 (context.warntype.obj & M2_ORC) ? "orcs"
2692 : (context.warntype.obj & M2_ELF) ? "elves"
2693 : (context.warntype.obj & M2_DEMON) ? "demons" : something);
2694 you_are(buf, from_what(WARN_OF_MON));
2695 }
2696 if (Warn_of_mon && context.warntype.polyd) {
2697 Sprintf(buf, "aware of the presence of %s",
2698 ((context.warntype.polyd & (M2_HUMAN | M2_ELF))
2699 == (M2_HUMAN | M2_ELF))
2700 ? "humans and elves"
2701 : (context.warntype.polyd & M2_HUMAN)
2702 ? "humans"
2703 : (context.warntype.polyd & M2_ELF)
2704 ? "elves"
2705 : (context.warntype.polyd & M2_ORC)
2706 ? "orcs"
2707 : (context.warntype.polyd & M2_DEMON)
2708 ? "demons"
2709 : "certain monsters");
2710 you_are(buf, "");
2711 }
2712 if (Warn_of_mon && context.warntype.speciesidx >= LOW_PM) {
2713 Sprintf(buf, "aware of the presence of %s",
2714 makeplural(mons[context.warntype.speciesidx].mname));
2715 you_are(buf, from_what(WARN_OF_MON));
2716 }
2717 if (Undead_warning)
2718 you_are("warned of undead", from_what(WARN_UNDEAD));
2719 if (Searching)
2720 you_have("automatic searching", from_what(SEARCHING));
2721 if (Clairvoyant)
2722 you_are("clairvoyant", from_what(CLAIRVOYANT));
2723 else if ((HClairvoyant || EClairvoyant) && BClairvoyant) {
2724 Strcpy(buf, from_what(-CLAIRVOYANT));
2725 if (!strncmp(buf, " because of ", 12))
2726 /* overwrite substring; strncpy doesn't add terminator */
2727 (void) strncpy(buf, " if not for ", 12);
2728 enl_msg(You_, "could be", "could have been", " clairvoyant", buf);
2729 }
2730 if (Infravision)
2731 you_have("infravision", from_what(INFRAVISION));
2732 if (Detect_monsters)
2733 you_are("sensing the presence of monsters", "");
2734 if (u.umconf)
2735 you_are("going to confuse monsters", "");
2736
2737 /*** Appearance and behavior ***/
2738 if (Adornment) {
2739 int adorn = 0;
2740
2741 if (uleft && uleft->otyp == RIN_ADORNMENT)
2742 adorn += uleft->spe;
2743 if (uright && uright->otyp == RIN_ADORNMENT)
2744 adorn += uright->spe;
2745 /* the sum might be 0 (+0 ring or two which negate each other);
2746 that yields "you are charismatic" (which isn't pointless
2747 because it potentially impacts seduction attacks) */
2748 Sprintf(buf, "%scharismatic",
2749 (adorn > 0) ? "more " : (adorn < 0) ? "less " : "");
2750 you_are(buf, from_what(ADORNED));
2751 }
2752 if (Invisible)
2753 you_are("invisible", from_what(INVIS));
2754 else if (Invis)
2755 you_are("invisible to others", from_what(INVIS));
2756 /* ordinarily "visible" is redundant; this is a special case for
2757 the situation when invisibility would be an expected attribute */
2758 else if ((HInvis || EInvis) && BInvis)
2759 you_are("visible", from_what(-INVIS));
2760 if (Displaced)
2761 you_are("displaced", from_what(DISPLACED));
2762 if (Stealth)
2763 you_are("stealthy", from_what(STEALTH));
2764 if (Aggravate_monster)
2765 enl_msg("You aggravate", "", "d", " monsters",
2766 from_what(AGGRAVATE_MONSTER));
2767 if (Conflict)
2768 enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT));
2769
2770 /*** Transportation ***/
2771 if (Jumping)
2772 you_can("jump", from_what(JUMPING));
2773 if (Teleportation)
2774 you_can("teleport", from_what(TELEPORT));
2775 if (Teleport_control)
2776 you_have("teleport control", from_what(TELEPORT_CONTROL));
2777 /* actively levitating handled earlier as a status condition */
2778 if (BLevitation) { /* levitation is blocked */
2779 long save_BLev = BLevitation;
2780
2781 BLevitation = 0L;
2782 if (Levitation) {
2783 /* either trapped in the floor or inside solid rock
2784 (or both if chained to buried iron ball and have
2785 moved one step into solid rock somehow) */
2786 boolean trapped = (save_BLev & I_SPECIAL) != 0L,
2787 terrain = (save_BLev & FROMOUTSIDE) != 0L;
2788
2789 Sprintf(buf, "%s%s%s",
2790 trapped ? " if not trapped" : "",
2791 (trapped && terrain) ? " and" : "",
2792 terrain ? if_surroundings_permitted : "");
2793 enl_msg(You_, "would levitate", "would have levitated", buf, "");
2794 }
2795 BLevitation = save_BLev;
2796 }
2797 /* actively flying handled earlier as a status condition */
2798 if (BFlying) { /* flight is blocked */
2799 long save_BFly = BFlying;
2800
2801 BFlying = 0L;
2802 if (Flying) {
2803 enl_msg(You_, "would fly", "would have flown",
2804 /* wording quibble: for past tense, "hadn't been"
2805 would sound better than "weren't" (and
2806 "had permitted" better than "permitted"), but
2807 "weren't" and "permitted" are adequate so the
2808 extra complexity to handle that isn't worth it */
2809 Levitation
2810 ? " if you weren't levitating"
2811 : (save_BFly == I_SPECIAL)
2812 /* this is an oversimpliction; being trapped
2813 might also be blocking levitation so flight
2814 would still be blocked after escaping trap */
2815 ? " if you weren't trapped"
2816 : (save_BFly == FROMOUTSIDE)
2817 ? if_surroundings_permitted
2818 /* two or more of levitation, surroundings,
2819 and being trapped in the floor */
2820 : " if circumstances permitted",
2821 "");
2822 }
2823 BFlying = save_BFly;
2824 }
2825 /* actively walking on water handled earlier as a status condition */
2826 if (Wwalking && !walking_on_water())
2827 you_can("walk on water", from_what(WWALKING));
2828 /* actively swimming (in water but not under it) handled earlier */
2829 if (Swimming && (Underwater || !u.uinwater))
2830 you_can("swim", from_what(SWIMMING));
2831 if (Breathless)
2832 you_can("survive without air", from_what(MAGICAL_BREATHING));
2833 else if (Amphibious)
2834 you_can("breathe water", from_what(MAGICAL_BREATHING));
2835 if (Passes_walls)
2836 you_can("walk through walls", from_what(PASSES_WALLS));
2837
2838 /*** Physical attributes ***/
2839 if (Regeneration)
2840 enl_msg("You regenerate", "", "d", "", from_what(REGENERATION));
2841 if (Slow_digestion)
2842 you_have("slower digestion", from_what(SLOW_DIGESTION));
2843 if (u.uhitinc)
2844 you_have(enlght_combatinc("to hit", u.uhitinc, final, buf), "");
2845 if (u.udaminc)
2846 you_have(enlght_combatinc("damage", u.udaminc, final, buf), "");
2847 if (u.uspellprot || Protection) {
2848 int prot = 0;
2849
2850 if (uleft && uleft->otyp == RIN_PROTECTION)
2851 prot += uleft->spe;
2852 if (uright && uright->otyp == RIN_PROTECTION)
2853 prot += uright->spe;
2854 if (HProtection & INTRINSIC)
2855 prot += u.ublessed;
2856 prot += u.uspellprot;
2857 if (prot)
2858 you_have(enlght_combatinc("defense", prot, final, buf), "");
2859 }
2860 if ((armpro = magic_negation(&youmonst)) > 0) {
2861 /* magic cancellation factor, conferred by worn armor */
2862 static const char *const mc_types[] = {
2863 "" /*ordinary*/, "warded", "guarded", "protected",
2864 };
2865 /* sanity check */
2866 if (armpro >= SIZE(mc_types))
2867 armpro = SIZE(mc_types) - 1;
2868 you_are(mc_types[armpro], "");
2869 }
2870 if (Half_physical_damage)
2871 enlght_halfdmg(HALF_PHDAM, final);
2872 if (Half_spell_damage)
2873 enlght_halfdmg(HALF_SPDAM, final);
2874 /* polymorph and other shape change */
2875 if (Protection_from_shape_changers)
2876 you_are("protected from shape changers",
2877 from_what(PROT_FROM_SHAPE_CHANGERS));
2878 if (Unchanging) {
2879 const char *what = 0;
2880
2881 if (!Upolyd) /* Upolyd handled below after current form */
2882 you_can("not change from your current form",
2883 from_what(UNCHANGING));
2884 /* blocked shape changes */
2885 if (Polymorph)
2886 what = !final ? "polymorph" : "have polymorphed";
2887 else if (u.ulycn >= LOW_PM)
2888 what = !final ? "change shape" : "have changed shape";
2889 if (what) {
2890 Sprintf(buf, "would %s periodically", what);
2891 /* omit from_what(UNCHANGING); too verbose */
2892 enl_msg(You_, buf, buf, " if not locked into your current form",
2893 "");
2894 }
2895 } else if (Polymorph) {
2896 you_are("polymorphing periodically", from_what(POLYMORPH));
2897 }
2898 if (Polymorph_control)
2899 you_have("polymorph control", from_what(POLYMORPH_CONTROL));
2900 if (Upolyd && u.umonnum != u.ulycn
2901 /* if we've died from turning into slime, we're polymorphed
2902 right now but don't want to list it as a temporary attribute
2903 [we need a more reliable way to detect this situation] */
2904 && !(final == ENL_GAMEOVERDEAD
2905 && u.umonnum == PM_GREEN_SLIME && !Unchanging)) {
2906 /* foreign shape (except were-form which is handled below) */
2907 Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname));
2908 if (wizard)
2909 Sprintf(eos(buf), " (%d)", u.mtimedone);
2910 you_are(buf, "");
2911 }
2912 if (lays_eggs(youmonst.data) && flags.female) /* Upolyd */
2913 you_can("lay eggs", "");
2914 if (u.ulycn >= LOW_PM) {
2915 /* "you are a werecreature [in beast form]" */
2916 Strcpy(buf, an(mons[u.ulycn].mname));
2917 if (u.umonnum == u.ulycn) {
2918 Strcat(buf, " in beast form");
2919 if (wizard)
2920 Sprintf(eos(buf), " (%d)", u.mtimedone);
2921 }
2922 you_are(buf, "");
2923 }
2924 if (Unchanging && Upolyd) /* !Upolyd handled above */
2925 you_can("not change from your current form", from_what(UNCHANGING));
2926 if (Hate_silver)
2927 you_are("harmed by silver", "");
2928 /* movement and non-armor-based protection */
2929 if (Fast)
2930 you_are(Very_fast ? "very fast" : "fast", from_what(FAST));
2931 if (Reflecting)
2932 you_have("reflection", from_what(REFLECTING));
2933 if (Free_action)
2934 you_have("free action", from_what(FREE_ACTION));
2935 if (Fixed_abil)
2936 you_have("fixed abilities", from_what(FIXED_ABIL));
2937 if (Lifesaved)
2938 enl_msg("Your life ", "will be", "would have been", " saved", "");
2939
2940 /*** Miscellany ***/
2941 if (Luck) {
2942 ltmp = abs((int) Luck);
2943 Sprintf(buf, "%s%slucky",
2944 ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "",
2945 Luck < 0 ? "un" : "");
2946 if (wizard)
2947 Sprintf(eos(buf), " (%d)", Luck);
2948 you_are(buf, "");
2949 } else if (wizard)
2950 enl_msg("Your luck ", "is", "was", " zero", "");
2951 if (u.moreluck > 0)
2952 you_have("extra luck", "");
2953 else if (u.moreluck < 0)
2954 you_have("reduced luck", "");
2955 if (carrying(LUCKSTONE) || stone_luck(TRUE)) {
2956 ltmp = stone_luck(FALSE);
2957 if (ltmp <= 0)
2958 enl_msg("Bad luck ", "does", "did", " not time out for you", "");
2959 if (ltmp >= 0)
2960 enl_msg("Good luck ", "does", "did", " not time out for you", "");
2961 }
2962
2963 if (u.ugangr) {
2964 Sprintf(buf, " %sangry with you",
2965 u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : "");
2966 if (wizard)
2967 Sprintf(eos(buf), " (%d)", u.ugangr);
2968 enl_msg(u_gname(), " is", " was", buf, "");
2969 } else {
2970 /*
2971 * We need to suppress this when the game is over, because death
2972 * can change the value calculated by can_pray(), potentially
2973 * resulting in a false claim that you could have prayed safely.
2974 */
2975 if (!final) {
2976 #if 0
2977 /* "can [not] safely pray" vs "could [not] have safely prayed" */
2978 Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ",
2979 final ? "have " : "", final ? "ed" : "");
2980 #else
2981 Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not ");
2982 #endif
2983 if (wizard)
2984 Sprintf(eos(buf), " (%d)", u.ublesscnt);
2985 you_can(buf, "");
2986 }
2987 }
2988
2989 #ifdef DEBUG
2990 /* named fruit debugging (doesn't really belong here...); to enable,
2991 include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
2992 if (wizard && explicitdebug("fruit")) {
2993 struct fruit *f;
2994
2995 reorder_fruit(TRUE); /* sort by fruit index, from low to high;
2996 * this modifies the ffruit chain, so could
2997 * possibly mask or even introduce a problem,
2998 * but it does useful sanity checking */
2999 for (f = ffruit; f; f = f->nextf) {
3000 Sprintf(buf, "Fruit #%d ", f->fid);
3001 enl_msg(buf, "is ", "was ", f->fname, "");
3002 }
3003 enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
3004 Sprintf(buf, "%d", flags.made_fruit);
3005 enl_msg("The made fruit flag ", "is ", "was ", buf, "");
3006 }
3007 #endif
3008
3009 {
3010 const char *p;
3011
3012 buf[0] = '\0';
3013 if (final < 2) { /* still in progress, or quit/escaped/ascended */
3014 p = "survived after being killed ";
3015 switch (u.umortality) {
3016 case 0:
3017 p = !final ? (char *) 0 : "survived";
3018 break;
3019 case 1:
3020 Strcpy(buf, "once");
3021 break;
3022 case 2:
3023 Strcpy(buf, "twice");
3024 break;
3025 case 3:
3026 Strcpy(buf, "thrice");
3027 break;
3028 default:
3029 Sprintf(buf, "%d times", u.umortality);
3030 break;
3031 }
3032 } else { /* game ended in character's death */
3033 p = "are dead";
3034 switch (u.umortality) {
3035 case 0:
3036 impossible("dead without dying?");
3037 case 1:
3038 break; /* just "are dead" */
3039 default:
3040 Sprintf(buf, " (%d%s time!)", u.umortality,
3041 ordin(u.umortality));
3042 break;
3043 }
3044 }
3045 if (p)
3046 enl_msg(You_, "have been killed ", p, buf, "");
3047 }
3048 }
3049
3050 #if 0 /* no longer used */
3051 STATIC_DCL boolean NDECL(minimal_enlightenment);
3052
3053 /*
3054 * Courtesy function for non-debug, non-explorer mode players
3055 * to help refresh them about who/what they are.
3056 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
3057 */
3058 STATIC_OVL boolean
3059 minimal_enlightenment()
3060 {
3061 winid tmpwin;
3062 menu_item *selected;
3063 anything any;
3064 int genidx, n;
3065 char buf[BUFSZ], buf2[BUFSZ];
3066 static const char untabbed_fmtstr[] = "%-15s: %-12s";
3067 static const char untabbed_deity_fmtstr[] = "%-17s%s";
3068 static const char tabbed_fmtstr[] = "%s:\t%-12s";
3069 static const char tabbed_deity_fmtstr[] = "%s\t%s";
3070 static const char *fmtstr;
3071 static const char *deity_fmtstr;
3072
3073 fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr;
3074 deity_fmtstr = iflags.menu_tab_sep ? tabbed_deity_fmtstr
3075 : untabbed_deity_fmtstr;
3076 any = zeroany;
3077 buf[0] = buf2[0] = '\0';
3078 tmpwin = create_nhwindow(NHW_MENU);
3079 start_menu(tmpwin);
3080 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3081 "Starting", FALSE);
3082
3083 /* Starting name, race, role, gender */
3084 Sprintf(buf, fmtstr, "name", plname);
3085 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3086 Sprintf(buf, fmtstr, "race", urace.noun);
3087 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3088 Sprintf(buf, fmtstr, "role",
3089 (flags.initgend && urole.name.f) ? urole.name.f : urole.name.m);
3090 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3091 Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj);
3092 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3093
3094 /* Starting alignment */
3095 Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL]));
3096 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3097
3098 /* Current name, race, role, gender */
3099 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
3100 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3101 "Current", FALSE);
3102 Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun);
3103 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3104 if (Upolyd) {
3105 Sprintf(buf, fmtstr, "role (base)",
3106 (u.mfemale && urole.name.f) ? urole.name.f
3107 : urole.name.m);
3108 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3109 } else {
3110 Sprintf(buf, fmtstr, "role",
3111 (flags.female && urole.name.f) ? urole.name.f
3112 : urole.name.m);
3113 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3114 }
3115 /* don't want poly_gender() here; it forces `2' for non-humanoids */
3116 genidx = is_neuter(youmonst.data) ? 2 : flags.female;
3117 Sprintf(buf, fmtstr, "gender", genders[genidx].adj);
3118 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3119 if (Upolyd && (int) u.mfemale != genidx) {
3120 Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj);
3121 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3122 }
3123
3124 /* Current alignment */
3125 Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type));
3126 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3127
3128 /* Deity list */
3129 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
3130 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3131 "Deities", FALSE);
3132 Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC),
3133 (u.ualignbase[A_ORIGINAL] == u.ualign.type
3134 && u.ualign.type == A_CHAOTIC) ? " (s,c)"
3135 : (u.ualignbase[A_ORIGINAL] == A_CHAOTIC) ? " (s)"
3136 : (u.ualign.type == A_CHAOTIC) ? " (c)" : "");
3137 Sprintf(buf, fmtstr, "Chaotic", buf2);
3138 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3139
3140 Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL),
3141 (u.ualignbase[A_ORIGINAL] == u.ualign.type
3142 && u.ualign.type == A_NEUTRAL) ? " (s,c)"
3143 : (u.ualignbase[A_ORIGINAL] == A_NEUTRAL) ? " (s)"
3144 : (u.ualign.type == A_NEUTRAL) ? " (c)" : "");
3145 Sprintf(buf, fmtstr, "Neutral", buf2);
3146 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3147
3148 Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL),
3149 (u.ualignbase[A_ORIGINAL] == u.ualign.type
3150 && u.ualign.type == A_LAWFUL) ? " (s,c)"
3151 : (u.ualignbase[A_ORIGINAL] == A_LAWFUL) ? " (s)"
3152 : (u.ualign.type == A_LAWFUL) ? " (c)" : "");
3153 Sprintf(buf, fmtstr, "Lawful", buf2);
3154 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
3155
3156 end_menu(tmpwin, "Base Attributes");
3157 n = select_menu(tmpwin, PICK_NONE, &selected);
3158 destroy_nhwindow(tmpwin);
3159 return (boolean) (n != -1);
3160 }
3161 #endif /*0*/
3162
3163 /* ^X command */
3164 STATIC_PTR int
doattributes(VOID_ARGS)3165 doattributes(VOID_ARGS)
3166 {
3167 int mode = BASICENLIGHTENMENT;
3168
3169 /* show more--as if final disclosure--for wizard and explore modes */
3170 if (wizard || discover)
3171 mode |= MAGICENLIGHTENMENT;
3172
3173 enlightenment(mode, ENL_GAMEINPROGRESS);
3174 return 0;
3175 }
3176
3177 void
youhiding(via_enlghtmt,msgflag)3178 youhiding(via_enlghtmt, msgflag)
3179 boolean via_enlghtmt; /* englightment line vs topl message */
3180 int msgflag; /* for variant message phrasing */
3181 {
3182 char *bp, buf[BUFSZ];
3183
3184 Strcpy(buf, "hiding");
3185 if (U_AP_TYPE != M_AP_NOTHING) {
3186 /* mimic; hero is only able to mimic a strange object or gold
3187 or hallucinatory alternative to gold, so we skip the details
3188 for the hypothetical furniture and monster cases */
3189 bp = eos(strcpy(buf, "mimicking"));
3190 if (U_AP_TYPE == M_AP_OBJECT) {
3191 Sprintf(bp, " %s", an(simple_typename(youmonst.mappearance)));
3192 } else if (U_AP_TYPE == M_AP_FURNITURE) {
3193 Strcpy(bp, " something");
3194 } else if (U_AP_TYPE == M_AP_MONSTER) {
3195 Strcpy(bp, " someone");
3196 } else {
3197 ; /* something unexpected; leave 'buf' as-is */
3198 }
3199 } else if (u.uundetected) {
3200 bp = eos(buf); /* points past "hiding" */
3201 if (youmonst.data->mlet == S_EEL) {
3202 if (is_pool(u.ux, u.uy))
3203 Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy));
3204 } else if (hides_under(youmonst.data)) {
3205 struct obj *o = level.objects[u.ux][u.uy];
3206
3207 if (o)
3208 Sprintf(bp, " underneath %s", ansimpleoname(o));
3209 } else if (is_clinger(youmonst.data) || Flying) {
3210 /* Flying: 'lurker above' hides on ceiling but doesn't cling */
3211 Sprintf(bp, " on the %s", ceiling(u.ux, u.uy));
3212 } else {
3213 /* on floor; is_hider() but otherwise not special: 'trapper' */
3214 if (u.utrap && u.utraptype == TT_PIT) {
3215 struct trap *t = t_at(u.ux, u.uy);
3216
3217 Sprintf(bp, " in a %spit",
3218 (t && t->ttyp == SPIKED_PIT) ? "spiked " : "");
3219 } else
3220 Sprintf(bp, " on the %s", surface(u.ux, u.uy));
3221 }
3222 } else {
3223 ; /* shouldn't happen; will result in generic "you are hiding" */
3224 }
3225
3226 if (via_enlghtmt) {
3227 int final = msgflag; /* 'final' is used by you_are() macro */
3228
3229 you_are(buf, "");
3230 } else {
3231 /* for dohide(), when player uses '#monster' command */
3232 You("are %s %s.", msgflag ? "already" : "now", buf);
3233 }
3234 }
3235
3236 /* KMH, #conduct
3237 * (shares enlightenment's tense handling)
3238 */
3239 int
doconduct(VOID_ARGS)3240 doconduct(VOID_ARGS)
3241 {
3242 show_conduct(0);
3243 return 0;
3244 }
3245
3246 void
show_conduct(final)3247 show_conduct(final)
3248 int final;
3249 {
3250 char buf[BUFSZ];
3251 int ngenocided;
3252
3253 /* Create the conduct window */
3254 en_win = create_nhwindow(NHW_MENU);
3255 putstr(en_win, 0, "Voluntary challenges:");
3256
3257 if (u.uroleplay.blind)
3258 you_have_been("blind from birth");
3259 if (u.uroleplay.nudist)
3260 you_have_been("faithfully nudist");
3261
3262 if (!u.uconduct.food)
3263 enl_msg(You_, "have gone", "went", " without food", "");
3264 /* but beverages are okay */
3265 else if (!u.uconduct.unvegan)
3266 you_have_X("followed a strict vegan diet");
3267 else if (!u.uconduct.unvegetarian)
3268 you_have_been("vegetarian");
3269
3270 if (!u.uconduct.gnostic)
3271 you_have_been("an atheist");
3272
3273 if (!u.uconduct.weaphit) {
3274 you_have_never("hit with a wielded weapon");
3275 } else if (wizard) {
3276 Sprintf(buf, "used a wielded weapon %ld time%s", u.uconduct.weaphit,
3277 plur(u.uconduct.weaphit));
3278 you_have_X(buf);
3279 }
3280 if (!u.uconduct.killer)
3281 you_have_been("a pacifist");
3282
3283 if (!u.uconduct.literate) {
3284 you_have_been("illiterate");
3285 } else if (wizard) {
3286 Sprintf(buf, "read items or engraved %ld time%s", u.uconduct.literate,
3287 plur(u.uconduct.literate));
3288 you_have_X(buf);
3289 }
3290
3291 ngenocided = num_genocides();
3292 if (ngenocided == 0) {
3293 you_have_never("genocided any monsters");
3294 } else {
3295 Sprintf(buf, "genocided %d type%s of monster%s", ngenocided,
3296 plur(ngenocided), plur(ngenocided));
3297 you_have_X(buf);
3298 }
3299
3300 if (!u.uconduct.polypiles) {
3301 you_have_never("polymorphed an object");
3302 } else if (wizard) {
3303 Sprintf(buf, "polymorphed %ld item%s", u.uconduct.polypiles,
3304 plur(u.uconduct.polypiles));
3305 you_have_X(buf);
3306 }
3307
3308 if (!u.uconduct.polyselfs) {
3309 you_have_never("changed form");
3310 } else if (wizard) {
3311 Sprintf(buf, "changed form %ld time%s", u.uconduct.polyselfs,
3312 plur(u.uconduct.polyselfs));
3313 you_have_X(buf);
3314 }
3315
3316 if (!u.uconduct.wishes) {
3317 you_have_X("used no wishes");
3318 } else {
3319 Sprintf(buf, "used %ld wish%s", u.uconduct.wishes,
3320 (u.uconduct.wishes > 1L) ? "es" : "");
3321 if (u.uconduct.wisharti) {
3322 /* if wisharti == wishes
3323 * 1 wish (for an artifact)
3324 * 2 wishes (both for artifacts)
3325 * N wishes (all for artifacts)
3326 * else (N is at least 2 in order to get here; M < N)
3327 * N wishes (1 for an artifact)
3328 * N wishes (M for artifacts)
3329 */
3330 if (u.uconduct.wisharti == u.uconduct.wishes)
3331 Sprintf(eos(buf), " (%s",
3332 (u.uconduct.wisharti > 2L) ? "all "
3333 : (u.uconduct.wisharti == 2L) ? "both " : "");
3334 else
3335 Sprintf(eos(buf), " (%ld ", u.uconduct.wisharti);
3336
3337 Sprintf(eos(buf), "for %s)",
3338 (u.uconduct.wisharti == 1L) ? "an artifact"
3339 : "artifacts");
3340 }
3341 you_have_X(buf);
3342
3343 if (!u.uconduct.wisharti)
3344 enl_msg(You_, "have not wished", "did not wish",
3345 " for any artifacts", "");
3346 }
3347
3348 /* Pop up the window and wait for a key */
3349 display_nhwindow(en_win, TRUE);
3350 destroy_nhwindow(en_win);
3351 en_win = WIN_ERR;
3352 }
3353
3354 /* ordered by command name */
3355 struct ext_func_tab extcmdlist[] = {
3356 { '#', "#", "perform an extended command",
3357 doextcmd, IFBURIED | GENERALCMD },
3358 { M('?'), "?", "list all extended commands",
3359 doextlist, IFBURIED | AUTOCOMPLETE | GENERALCMD },
3360 { M('a'), "adjust", "adjust inventory letters",
3361 doorganize, IFBURIED | AUTOCOMPLETE },
3362 { M('A'), "annotate", "name current level",
3363 donamelevel, IFBURIED | AUTOCOMPLETE },
3364 { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)",
3365 doapply },
3366 { C('x'), "attributes", "show your attributes",
3367 doattributes, IFBURIED },
3368 { '@', "autopickup", "toggle the pickup option on/off",
3369 dotogglepickup, IFBURIED },
3370 { 'C', "call", "call (name) something", docallcmd, IFBURIED },
3371 { 'Z', "cast", "zap (cast) a spell", docast, IFBURIED },
3372 { M('c'), "chat", "talk to someone", dotalk, IFBURIED | AUTOCOMPLETE },
3373 { 'c', "close", "close a door", doclose },
3374 { M('C'), "conduct", "list voluntary challenges you have maintained",
3375 doconduct, IFBURIED | AUTOCOMPLETE },
3376 { M('d'), "dip", "dip an object into something", dodip, AUTOCOMPLETE },
3377 { '>', "down", "go down a staircase", dodown },
3378 { 'd', "drop", "drop an item", dodrop },
3379 { 'D', "droptype", "drop specific item types", doddrop },
3380 { 'e', "eat", "eat something", doeat },
3381 { 'E', "engrave", "engrave writing on the floor", doengrave },
3382 { M('e'), "enhance", "advance or check weapon and spell skills",
3383 enhance_weapon_skill, IFBURIED | AUTOCOMPLETE },
3384 { '\0', "exploremode", "enter explore (discovery) mode",
3385 enter_explore_mode, IFBURIED },
3386 { 'f', "fire", "fire ammunition from quiver", dofire },
3387 { M('f'), "force", "force a lock", doforce, AUTOCOMPLETE },
3388 { ';', "glance", "show what type of thing a map symbol corresponds to",
3389 doquickwhatis, IFBURIED | GENERALCMD },
3390 { '?', "help", "give a help message", dohelp, IFBURIED | GENERALCMD },
3391 { '\0', "herecmdmenu", "show menu of commands you can do here",
3392 doherecmdmenu, IFBURIED },
3393 { 'V', "history", "show long version and game history",
3394 dohistory, IFBURIED | GENERALCMD },
3395 { 'i', "inventory", "show your inventory", ddoinv, IFBURIED },
3396 { 'I', "inventtype", "inventory specific item types",
3397 dotypeinv, IFBURIED },
3398 { M('i'), "invoke", "invoke an object's special powers",
3399 doinvoke, IFBURIED | AUTOCOMPLETE },
3400 { M('j'), "jump", "jump to another location", dojump, AUTOCOMPLETE },
3401 { C('d'), "kick", "kick something", dokick },
3402 { '\\', "known", "show what object types have been discovered",
3403 dodiscovered, IFBURIED | GENERALCMD },
3404 { '`', "knownclass", "show discovered types for one class of objects",
3405 doclassdisco, IFBURIED | GENERALCMD },
3406 { '\0', "levelchange", "change experience level",
3407 wiz_level_change, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3408 { '\0', "lightsources", "show mobile light sources",
3409 wiz_light_sources, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3410 { ':', "look", "look at what is here", dolook, IFBURIED },
3411 { M('l'), "loot", "loot a box on the floor", doloot, AUTOCOMPLETE },
3412 #ifdef DEBUG_MIGRATING_MONS
3413 { '\0', "migratemons", "migrate N random monsters",
3414 wiz_migrate_mons, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3415 #endif
3416 { M('m'), "monster", "use monster's special ability",
3417 domonability, IFBURIED | AUTOCOMPLETE },
3418 { 'N', "name", "name a monster or an object",
3419 docallcmd, IFBURIED | AUTOCOMPLETE },
3420 { M('o'), "offer", "offer a sacrifice to the gods",
3421 dosacrifice, AUTOCOMPLETE },
3422 { 'o', "open", "open a door", doopen },
3423 { 'O', "options", "show option settings, possibly change them",
3424 doset, IFBURIED | GENERALCMD },
3425 { C('o'), "overview", "show a summary of the explored dungeon",
3426 dooverview, IFBURIED | AUTOCOMPLETE },
3427 { '\0', "panic", "test panic routine (fatal to game)",
3428 wiz_panic, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3429 { 'p', "pay", "pay your shopping bill", dopay },
3430 { ',', "pickup", "pick up things at the current location", dopickup },
3431 { '\0', "polyself", "polymorph self",
3432 wiz_polyself, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3433 { M('p'), "pray", "pray to the gods for help",
3434 dopray, IFBURIED | AUTOCOMPLETE },
3435 { C('p'), "prevmsg", "view recent game messages",
3436 doprev_message, IFBURIED | GENERALCMD },
3437 { 'P', "puton", "put on an accessory (ring, amulet, etc)", doputon },
3438 { 'q', "quaff", "quaff (drink) something", dodrink },
3439 { M('q'), "quit", "exit without saving current game",
3440 done2, IFBURIED | AUTOCOMPLETE | GENERALCMD },
3441 { 'Q', "quiver", "select ammunition for quiver", dowieldquiver },
3442 { 'r', "read", "read a scroll or spellbook", doread },
3443 { C('r'), "redraw", "redraw screen", doredraw, IFBURIED | GENERALCMD },
3444 { 'R', "remove", "remove an accessory (ring, amulet, etc)", doremring },
3445 { M('R'), "ride", "mount or dismount a saddled steed",
3446 doride, AUTOCOMPLETE },
3447 { M('r'), "rub", "rub a lamp or a stone", dorub, AUTOCOMPLETE },
3448 { 'S', "save", "save the game and exit", dosave, IFBURIED | GENERALCMD },
3449 { 's', "search", "search for traps and secret doors",
3450 dosearch, IFBURIED, "searching" },
3451 { '*', "seeall", "show all equipment in use", doprinuse, IFBURIED },
3452 { AMULET_SYM, "seeamulet", "show the amulet currently worn",
3453 dopramulet, IFBURIED },
3454 { ARMOR_SYM, "seearmor", "show the armor currently worn",
3455 doprarm, IFBURIED },
3456 { GOLD_SYM, "seegold", "count your gold", doprgold, IFBURIED },
3457 { '\0', "seenv", "show seen vectors",
3458 wiz_show_seenv, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3459 { RING_SYM, "seerings", "show the ring(s) currently worn",
3460 doprring, IFBURIED },
3461 { SPBOOK_SYM, "seespells", "list and reorder known spells",
3462 dovspell, IFBURIED },
3463 { TOOL_SYM, "seetools", "show the tools currently in use",
3464 doprtool, IFBURIED },
3465 { '^', "seetrap", "show the type of adjacent trap", doidtrap, IFBURIED },
3466 { WEAPON_SYM, "seeweapon", "show the weapon currently wielded",
3467 doprwep, IFBURIED },
3468 { '!', "shell", "do a shell escape",
3469 dosh_core, IFBURIED | GENERALCMD
3470 #ifndef SHELL
3471 | CMD_NOT_AVAILABLE
3472 #endif /* SHELL */
3473 },
3474 { M('s'), "sit", "sit down", dosit, AUTOCOMPLETE },
3475 { '\0', "stats", "show memory statistics",
3476 wiz_show_stats, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3477 { C('z'), "suspend", "suspend the game",
3478 dosuspend_core, IFBURIED | GENERALCMD
3479 #ifndef SUSPEND
3480 | CMD_NOT_AVAILABLE
3481 #endif /* SUSPEND */
3482 },
3483 { 'x', "swap", "swap wielded and secondary weapons", doswapweapon },
3484 { 'T', "takeoff", "take off one piece of armor", dotakeoff },
3485 { 'A', "takeoffall", "remove all armor", doddoremarm },
3486 { C('t'), "teleport", "teleport around the level", dotelecmd, IFBURIED },
3487 { '\0', "terrain", "show map without obstructions",
3488 doterrain, IFBURIED | AUTOCOMPLETE },
3489 { '\0', "therecmdmenu",
3490 "menu of commands you can do from here to adjacent spot",
3491 dotherecmdmenu },
3492 { 't', "throw", "throw something", dothrow },
3493 { '\0', "timeout", "look at timeout queue and hero's timed intrinsics",
3494 wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3495 { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE },
3496 { '_', "travel", "travel to a specific location on the map", dotravel },
3497 { M('t'), "turn", "turn undead away", doturn, IFBURIED | AUTOCOMPLETE },
3498 { 'X', "twoweapon", "toggle two-weapon combat",
3499 dotwoweapon, AUTOCOMPLETE },
3500 { M('u'), "untrap", "untrap something", dountrap, AUTOCOMPLETE },
3501 { '<', "up", "go up a staircase", doup },
3502 { '\0', "vanquished", "list vanquished monsters",
3503 dovanquished, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3504 { M('v'), "version",
3505 "list compile time options for this version of NetHack",
3506 doextversion, IFBURIED | AUTOCOMPLETE | GENERALCMD },
3507 { 'v', "versionshort", "show version", doversion, IFBURIED | GENERALCMD },
3508 { '\0', "vision", "show vision array",
3509 wiz_show_vision, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3510 { '.', "wait", "rest one move while doing nothing",
3511 donull, IFBURIED, "waiting" },
3512 { 'W', "wear", "wear a piece of armor", dowear },
3513 { '&', "whatdoes", "tell what a command does", dowhatdoes, IFBURIED },
3514 { '/', "whatis", "show what type of thing a symbol corresponds to",
3515 dowhatis, IFBURIED | GENERALCMD },
3516 { 'w', "wield", "wield (put in use) a weapon", dowield },
3517 { M('w'), "wipe", "wipe off your face", dowipe, AUTOCOMPLETE },
3518 #ifdef DEBUG
3519 { '\0', "wizbury", "bury objs under and around you",
3520 wiz_debug_cmd_bury, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3521 #endif
3522 { C('e'), "wizdetect", "reveal hidden things within a small radius",
3523 wiz_detect, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3524 { C('g'), "wizgenesis", "create a monster",
3525 wiz_genesis, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3526 { C('i'), "wizidentify", "identify all items in inventory",
3527 wiz_identify, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3528 { '\0', "wizintrinsic", "set an intrinsic",
3529 wiz_intrinsic, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3530 { C('v'), "wizlevelport", "teleport to another level",
3531 wiz_level_tele, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3532 { '\0', "wizmakemap", "recreate the current level",
3533 wiz_makemap, IFBURIED | WIZMODECMD },
3534 { C('f'), "wizmap", "map the level",
3535 wiz_map, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3536 { '\0', "wizrumorcheck", "verify rumor boundaries",
3537 wiz_rumor_check, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3538 { '\0', "wizsmell", "smell monster",
3539 wiz_smell, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3540 { '\0', "wizwhere", "show locations of special levels",
3541 wiz_where, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3542 { C('w'), "wizwish", "wish for something",
3543 wiz_wish, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3544 { '\0', "wmode", "show wall modes",
3545 wiz_show_wmodes, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
3546 { 'z', "zap", "zap a wand", dozap },
3547 { '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */
3548 };
3549
3550 /* for key2extcmddesc() to support dowhatdoes() */
3551 struct movcmd {
3552 uchar k1, k2, k3, k4; /* 'normal', 'qwertz', 'numpad', 'phone' */
3553 const char *txt, *alt; /* compass direction, screen direction */
3554 };
3555 static const struct movcmd movtab[] = {
3556 { 'h', 'h', '4', '4', "west", "left" },
3557 { 'j', 'j', '2', '8', "south", "down" },
3558 { 'k', 'k', '8', '2', "north", "up" },
3559 { 'l', 'l', '6', '6', "east", "right" },
3560 { 'b', 'b', '1', '7', "southwest", "lower left" },
3561 { 'n', 'n', '3', '9', "southeast", "lower right" },
3562 { 'u', 'u', '9', '3', "northeast", "upper right" },
3563 { 'y', 'z', '7', '1', "northwest", "upper left" },
3564 { 0, 0, 0, 0, (char *) 0, (char *) 0 }
3565 };
3566
3567 int extcmdlist_length = SIZE(extcmdlist) - 1;
3568
3569 const char *
key2extcmddesc(key)3570 key2extcmddesc(key)
3571 uchar key;
3572 {
3573 static char key2cmdbuf[48];
3574 const struct movcmd *mov;
3575 int k, c;
3576 uchar M_5 = (uchar) M('5'), M_0 = (uchar) M('0');
3577
3578 /* need to check for movement commands before checking the extended
3579 commands table because it contains entries for number_pad commands
3580 that match !number_pad movement (like 'j' for "jump") */
3581 key2cmdbuf[0] = '\0';
3582 if (movecmd(k = key))
3583 Strcpy(key2cmdbuf, "move"); /* "move or attack"? */
3584 else if (movecmd(k = unctrl(key)))
3585 Strcpy(key2cmdbuf, "rush");
3586 else if (movecmd(k = (Cmd.num_pad ? unmeta(key) : lowc(key))))
3587 Strcpy(key2cmdbuf, "run");
3588 if (*key2cmdbuf) {
3589 for (mov = &movtab[0]; mov->k1; ++mov) {
3590 c = !Cmd.num_pad ? (!Cmd.swap_yz ? mov->k1 : mov->k2)
3591 : (!Cmd.phone_layout ? mov->k3 : mov->k4);
3592 if (c == k) {
3593 Sprintf(eos(key2cmdbuf), " %s (screen %s)",
3594 mov->txt, mov->alt);
3595 return key2cmdbuf;
3596 }
3597 }
3598 } else if (digit(key) || (Cmd.num_pad && digit(unmeta(key)))) {
3599 key2cmdbuf[0] = '\0';
3600 if (!Cmd.num_pad)
3601 Strcpy(key2cmdbuf, "start of, or continuation of, a count");
3602 else if (key == '5' || key == M_5)
3603 Sprintf(key2cmdbuf, "%s prefix",
3604 (!!Cmd.pcHack_compat ^ (key == M_5)) ? "run" : "rush");
3605 else if (key == '0' || (Cmd.pcHack_compat && key == M_0))
3606 Strcpy(key2cmdbuf, "synonym for 'i'");
3607 if (*key2cmdbuf)
3608 return key2cmdbuf;
3609 }
3610 if (Cmd.commands[key]) {
3611 if (Cmd.commands[key]->ef_txt)
3612 return Cmd.commands[key]->ef_desc;
3613
3614 }
3615 return (char *) 0;
3616 }
3617
3618 boolean
bind_key(key,command)3619 bind_key(key, command)
3620 uchar key;
3621 const char *command;
3622 {
3623 struct ext_func_tab *extcmd;
3624
3625 /* special case: "nothing" is reserved for unbinding */
3626 if (!strcmp(command, "nothing")) {
3627 Cmd.commands[key] = (struct ext_func_tab *) 0;
3628 return TRUE;
3629 }
3630
3631 for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) {
3632 if (strcmp(command, extcmd->ef_txt))
3633 continue;
3634 Cmd.commands[key] = extcmd;
3635 #if 0 /* silently accept key binding for unavailable command (!SHELL,&c) */
3636 if ((extcmd->flags & CMD_NOT_AVAILABLE) != 0) {
3637 char buf[BUFSZ];
3638
3639 Sprintf(buf, cmdnotavail, extcmd->ef_txt);
3640 config_error_add("%s", buf);
3641 }
3642 #endif
3643 return TRUE;
3644 }
3645
3646 return FALSE;
3647 }
3648
3649 /* initialize all keyboard commands */
3650 void
commands_init()3651 commands_init()
3652 {
3653 struct ext_func_tab *extcmd;
3654
3655 for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++)
3656 if (extcmd->key)
3657 Cmd.commands[extcmd->key] = extcmd;
3658
3659 (void) bind_key(C('l'), "redraw"); /* if number_pad is set */
3660 /* 'b', 'B' : go sw */
3661 /* 'F' : fight (one time) */
3662 /* 'g', 'G' : multiple go */
3663 /* 'h', 'H' : go west */
3664 (void) bind_key('h', "help"); /* if number_pad is set */
3665 (void) bind_key('j', "jump"); /* if number_pad is on */
3666 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' move commands */
3667 (void) bind_key('k', "kick"); /* if number_pad is on */
3668 (void) bind_key('l', "loot"); /* if number_pad is on */
3669 (void) bind_key(C('n'), "annotate"); /* if number_pad is on */
3670 (void) bind_key(M('n'), "name");
3671 (void) bind_key(M('N'), "name");
3672 (void) bind_key('u', "untrap"); /* if number_pad is on */
3673
3674 /* alt keys: */
3675 (void) bind_key(M('O'), "overview");
3676 (void) bind_key(M('2'), "twoweapon");
3677
3678 /* wait_on_space */
3679 (void) bind_key(' ', "wait");
3680 }
3681
3682 int
dokeylist_putcmds(datawin,docount,cmdflags,exflags,keys_used)3683 dokeylist_putcmds(datawin, docount, cmdflags, exflags, keys_used)
3684 winid datawin;
3685 boolean docount;
3686 int cmdflags, exflags;
3687 boolean *keys_used; /* boolean keys_used[256] */
3688 {
3689 int i;
3690 char buf[BUFSZ];
3691 char buf2[QBUFSZ];
3692 int count = 0;
3693
3694 for (i = 0; i < 256; i++) {
3695 const struct ext_func_tab *extcmd;
3696 uchar key = (uchar) i;
3697
3698 if (keys_used[i])
3699 continue;
3700 if (key == ' ' && !flags.rest_on_space)
3701 continue;
3702 if ((extcmd = Cmd.commands[i]) != (struct ext_func_tab *) 0) {
3703 if ((cmdflags && !(extcmd->flags & cmdflags))
3704 || (exflags && (extcmd->flags & exflags)))
3705 continue;
3706 if (docount) {
3707 count++;
3708 continue;
3709 }
3710 Sprintf(buf, "%-8s %-12s %s", key2txt(key, buf2),
3711 extcmd->ef_txt,
3712 extcmd->ef_desc);
3713 putstr(datawin, 0, buf);
3714 keys_used[i] = TRUE;
3715 }
3716 }
3717 return count;
3718 }
3719
3720 /* list all keys and their bindings, like dat/hh but dynamic */
3721 void
dokeylist(VOID_ARGS)3722 dokeylist(VOID_ARGS)
3723 {
3724 char buf[BUFSZ], buf2[BUFSZ];
3725 uchar key;
3726 boolean keys_used[256] = {0};
3727 winid datawin;
3728 int i;
3729 static const char
3730 run_desc[] = "Prefix: run until something very interesting is seen",
3731 forcefight_desc[] =
3732 "Prefix: force fight even if you don't see a monster";
3733 static const struct {
3734 int nhkf;
3735 const char *desc;
3736 boolean numpad;
3737 } misc_keys[] = {
3738 { NHKF_ESC, "escape from the current query/action", FALSE },
3739 { NHKF_RUSH,
3740 "Prefix: rush until something interesting is seen", FALSE },
3741 { NHKF_RUN, run_desc, FALSE },
3742 { NHKF_RUN2, run_desc, TRUE },
3743 { NHKF_FIGHT, forcefight_desc, FALSE },
3744 { NHKF_FIGHT2, forcefight_desc, TRUE } ,
3745 { NHKF_NOPICKUP,
3746 "Prefix: move without picking up objects/fighting", FALSE },
3747 { NHKF_RUN_NOPICKUP,
3748 "Prefix: run without picking up objects/fighting", FALSE },
3749 { NHKF_DOINV, "view inventory", TRUE },
3750 { NHKF_REQMENU, "Prefix: request a menu", FALSE },
3751 #ifdef REDO
3752 { NHKF_DOAGAIN , "re-do: perform the previous command again", FALSE },
3753 #endif
3754 { 0, (const char *) 0, FALSE }
3755 };
3756
3757 datawin = create_nhwindow(NHW_TEXT);
3758 putstr(datawin, 0, "");
3759 putstr(datawin, 0, " Full Current Key Bindings List");
3760
3761 /* directional keys */
3762 putstr(datawin, 0, "");
3763 putstr(datawin, 0, "Directional keys:");
3764 show_direction_keys(datawin, '.', FALSE); /* '.'==self in direction grid */
3765
3766 keys_used[(uchar) Cmd.move_NW] = keys_used[(uchar) Cmd.move_N]
3767 = keys_used[(uchar) Cmd.move_NE] = keys_used[(uchar) Cmd.move_W]
3768 = keys_used[(uchar) Cmd.move_E] = keys_used[(uchar) Cmd.move_SW]
3769 = keys_used[(uchar) Cmd.move_S] = keys_used[(uchar) Cmd.move_SE]
3770 = TRUE;
3771
3772 if (!iflags.num_pad) {
3773 keys_used[(uchar) highc(Cmd.move_NW)]
3774 = keys_used[(uchar) highc(Cmd.move_N)]
3775 = keys_used[(uchar) highc(Cmd.move_NE)]
3776 = keys_used[(uchar) highc(Cmd.move_W)]
3777 = keys_used[(uchar) highc(Cmd.move_E)]
3778 = keys_used[(uchar) highc(Cmd.move_SW)]
3779 = keys_used[(uchar) highc(Cmd.move_S)]
3780 = keys_used[(uchar) highc(Cmd.move_SE)] = TRUE;
3781 keys_used[(uchar) C(Cmd.move_NW)]
3782 = keys_used[(uchar) C(Cmd.move_N)]
3783 = keys_used[(uchar) C(Cmd.move_NE)]
3784 = keys_used[(uchar) C(Cmd.move_W)]
3785 = keys_used[(uchar) C(Cmd.move_E)]
3786 = keys_used[(uchar) C(Cmd.move_SW)]
3787 = keys_used[(uchar) C(Cmd.move_S)]
3788 = keys_used[(uchar) C(Cmd.move_SE)] = TRUE;
3789 putstr(datawin, 0, "");
3790 putstr(datawin, 0,
3791 "Shift-<direction> will move in specified direction until you hit");
3792 putstr(datawin, 0, " a wall or run into something.");
3793 putstr(datawin, 0,
3794 "Ctrl-<direction> will run in specified direction until something");
3795 putstr(datawin, 0, " very interesting is seen.");
3796 }
3797
3798 putstr(datawin, 0, "");
3799 putstr(datawin, 0, "Miscellaneous keys:");
3800 for (i = 0; misc_keys[i].desc; i++) {
3801 key = Cmd.spkeys[misc_keys[i].nhkf];
3802 if (key && ((misc_keys[i].numpad && iflags.num_pad)
3803 || !misc_keys[i].numpad)) {
3804 keys_used[(uchar) key] = TRUE;
3805 Sprintf(buf, "%-8s %s", key2txt(key, buf2), misc_keys[i].desc);
3806 putstr(datawin, 0, buf);
3807 }
3808 }
3809 #ifndef NO_SIGNAL
3810 putstr(datawin, 0, "^c break out of NetHack (SIGINT)");
3811 keys_used[(uchar) C('c')] = TRUE;
3812 #endif
3813
3814 putstr(datawin, 0, "");
3815 show_menu_controls(datawin, TRUE);
3816
3817 if (dokeylist_putcmds(datawin, TRUE, GENERALCMD, WIZMODECMD, keys_used)) {
3818 putstr(datawin, 0, "");
3819 putstr(datawin, 0, "General commands:");
3820 (void) dokeylist_putcmds(datawin, FALSE, GENERALCMD, WIZMODECMD,
3821 keys_used);
3822 }
3823
3824 if (dokeylist_putcmds(datawin, TRUE, 0, WIZMODECMD, keys_used)) {
3825 putstr(datawin, 0, "");
3826 putstr(datawin, 0, "Game commands:");
3827 (void) dokeylist_putcmds(datawin, FALSE, 0, WIZMODECMD, keys_used);
3828 }
3829
3830 if (wizard
3831 && dokeylist_putcmds(datawin, TRUE, WIZMODECMD, 0, keys_used)) {
3832 putstr(datawin, 0, "");
3833 putstr(datawin, 0, "Wizard-mode commands:");
3834 (void) dokeylist_putcmds(datawin, FALSE, WIZMODECMD, 0, keys_used);
3835 }
3836
3837 display_nhwindow(datawin, FALSE);
3838 destroy_nhwindow(datawin);
3839 }
3840
3841 char
3842 cmd_from_func(fn)
3843 int NDECL((*fn));
3844 {
3845 int i;
3846
3847 for (i = 0; i < 256; ++i)
3848 if (Cmd.commands[i] && Cmd.commands[i]->ef_funct == fn)
3849 return (char) i;
3850 return '\0';
3851 }
3852
3853 /*
3854 * wizard mode sanity_check code
3855 */
3856
3857 static const char template[] = "%-27s %4ld %6ld";
3858 static const char stats_hdr[] = " count bytes";
3859 static const char stats_sep[] = "--------------------------- ----- -------";
3860
3861 STATIC_OVL int
size_obj(otmp)3862 size_obj(otmp)
3863 struct obj *otmp;
3864 {
3865 int sz = (int) sizeof (struct obj);
3866
3867 if (otmp->oextra) {
3868 sz += (int) sizeof (struct oextra);
3869 if (ONAME(otmp))
3870 sz += (int) strlen(ONAME(otmp)) + 1;
3871 if (OMONST(otmp))
3872 sz += size_monst(OMONST(otmp), FALSE);
3873 if (OMID(otmp))
3874 sz += (int) sizeof (unsigned);
3875 if (OLONG(otmp))
3876 sz += (int) sizeof (long);
3877 if (OMAILCMD(otmp))
3878 sz += (int) strlen(OMAILCMD(otmp)) + 1;
3879 }
3880 return sz;
3881 }
3882
3883 STATIC_OVL void
count_obj(chain,total_count,total_size,top,recurse)3884 count_obj(chain, total_count, total_size, top, recurse)
3885 struct obj *chain;
3886 long *total_count;
3887 long *total_size;
3888 boolean top;
3889 boolean recurse;
3890 {
3891 long count, size;
3892 struct obj *obj;
3893
3894 for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
3895 if (top) {
3896 count++;
3897 size += size_obj(obj);
3898 }
3899 if (recurse && obj->cobj)
3900 count_obj(obj->cobj, total_count, total_size, TRUE, TRUE);
3901 }
3902 *total_count += count;
3903 *total_size += size;
3904 }
3905
3906 STATIC_OVL void
obj_chain(win,src,chain,force,total_count,total_size)3907 obj_chain(win, src, chain, force, total_count, total_size)
3908 winid win;
3909 const char *src;
3910 struct obj *chain;
3911 boolean force;
3912 long *total_count;
3913 long *total_size;
3914 {
3915 char buf[BUFSZ];
3916 long count = 0L, size = 0L;
3917
3918 count_obj(chain, &count, &size, TRUE, FALSE);
3919
3920 if (count || size || force) {
3921 *total_count += count;
3922 *total_size += size;
3923 Sprintf(buf, template, src, count, size);
3924 putstr(win, 0, buf);
3925 }
3926 }
3927
3928 STATIC_OVL void
mon_invent_chain(win,src,chain,total_count,total_size)3929 mon_invent_chain(win, src, chain, total_count, total_size)
3930 winid win;
3931 const char *src;
3932 struct monst *chain;
3933 long *total_count;
3934 long *total_size;
3935 {
3936 char buf[BUFSZ];
3937 long count = 0, size = 0;
3938 struct monst *mon;
3939
3940 for (mon = chain; mon; mon = mon->nmon)
3941 count_obj(mon->minvent, &count, &size, TRUE, FALSE);
3942
3943 if (count || size) {
3944 *total_count += count;
3945 *total_size += size;
3946 Sprintf(buf, template, src, count, size);
3947 putstr(win, 0, buf);
3948 }
3949 }
3950
3951 STATIC_OVL void
contained_stats(win,src,total_count,total_size)3952 contained_stats(win, src, total_count, total_size)
3953 winid win;
3954 const char *src;
3955 long *total_count;
3956 long *total_size;
3957 {
3958 char buf[BUFSZ];
3959 long count = 0, size = 0;
3960 struct monst *mon;
3961
3962 count_obj(invent, &count, &size, FALSE, TRUE);
3963 count_obj(fobj, &count, &size, FALSE, TRUE);
3964 count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE);
3965 count_obj(migrating_objs, &count, &size, FALSE, TRUE);
3966 /* DEADMONSTER check not required in this loop since they have no
3967 * inventory */
3968 for (mon = fmon; mon; mon = mon->nmon)
3969 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3970 for (mon = migrating_mons; mon; mon = mon->nmon)
3971 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3972
3973 if (count || size) {
3974 *total_count += count;
3975 *total_size += size;
3976 Sprintf(buf, template, src, count, size);
3977 putstr(win, 0, buf);
3978 }
3979 }
3980
3981 STATIC_OVL int
size_monst(mtmp,incl_wsegs)3982 size_monst(mtmp, incl_wsegs)
3983 struct monst *mtmp;
3984 boolean incl_wsegs;
3985 {
3986 int sz = (int) sizeof (struct monst);
3987
3988 if (mtmp->wormno && incl_wsegs)
3989 sz += size_wseg(mtmp);
3990
3991 if (mtmp->mextra) {
3992 sz += (int) sizeof (struct mextra);
3993 if (MNAME(mtmp))
3994 sz += (int) strlen(MNAME(mtmp)) + 1;
3995 if (EGD(mtmp))
3996 sz += (int) sizeof (struct egd);
3997 if (EPRI(mtmp))
3998 sz += (int) sizeof (struct epri);
3999 if (ESHK(mtmp))
4000 sz += (int) sizeof (struct eshk);
4001 if (EMIN(mtmp))
4002 sz += (int) sizeof (struct emin);
4003 if (EDOG(mtmp))
4004 sz += (int) sizeof (struct edog);
4005 /* mextra->mcorpsenm doesn't point to more memory */
4006 }
4007 return sz;
4008 }
4009
4010 STATIC_OVL void
mon_chain(win,src,chain,force,total_count,total_size)4011 mon_chain(win, src, chain, force, total_count, total_size)
4012 winid win;
4013 const char *src;
4014 struct monst *chain;
4015 boolean force;
4016 long *total_count;
4017 long *total_size;
4018 {
4019 char buf[BUFSZ];
4020 long count, size;
4021 struct monst *mon;
4022 /* mon->wormno means something different for migrating_mons and mydogs */
4023 boolean incl_wsegs = !strcmpi(src, "fmon");
4024
4025 count = size = 0L;
4026 for (mon = chain; mon; mon = mon->nmon) {
4027 count++;
4028 size += size_monst(mon, incl_wsegs);
4029 }
4030 if (count || size || force) {
4031 *total_count += count;
4032 *total_size += size;
4033 Sprintf(buf, template, src, count, size);
4034 putstr(win, 0, buf);
4035 }
4036 }
4037
4038 STATIC_OVL void
misc_stats(win,total_count,total_size)4039 misc_stats(win, total_count, total_size)
4040 winid win;
4041 long *total_count;
4042 long *total_size;
4043 {
4044 char buf[BUFSZ], hdrbuf[QBUFSZ];
4045 long count, size;
4046 int idx;
4047 struct trap *tt;
4048 struct damage *sd; /* shop damage */
4049 struct kinfo *k; /* delayed killer */
4050 struct cemetery *bi; /* bones info */
4051
4052 /* traps and engravings are output unconditionally;
4053 * others only if nonzero
4054 */
4055 count = size = 0L;
4056 for (tt = ftrap; tt; tt = tt->ntrap) {
4057 ++count;
4058 size += (long) sizeof *tt;
4059 }
4060 *total_count += count;
4061 *total_size += size;
4062 Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap));
4063 Sprintf(buf, template, hdrbuf, count, size);
4064 putstr(win, 0, buf);
4065
4066 count = size = 0L;
4067 engr_stats("engravings, size %ld+text", hdrbuf, &count, &size);
4068 *total_count += count;
4069 *total_size += size;
4070 Sprintf(buf, template, hdrbuf, count, size);
4071 putstr(win, 0, buf);
4072
4073 count = size = 0L;
4074 light_stats("light sources, size %ld", hdrbuf, &count, &size);
4075 if (count || size) {
4076 *total_count += count;
4077 *total_size += size;
4078 Sprintf(buf, template, hdrbuf, count, size);
4079 putstr(win, 0, buf);
4080 }
4081
4082 count = size = 0L;
4083 timer_stats("timers, size %ld", hdrbuf, &count, &size);
4084 if (count || size) {
4085 *total_count += count;
4086 *total_size += size;
4087 Sprintf(buf, template, hdrbuf, count, size);
4088 putstr(win, 0, buf);
4089 }
4090
4091 count = size = 0L;
4092 for (sd = level.damagelist; sd; sd = sd->next) {
4093 ++count;
4094 size += (long) sizeof *sd;
4095 }
4096 if (count || size) {
4097 *total_count += count;
4098 *total_size += size;
4099 Sprintf(hdrbuf, "shop damage, size %ld",
4100 (long) sizeof (struct damage));
4101 Sprintf(buf, template, hdrbuf, count, size);
4102 putstr(win, 0, buf);
4103 }
4104
4105 count = size = 0L;
4106 region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size);
4107 if (count || size) {
4108 *total_count += count;
4109 *total_size += size;
4110 Sprintf(buf, template, hdrbuf, count, size);
4111 putstr(win, 0, buf);
4112 }
4113
4114 count = size = 0L;
4115 for (k = killer.next; k; k = k->next) {
4116 ++count;
4117 size += (long) sizeof *k;
4118 }
4119 if (count || size) {
4120 *total_count += count;
4121 *total_size += size;
4122 Sprintf(hdrbuf, "delayed killer%s, size %ld",
4123 plur(count), (long) sizeof (struct kinfo));
4124 Sprintf(buf, template, hdrbuf, count, size);
4125 putstr(win, 0, buf);
4126 }
4127
4128 count = size = 0L;
4129 for (bi = level.bonesinfo; bi; bi = bi->next) {
4130 ++count;
4131 size += (long) sizeof *bi;
4132 }
4133 if (count || size) {
4134 *total_count += count;
4135 *total_size += size;
4136 Sprintf(hdrbuf, "bones history, size %ld",
4137 (long) sizeof (struct cemetery));
4138 Sprintf(buf, template, hdrbuf, count, size);
4139 putstr(win, 0, buf);
4140 }
4141
4142 count = size = 0L;
4143 for (idx = 0; idx < NUM_OBJECTS; ++idx)
4144 if (objects[idx].oc_uname) {
4145 ++count;
4146 size += (long) (strlen(objects[idx].oc_uname) + 1);
4147 }
4148 if (count || size) {
4149 *total_count += count;
4150 *total_size += size;
4151 Strcpy(hdrbuf, "object type names, text");
4152 Sprintf(buf, template, hdrbuf, count, size);
4153 putstr(win, 0, buf);
4154 }
4155 }
4156
4157 /*
4158 * Display memory usage of all monsters and objects on the level.
4159 */
4160 static int
wiz_show_stats()4161 wiz_show_stats()
4162 {
4163 char buf[BUFSZ];
4164 winid win;
4165 long total_obj_size, total_obj_count,
4166 total_mon_size, total_mon_count,
4167 total_ovr_size, total_ovr_count,
4168 total_misc_size, total_misc_count;
4169
4170 win = create_nhwindow(NHW_TEXT);
4171 putstr(win, 0, "Current memory statistics:");
4172
4173 total_obj_count = total_obj_size = 0L;
4174 putstr(win, 0, stats_hdr);
4175 Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj));
4176 putstr(win, 0, buf);
4177 obj_chain(win, "invent", invent, TRUE, &total_obj_count, &total_obj_size);
4178 obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size);
4179 obj_chain(win, "buried", level.buriedobjlist, FALSE,
4180 &total_obj_count, &total_obj_size);
4181 obj_chain(win, "migrating obj", migrating_objs, FALSE,
4182 &total_obj_count, &total_obj_size);
4183 obj_chain(win, "billobjs", billobjs, FALSE,
4184 &total_obj_count, &total_obj_size);
4185 mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size);
4186 mon_invent_chain(win, "migrating minvent", migrating_mons,
4187 &total_obj_count, &total_obj_size);
4188 contained_stats(win, "contained", &total_obj_count, &total_obj_size);
4189 putstr(win, 0, stats_sep);
4190 Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size);
4191 putstr(win, 0, buf);
4192
4193 total_mon_count = total_mon_size = 0L;
4194 putstr(win, 0, "");
4195 Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst));
4196 putstr(win, 0, buf);
4197 mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size);
4198 mon_chain(win, "migrating", migrating_mons, FALSE,
4199 &total_mon_count, &total_mon_size);
4200 /* 'mydogs' is only valid during level change or end of game disclosure,
4201 but conceivably we've been called from within debugger at such time */
4202 if (mydogs) /* monsters accompanying hero */
4203 mon_chain(win, "mydogs", mydogs, FALSE,
4204 &total_mon_count, &total_mon_size);
4205 putstr(win, 0, stats_sep);
4206 Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size);
4207 putstr(win, 0, buf);
4208
4209 total_ovr_count = total_ovr_size = 0L;
4210 putstr(win, 0, "");
4211 putstr(win, 0, " Overview");
4212 overview_stats(win, template, &total_ovr_count, &total_ovr_size);
4213 putstr(win, 0, stats_sep);
4214 Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size);
4215 putstr(win, 0, buf);
4216
4217 total_misc_count = total_misc_size = 0L;
4218 putstr(win, 0, "");
4219 putstr(win, 0, " Miscellaneous");
4220 misc_stats(win, &total_misc_count, &total_misc_size);
4221 putstr(win, 0, stats_sep);
4222 Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size);
4223 putstr(win, 0, buf);
4224
4225 putstr(win, 0, "");
4226 putstr(win, 0, stats_sep);
4227 Sprintf(buf, template, " Grand total",
4228 (total_obj_count + total_mon_count
4229 + total_ovr_count + total_misc_count),
4230 (total_obj_size + total_mon_size
4231 + total_ovr_size + total_misc_size));
4232 putstr(win, 0, buf);
4233
4234 #if defined(__BORLANDC__) && !defined(_WIN32)
4235 show_borlandc_stats(win);
4236 #endif
4237
4238 display_nhwindow(win, FALSE);
4239 destroy_nhwindow(win);
4240 return 0;
4241 }
4242
4243 void
sanity_check()4244 sanity_check()
4245 {
4246 obj_sanity_check();
4247 timer_sanity_check();
4248 mon_sanity_check();
4249 light_sources_sanity_check();
4250 bc_sanity_check();
4251 }
4252
4253 #ifdef DEBUG_MIGRATING_MONS
4254 static int
wiz_migrate_mons()4255 wiz_migrate_mons()
4256 {
4257 int mcount = 0;
4258 char inbuf[BUFSZ] = DUMMY;
4259 struct permonst *ptr;
4260 struct monst *mtmp;
4261 d_level tolevel;
4262
4263 getlin("How many random monsters to migrate? [0]", inbuf);
4264 if (*inbuf == '\033')
4265 return 0;
4266 mcount = atoi(inbuf);
4267 if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz))
4268 return 0;
4269 while (mcount > 0) {
4270 if (Is_stronghold(&u.uz))
4271 assign_level(&tolevel, &valley_level);
4272 else
4273 get_level(&tolevel, depth(&u.uz) + 1);
4274 ptr = rndmonst();
4275 mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS);
4276 if (mtmp)
4277 migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM,
4278 (coord *) 0);
4279 mcount--;
4280 }
4281 return 0;
4282 }
4283 #endif
4284
4285 struct {
4286 int nhkf;
4287 char key;
4288 const char *name;
4289 } const spkeys_binds[] = {
4290 { NHKF_ESC, '\033', (char *) 0 }, /* no binding */
4291 { NHKF_DOAGAIN, DOAGAIN, "repeat" },
4292 { NHKF_REQMENU, 'm', "reqmenu" },
4293 { NHKF_RUN, 'G', "run" },
4294 { NHKF_RUN2, '5', "run.numpad" },
4295 { NHKF_RUSH, 'g', "rush" },
4296 { NHKF_FIGHT, 'F', "fight" },
4297 { NHKF_FIGHT2, '-', "fight.numpad" },
4298 { NHKF_NOPICKUP, 'm', "nopickup" },
4299 { NHKF_RUN_NOPICKUP, 'M', "run.nopickup" },
4300 { NHKF_DOINV, '0', "doinv" },
4301 { NHKF_TRAVEL, CMD_TRAVEL, (char *) 0 }, /* no binding */
4302 { NHKF_CLICKLOOK, CMD_CLICKLOOK, (char *) 0 }, /* no binding */
4303 { NHKF_REDRAW, C('r'), "redraw" },
4304 { NHKF_REDRAW2, C('l'), "redraw.numpad" },
4305 { NHKF_GETDIR_SELF, '.', "getdir.self" },
4306 { NHKF_GETDIR_SELF2, 's', "getdir.self2" },
4307 { NHKF_GETDIR_HELP, '?', "getdir.help" },
4308 { NHKF_COUNT, 'n', "count" },
4309 { NHKF_GETPOS_SELF, '@', "getpos.self" },
4310 { NHKF_GETPOS_PICK, '.', "getpos.pick" },
4311 { NHKF_GETPOS_PICK_Q, ',', "getpos.pick.quick" },
4312 { NHKF_GETPOS_PICK_O, ';', "getpos.pick.once" },
4313 { NHKF_GETPOS_PICK_V, ':', "getpos.pick.verbose" },
4314 { NHKF_GETPOS_SHOWVALID, '$', "getpos.valid" },
4315 { NHKF_GETPOS_AUTODESC, '#', "getpos.autodescribe" },
4316 { NHKF_GETPOS_MON_NEXT, 'm', "getpos.mon.next" },
4317 { NHKF_GETPOS_MON_PREV, 'M', "getpos.mon.prev" },
4318 { NHKF_GETPOS_OBJ_NEXT, 'o', "getpos.obj.next" },
4319 { NHKF_GETPOS_OBJ_PREV, 'O', "getpos.obj.prev" },
4320 { NHKF_GETPOS_DOOR_NEXT, 'd', "getpos.door.next" },
4321 { NHKF_GETPOS_DOOR_PREV, 'D', "getpos.door.prev" },
4322 { NHKF_GETPOS_UNEX_NEXT, 'x', "getpos.unexplored.next" },
4323 { NHKF_GETPOS_UNEX_PREV, 'X', "getpos.unexplored.prev" },
4324 { NHKF_GETPOS_VALID_NEXT, 'z', "getpos.valid.next" },
4325 { NHKF_GETPOS_VALID_PREV, 'Z', "getpos.valid.prev" },
4326 { NHKF_GETPOS_INTERESTING_NEXT, 'a', "getpos.all.next" },
4327 { NHKF_GETPOS_INTERESTING_PREV, 'A', "getpos.all.prev" },
4328 { NHKF_GETPOS_HELP, '?', "getpos.help" },
4329 { NHKF_GETPOS_LIMITVIEW, '"', "getpos.filter" },
4330 { NHKF_GETPOS_MOVESKIP, '*', "getpos.moveskip" },
4331 { NHKF_GETPOS_MENU, '!', "getpos.menu" }
4332 };
4333
4334 boolean
bind_specialkey(key,command)4335 bind_specialkey(key, command)
4336 uchar key;
4337 const char *command;
4338 {
4339 int i;
4340 for (i = 0; i < SIZE(spkeys_binds); i++) {
4341 if (!spkeys_binds[i].name || strcmp(command, spkeys_binds[i].name))
4342 continue;
4343 Cmd.spkeys[spkeys_binds[i].nhkf] = key;
4344 return TRUE;
4345 }
4346 return FALSE;
4347 }
4348
4349 /* returns a one-byte character from the text (it may massacre the txt
4350 * buffer) */
4351 char
txt2key(txt)4352 txt2key(txt)
4353 char *txt;
4354 {
4355 txt = trimspaces(txt);
4356 if (!*txt)
4357 return '\0';
4358
4359 /* simple character */
4360 if (!txt[1])
4361 return txt[0];
4362
4363 /* a few special entries */
4364 if (!strcmp(txt, "<enter>"))
4365 return '\n';
4366 if (!strcmp(txt, "<space>"))
4367 return ' ';
4368 if (!strcmp(txt, "<esc>"))
4369 return '\033';
4370
4371 /* control and meta keys */
4372 switch (*txt) {
4373 case 'm': /* can be mx, Mx, m-x, M-x */
4374 case 'M':
4375 txt++;
4376 if (*txt == '-' && txt[1])
4377 txt++;
4378 if (txt[1])
4379 return '\0';
4380 return M(*txt);
4381 case 'c': /* can be cx, Cx, ^x, c-x, C-x, ^-x */
4382 case 'C':
4383 case '^':
4384 txt++;
4385 if (*txt == '-' && txt[1])
4386 txt++;
4387 if (txt[1])
4388 return '\0';
4389 return C(*txt);
4390 }
4391
4392 /* ascii codes: must be three-digit decimal */
4393 if (*txt >= '0' && *txt <= '9') {
4394 uchar key = 0;
4395 int i;
4396
4397 for (i = 0; i < 3; i++) {
4398 if (txt[i] < '0' || txt[i] > '9')
4399 return '\0';
4400 key = 10 * key + txt[i] - '0';
4401 }
4402 return key;
4403 }
4404
4405 return '\0';
4406 }
4407
4408 /* returns the text for a one-byte encoding;
4409 * must be shorter than a tab for proper formatting */
4410 char *
key2txt(c,txt)4411 key2txt(c, txt)
4412 uchar c;
4413 char *txt; /* sufficiently long buffer */
4414 {
4415 /* should probably switch to "SPC", "ESC", "RET"
4416 since nethack's documentation uses ESC for <escape> */
4417 if (c == ' ')
4418 Sprintf(txt, "<space>");
4419 else if (c == '\033')
4420 Sprintf(txt, "<esc>");
4421 else if (c == '\n')
4422 Sprintf(txt, "<enter>");
4423 else if (c == '\177')
4424 Sprintf(txt, "<del>"); /* "<delete>" won't fit */
4425 else
4426 Strcpy(txt, visctrl((char) c));
4427 return txt;
4428 }
4429
4430
4431 void
parseautocomplete(autocomplete,condition)4432 parseautocomplete(autocomplete, condition)
4433 char *autocomplete;
4434 boolean condition;
4435 {
4436 struct ext_func_tab *efp;
4437 register char *autoc;
4438
4439 /* break off first autocomplete from the rest; parse the rest */
4440 if ((autoc = index(autocomplete, ',')) != 0
4441 || (autoc = index(autocomplete, ':')) != 0) {
4442 *autoc++ = '\0';
4443 parseautocomplete(autoc, condition);
4444 }
4445
4446 /* strip leading and trailing white space */
4447 autocomplete = trimspaces(autocomplete);
4448
4449 if (!*autocomplete)
4450 return;
4451
4452 /* take off negation */
4453 if (*autocomplete == '!') {
4454 /* unlike most options, a leading "no" might actually be a part of
4455 * the extended command. Thus you have to use ! */
4456 autocomplete++;
4457 autocomplete = trimspaces(autocomplete);
4458 condition = !condition;
4459 }
4460
4461 /* find and modify the extended command */
4462 for (efp = extcmdlist; efp->ef_txt; efp++) {
4463 if (!strcmp(autocomplete, efp->ef_txt)) {
4464 if (condition)
4465 efp->flags |= AUTOCOMPLETE;
4466 else
4467 efp->flags &= ~AUTOCOMPLETE;
4468 return;
4469 }
4470 }
4471
4472 /* not a real extended command */
4473 raw_printf("Bad autocomplete: invalid extended command '%s'.",
4474 autocomplete);
4475 wait_synch();
4476 }
4477
4478 /* called at startup and after number_pad is twiddled */
4479 void
reset_commands(initial)4480 reset_commands(initial)
4481 boolean initial;
4482 {
4483 static const char sdir[] = "hykulnjb><",
4484 sdir_swap_yz[] = "hzkulnjb><",
4485 ndir[] = "47896321><",
4486 ndir_phone_layout[] = "41236987><";
4487 static const int ylist[] = {
4488 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
4489 };
4490 static struct ext_func_tab *back_dir_cmd[8];
4491 const struct ext_func_tab *cmdtmp;
4492 boolean flagtemp;
4493 int c, i, updated = 0;
4494 static boolean backed_dir_cmd = FALSE;
4495
4496 if (initial) {
4497 updated = 1;
4498 Cmd.num_pad = FALSE;
4499 Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE;
4500 for (i = 0; i < SIZE(spkeys_binds); i++)
4501 Cmd.spkeys[spkeys_binds[i].nhkf] = spkeys_binds[i].key;
4502 commands_init();
4503 } else {
4504
4505 if (backed_dir_cmd) {
4506 for (i = 0; i < 8; i++) {
4507 Cmd.commands[(uchar) Cmd.dirchars[i]] = back_dir_cmd[i];
4508 }
4509 }
4510
4511 /* basic num_pad */
4512 flagtemp = iflags.num_pad;
4513 if (flagtemp != Cmd.num_pad) {
4514 Cmd.num_pad = flagtemp;
4515 ++updated;
4516 }
4517 /* swap_yz mode (only applicable for !num_pad); intended for
4518 QWERTZ keyboard used in Central Europe, particularly Germany */
4519 flagtemp = (iflags.num_pad_mode & 1) ? !Cmd.num_pad : FALSE;
4520 if (flagtemp != Cmd.swap_yz) {
4521 Cmd.swap_yz = flagtemp;
4522 ++updated;
4523 /* Cmd.swap_yz has been toggled;
4524 perform the swap (or reverse previous one) */
4525 for (i = 0; i < SIZE(ylist); i++) {
4526 c = ylist[i] & 0xff;
4527 cmdtmp = Cmd.commands[c]; /* tmp = [y] */
4528 Cmd.commands[c] = Cmd.commands[c + 1]; /* [y] = [z] */
4529 Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */
4530 }
4531 }
4532 /* MSDOS compatibility mode (only applicable for num_pad) */
4533 flagtemp = (iflags.num_pad_mode & 1) ? Cmd.num_pad : FALSE;
4534 if (flagtemp != Cmd.pcHack_compat) {
4535 Cmd.pcHack_compat = flagtemp;
4536 ++updated;
4537 /* pcHack_compat has been toggled */
4538 c = M('5') & 0xff;
4539 cmdtmp = Cmd.commands['5'];
4540 Cmd.commands['5'] = Cmd.commands[c];
4541 Cmd.commands[c] = cmdtmp;
4542 c = M('0') & 0xff;
4543 Cmd.commands[c] = Cmd.pcHack_compat ? Cmd.commands['I'] : 0;
4544 }
4545 /* phone keypad layout (only applicable for num_pad) */
4546 flagtemp = (iflags.num_pad_mode & 2) ? Cmd.num_pad : FALSE;
4547 if (flagtemp != Cmd.phone_layout) {
4548 Cmd.phone_layout = flagtemp;
4549 ++updated;
4550 /* phone_layout has been toggled */
4551 for (i = 0; i < 3; i++) {
4552 c = '1' + i; /* 1,2,3 <-> 7,8,9 */
4553 cmdtmp = Cmd.commands[c]; /* tmp = [1] */
4554 Cmd.commands[c] = Cmd.commands[c + 6]; /* [1] = [7] */
4555 Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */
4556 c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
4557 cmdtmp = Cmd.commands[c]; /* tmp = [M-1] */
4558 Cmd.commands[c] = Cmd.commands[c + 6]; /* [M-1] = [M-7] */
4559 Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */
4560 }
4561 }
4562 } /*?initial*/
4563
4564 if (updated)
4565 Cmd.serialno++;
4566 Cmd.dirchars = !Cmd.num_pad
4567 ? (!Cmd.swap_yz ? sdir : sdir_swap_yz)
4568 : (!Cmd.phone_layout ? ndir : ndir_phone_layout);
4569 Cmd.alphadirchars = !Cmd.num_pad ? Cmd.dirchars : sdir;
4570
4571 Cmd.move_W = Cmd.dirchars[0];
4572 Cmd.move_NW = Cmd.dirchars[1];
4573 Cmd.move_N = Cmd.dirchars[2];
4574 Cmd.move_NE = Cmd.dirchars[3];
4575 Cmd.move_E = Cmd.dirchars[4];
4576 Cmd.move_SE = Cmd.dirchars[5];
4577 Cmd.move_S = Cmd.dirchars[6];
4578 Cmd.move_SW = Cmd.dirchars[7];
4579
4580 if (!initial) {
4581 for (i = 0; i < 8; i++) {
4582 back_dir_cmd[i] =
4583 (struct ext_func_tab *) Cmd.commands[(uchar) Cmd.dirchars[i]];
4584 Cmd.commands[(uchar) Cmd.dirchars[i]] = (struct ext_func_tab *) 0;
4585 }
4586 backed_dir_cmd = TRUE;
4587 for (i = 0; i < 8; i++)
4588 (void) bind_key(Cmd.dirchars[i], "nothing");
4589 }
4590 }
4591
4592 /* non-movement commands which accept 'm' prefix to request menu operation */
4593 STATIC_OVL boolean
4594 accept_menu_prefix(cmd_func)
4595 int NDECL((*cmd_func));
4596 {
4597 if (cmd_func == dopickup || cmd_func == dotip
4598 /* eat, #offer, and apply tinning-kit all use floorfood() to pick
4599 an item on floor or in invent; 'm' skips picking from floor
4600 (ie, inventory only) rather than request use of menu operation */
4601 || cmd_func == doeat || cmd_func == dosacrifice || cmd_func == doapply
4602 /* 'm' for removing saddle from adjacent monster without checking
4603 for containers at <u.ux,u.uy> */
4604 || cmd_func == doloot
4605 /* travel: pop up a menu of interesting targets in view */
4606 || cmd_func == dotravel
4607 /* wizard mode ^V and ^T */
4608 || cmd_func == wiz_level_tele || cmd_func == dotelecmd
4609 /* 'm' prefix allowed for some extended commands */
4610 || cmd_func == doextcmd || cmd_func == doextlist)
4611 return TRUE;
4612 return FALSE;
4613 }
4614
4615 char
randomkey()4616 randomkey()
4617 {
4618 static unsigned i = 0;
4619 char c;
4620
4621 switch (rn2(16)) {
4622 default:
4623 c = '\033';
4624 break;
4625 case 0:
4626 c = '\n';
4627 break;
4628 case 1:
4629 case 2:
4630 case 3:
4631 case 4:
4632 c = (char) rn1('~' - ' ' + 1, ' ');
4633 break;
4634 case 5:
4635 c = (char) (rn2(2) ? '\t' : ' ');
4636 break;
4637 case 6:
4638 c = (char) rn1('z' - 'a' + 1, 'a');
4639 break;
4640 case 7:
4641 c = (char) rn1('Z' - 'A' + 1, 'A');
4642 break;
4643 case 8:
4644 c = extcmdlist[i++ % SIZE(extcmdlist)].key;
4645 break;
4646 case 9:
4647 c = '#';
4648 break;
4649 case 10:
4650 case 11:
4651 case 12:
4652 c = Cmd.dirchars[rn2(8)];
4653 if (!rn2(7))
4654 c = !Cmd.num_pad ? (!rn2(3) ? C(c) : (c + 'A' - 'a')) : M(c);
4655 break;
4656 case 13:
4657 c = (char) rn1('9' - '0' + 1, '0');
4658 break;
4659 case 14:
4660 /* any char, but avoid '\0' because it's used for mouse click */
4661 c = (char) rnd(iflags.wc_eight_bit_input ? 255 : 127);
4662 break;
4663 }
4664
4665 return c;
4666 }
4667
4668 void
random_response(buf,sz)4669 random_response(buf, sz)
4670 char *buf;
4671 int sz;
4672 {
4673 char c;
4674 int count = 0;
4675
4676 for (;;) {
4677 c = randomkey();
4678 if (c == '\n')
4679 break;
4680 if (c == '\033') {
4681 count = 0;
4682 break;
4683 }
4684 if (count < sz - 1)
4685 buf[count++] = c;
4686 }
4687 buf[count] = '\0';
4688 }
4689
4690 int
rnd_extcmd_idx(VOID_ARGS)4691 rnd_extcmd_idx(VOID_ARGS)
4692 {
4693 return rn2(extcmdlist_length + 1) - 1;
4694 }
4695
4696 int
ch2spkeys(c,start,end)4697 ch2spkeys(c, start, end)
4698 char c;
4699 int start,end;
4700 {
4701 int i;
4702
4703 for (i = start; i <= end; i++)
4704 if (Cmd.spkeys[i] == c)
4705 return i;
4706 return NHKF_ESC;
4707 }
4708
4709 void
rhack(cmd)4710 rhack(cmd)
4711 register char *cmd;
4712 {
4713 int spkey;
4714 boolean prefix_seen, bad_command,
4715 firsttime = (cmd == 0);
4716
4717 iflags.menu_requested = FALSE;
4718 #ifdef SAFERHANGUP
4719 if (program_state.done_hup)
4720 end_of_input();
4721 #endif
4722 if (firsttime) {
4723 context.nopick = 0;
4724 cmd = parse();
4725 }
4726 if (*cmd == Cmd.spkeys[NHKF_ESC]) {
4727 context.move = FALSE;
4728 return;
4729 }
4730 if (*cmd == DOAGAIN && !in_doagain && saveq[0]) {
4731 in_doagain = TRUE;
4732 stail = 0;
4733 rhack((char *) 0); /* read and execute command */
4734 in_doagain = FALSE;
4735 return;
4736 }
4737 /* Special case of *cmd == ' ' handled better below */
4738 if (!*cmd || *cmd == (char) 0377) {
4739 nhbell();
4740 context.move = FALSE;
4741 return; /* probably we just had an interrupt */
4742 }
4743
4744 /* handle most movement commands */
4745 prefix_seen = FALSE;
4746 context.travel = context.travel1 = 0;
4747 spkey = ch2spkeys(*cmd, NHKF_RUN, NHKF_CLICKLOOK);
4748
4749 switch (spkey) {
4750 case NHKF_RUSH:
4751 if (movecmd(cmd[1])) {
4752 context.run = 2;
4753 domove_attempting |= DOMOVE_RUSH;
4754 } else
4755 prefix_seen = TRUE;
4756 break;
4757 case NHKF_RUN2:
4758 if (!Cmd.num_pad)
4759 break;
4760 /*FALLTHRU*/
4761 case NHKF_RUN:
4762 if (movecmd(lowc(cmd[1]))) {
4763 context.run = 3;
4764 domove_attempting |= DOMOVE_RUSH;
4765 } else
4766 prefix_seen = TRUE;
4767 break;
4768 case NHKF_FIGHT2:
4769 if (!Cmd.num_pad)
4770 break;
4771 /*FALLTHRU*/
4772 /* Effects of movement commands and invisible monsters:
4773 * m: always move onto space (even if 'I' remembered)
4774 * F: always attack space (even if 'I' not remembered)
4775 * normal movement: attack if 'I', move otherwise.
4776 */
4777 case NHKF_FIGHT:
4778 if (movecmd(cmd[1])) {
4779 context.forcefight = 1;
4780 domove_attempting |= DOMOVE_WALK;
4781 } else
4782 prefix_seen = TRUE;
4783 break;
4784 case NHKF_NOPICKUP:
4785 if (movecmd(cmd[1]) || u.dz) {
4786 context.run = 0;
4787 context.nopick = 1;
4788 if (!u.dz)
4789 domove_attempting |= DOMOVE_WALK;
4790 else
4791 cmd[0] = cmd[1]; /* "m<" or "m>" */
4792 } else
4793 prefix_seen = TRUE;
4794 break;
4795 case NHKF_RUN_NOPICKUP:
4796 if (movecmd(lowc(cmd[1]))) {
4797 context.run = 1;
4798 context.nopick = 1;
4799 domove_attempting |= DOMOVE_RUSH;
4800 } else
4801 prefix_seen = TRUE;
4802 break;
4803 case NHKF_DOINV:
4804 if (!Cmd.num_pad)
4805 break;
4806 (void) ddoinv(); /* a convenience borrowed from the PC */
4807 context.move = FALSE;
4808 multi = 0;
4809 return;
4810 case NHKF_CLICKLOOK:
4811 if (iflags.clicklook) {
4812 context.move = FALSE;
4813 do_look(2, &clicklook_cc);
4814 }
4815 return;
4816 case NHKF_TRAVEL:
4817 if (flags.travelcmd) {
4818 context.travel = 1;
4819 context.travel1 = 1;
4820 context.run = 8;
4821 context.nopick = 1;
4822 domove_attempting |= DOMOVE_RUSH;
4823 break;
4824 }
4825 /*FALLTHRU*/
4826 default:
4827 if (movecmd(*cmd)) { /* ordinary movement */
4828 context.run = 0; /* only matters here if it was 8 */
4829 domove_attempting |= DOMOVE_WALK;
4830 } else if (movecmd(Cmd.num_pad ? unmeta(*cmd) : lowc(*cmd))) {
4831 context.run = 1;
4832 domove_attempting |= DOMOVE_RUSH;
4833 } else if (movecmd(unctrl(*cmd))) {
4834 context.run = 3;
4835 domove_attempting |= DOMOVE_RUSH;
4836 }
4837 break;
4838 }
4839
4840 /* some special prefix handling */
4841 /* overload 'm' prefix to mean "request a menu" */
4842 if (prefix_seen && cmd[0] == Cmd.spkeys[NHKF_REQMENU]) {
4843 /* (for func_tab cast, see below) */
4844 const struct ext_func_tab *ft = Cmd.commands[cmd[1] & 0xff];
4845 int NDECL((*func)) = ft ? ((struct ext_func_tab *) ft)->ef_funct : 0;
4846
4847 if (func && accept_menu_prefix(func)) {
4848 iflags.menu_requested = TRUE;
4849 ++cmd;
4850 }
4851 }
4852
4853 if (((domove_attempting & (DOMOVE_RUSH | DOMOVE_WALK)) != 0L)
4854 && !context.travel && !dxdy_moveok()) {
4855 /* trying to move diagonally as a grid bug;
4856 this used to be treated by movecmd() as not being
4857 a movement attempt, but that didn't provide for any
4858 feedback and led to strangeness if the key pressed
4859 ('u' in particular) was overloaded for num_pad use */
4860 You_cant("get there from here...");
4861 context.run = 0;
4862 context.nopick = context.forcefight = FALSE;
4863 context.move = context.mv = FALSE;
4864 multi = 0;
4865 return;
4866 }
4867
4868 if ((domove_attempting & DOMOVE_WALK) != 0L) {
4869 if (multi)
4870 context.mv = TRUE;
4871 domove();
4872 context.forcefight = 0;
4873 return;
4874 } else if ((domove_attempting & DOMOVE_RUSH) != 0L) {
4875 if (firsttime) {
4876 if (!multi)
4877 multi = max(COLNO, ROWNO);
4878 u.last_str_turn = 0;
4879 }
4880 context.mv = TRUE;
4881 domove();
4882 return;
4883 } else if (prefix_seen && cmd[1] == Cmd.spkeys[NHKF_ESC]) {
4884 /* <prefix><escape> */
4885 /* don't report "unknown command" for change of heart... */
4886 bad_command = FALSE;
4887 } else if (*cmd == ' ' && !flags.rest_on_space) {
4888 bad_command = TRUE; /* skip cmdlist[] loop */
4889
4890 /* handle all other commands */
4891 } else {
4892 register const struct ext_func_tab *tlist;
4893 int res, NDECL((*func));
4894
4895 /* current - use *cmd to directly index cmdlist array */
4896 if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) {
4897 if (!wizard && (tlist->flags & WIZMODECMD)) {
4898 You_cant("do that!");
4899 res = 0;
4900 } else if (u.uburied && !(tlist->flags & IFBURIED)) {
4901 You_cant("do that while you are buried!");
4902 res = 0;
4903 } else {
4904 /* we discard 'const' because some compilers seem to have
4905 trouble with the pointer passed to set_occupation() */
4906 func = ((struct ext_func_tab *) tlist)->ef_funct;
4907 if (tlist->f_text && !occupation && multi)
4908 set_occupation(func, tlist->f_text, multi);
4909 res = (*func)(); /* perform the command */
4910 }
4911 if (!res) {
4912 context.move = FALSE;
4913 multi = 0;
4914 }
4915 return;
4916 }
4917 /* if we reach here, cmd wasn't found in cmdlist[] */
4918 bad_command = TRUE;
4919 }
4920
4921 if (bad_command) {
4922 char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */
4923 char c, c1 = cmd[1];
4924
4925 expcmd[0] = '\0';
4926 while ((c = *cmd++) != '\0')
4927 Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */
4928
4929 if (!prefix_seen || !help_dir(c1, spkey, "Invalid direction key!"))
4930 Norep("Unknown command '%s'.", expcmd);
4931 }
4932 /* didn't move */
4933 context.move = FALSE;
4934 multi = 0;
4935 return;
4936 }
4937
4938 /* convert an x,y pair into a direction code */
4939 int
xytod(x,y)4940 xytod(x, y)
4941 schar x, y;
4942 {
4943 register int dd;
4944
4945 for (dd = 0; dd < 8; dd++)
4946 if (x == xdir[dd] && y == ydir[dd])
4947 return dd;
4948 return -1;
4949 }
4950
4951 /* convert a direction code into an x,y pair */
4952 void
dtoxy(cc,dd)4953 dtoxy(cc, dd)
4954 coord *cc;
4955 register int dd;
4956 {
4957 cc->x = xdir[dd];
4958 cc->y = ydir[dd];
4959 return;
4960 }
4961
4962 /* also sets u.dz, but returns false for <> */
4963 int
movecmd(sym)4964 movecmd(sym)
4965 char sym;
4966 {
4967 register const char *dp = index(Cmd.dirchars, sym);
4968
4969 u.dz = 0;
4970 if (!dp || !*dp)
4971 return 0;
4972 u.dx = xdir[dp - Cmd.dirchars];
4973 u.dy = ydir[dp - Cmd.dirchars];
4974 u.dz = zdir[dp - Cmd.dirchars];
4975 #if 0 /* now handled elsewhere */
4976 if (u.dx && u.dy && NODIAG(u.umonnum)) {
4977 u.dx = u.dy = 0;
4978 return 0;
4979 }
4980 #endif
4981 return !u.dz;
4982 }
4983
4984 /* grid bug handling which used to be in movecmd() */
4985 int
dxdy_moveok()4986 dxdy_moveok()
4987 {
4988 if (u.dx && u.dy && NODIAG(u.umonnum))
4989 u.dx = u.dy = 0;
4990 return u.dx || u.dy;
4991 }
4992
4993 /* decide whether a character (user input keystroke) requests screen repaint */
4994 boolean
redraw_cmd(c)4995 redraw_cmd(c)
4996 char c;
4997 {
4998 return (boolean) (c == Cmd.spkeys[NHKF_REDRAW]
4999 || (Cmd.num_pad && c == Cmd.spkeys[NHKF_REDRAW2]));
5000 }
5001
5002 boolean
prefix_cmd(c)5003 prefix_cmd(c)
5004 char c;
5005 {
5006 return (c == Cmd.spkeys[NHKF_RUSH]
5007 || c == Cmd.spkeys[NHKF_RUN]
5008 || c == Cmd.spkeys[NHKF_NOPICKUP]
5009 || c == Cmd.spkeys[NHKF_RUN_NOPICKUP]
5010 || c == Cmd.spkeys[NHKF_FIGHT]
5011 || (Cmd.num_pad && (c == Cmd.spkeys[NHKF_RUN2]
5012 || c == Cmd.spkeys[NHKF_FIGHT2])));
5013 }
5014
5015 /*
5016 * uses getdir() but unlike getdir() it specifically
5017 * produces coordinates using the direction from getdir()
5018 * and verifies that those coordinates are ok.
5019 *
5020 * If the call to getdir() returns 0, Never_mind is displayed.
5021 * If the resulting coordinates are not okay, emsg is displayed.
5022 *
5023 * Returns non-zero if coordinates in cc are valid.
5024 */
5025 int
get_adjacent_loc(prompt,emsg,x,y,cc)5026 get_adjacent_loc(prompt, emsg, x, y, cc)
5027 const char *prompt, *emsg;
5028 xchar x, y;
5029 coord *cc;
5030 {
5031 xchar new_x, new_y;
5032 if (!getdir(prompt)) {
5033 pline1(Never_mind);
5034 return 0;
5035 }
5036 new_x = x + u.dx;
5037 new_y = y + u.dy;
5038 if (cc && isok(new_x, new_y)) {
5039 cc->x = new_x;
5040 cc->y = new_y;
5041 } else {
5042 if (emsg)
5043 pline1(emsg);
5044 return 0;
5045 }
5046 return 1;
5047 }
5048
5049 int
getdir(s)5050 getdir(s)
5051 const char *s;
5052 {
5053 char dirsym;
5054 int is_mov;
5055
5056 retry:
5057 if (in_doagain || *readchar_queue)
5058 dirsym = readchar();
5059 else
5060 dirsym = yn_function((s && *s != '^') ? s : "In what direction?",
5061 (char *) 0, '\0');
5062 /* remove the prompt string so caller won't have to */
5063 clear_nhwindow(WIN_MESSAGE);
5064
5065 if (redraw_cmd(dirsym)) { /* ^R */
5066 docrt(); /* redraw */
5067 goto retry;
5068 }
5069 savech(dirsym);
5070
5071 if (dirsym == Cmd.spkeys[NHKF_GETDIR_SELF]
5072 || dirsym == Cmd.spkeys[NHKF_GETDIR_SELF2]) {
5073 u.dx = u.dy = u.dz = 0;
5074 } else if (!(is_mov = movecmd(dirsym)) && !u.dz) {
5075 boolean did_help = FALSE, help_requested;
5076
5077 if (!index(quitchars, dirsym)) {
5078 help_requested = (dirsym == Cmd.spkeys[NHKF_GETDIR_HELP]);
5079 if (help_requested || iflags.cmdassist) {
5080 did_help = help_dir((s && *s == '^') ? dirsym : '\0',
5081 NHKF_ESC,
5082 help_requested ? (const char *) 0
5083 : "Invalid direction key!");
5084 if (help_requested)
5085 goto retry;
5086 }
5087 if (!did_help)
5088 pline("What a strange direction!");
5089 }
5090 return 0;
5091 } else if (is_mov && !dxdy_moveok()) {
5092 You_cant("orient yourself that direction.");
5093 return 0;
5094 }
5095 if (!u.dz && (Stunned || (Confusion && !rn2(5))))
5096 confdir();
5097 return 1;
5098 }
5099
5100 STATIC_OVL void
show_direction_keys(win,centerchar,nodiag)5101 show_direction_keys(win, centerchar, nodiag)
5102 winid win; /* should specify a window which is using a fixed-width font... */
5103 char centerchar; /* '.' or '@' or ' ' */
5104 boolean nodiag;
5105 {
5106 char buf[BUFSZ];
5107
5108 if (!centerchar)
5109 centerchar = ' ';
5110
5111 if (nodiag) {
5112 Sprintf(buf, " %c ", Cmd.move_N);
5113 putstr(win, 0, buf);
5114 putstr(win, 0, " | ");
5115 Sprintf(buf, " %c- %c -%c",
5116 Cmd.move_W, centerchar, Cmd.move_E);
5117 putstr(win, 0, buf);
5118 putstr(win, 0, " | ");
5119 Sprintf(buf, " %c ", Cmd.move_S);
5120 putstr(win, 0, buf);
5121 } else {
5122 Sprintf(buf, " %c %c %c",
5123 Cmd.move_NW, Cmd.move_N, Cmd.move_NE);
5124 putstr(win, 0, buf);
5125 putstr(win, 0, " \\ | / ");
5126 Sprintf(buf, " %c- %c -%c",
5127 Cmd.move_W, centerchar, Cmd.move_E);
5128 putstr(win, 0, buf);
5129 putstr(win, 0, " / | \\ ");
5130 Sprintf(buf, " %c %c %c",
5131 Cmd.move_SW, Cmd.move_S, Cmd.move_SE);
5132 putstr(win, 0, buf);
5133 };
5134 }
5135
5136 /* explain choices if player has asked for getdir() help or has given
5137 an invalid direction after a prefix key ('F', 'g', 'm', &c), which
5138 might be bogus but could be up, down, or self when not applicable */
5139 STATIC_OVL boolean
help_dir(sym,spkey,msg)5140 help_dir(sym, spkey, msg)
5141 char sym;
5142 int spkey; /* NHKF_ code for prefix key, if one was used, or for ESC */
5143 const char *msg;
5144 {
5145 static const char wiz_only_list[] = "EFGIVW";
5146 char ctrl;
5147 winid win;
5148 char buf[BUFSZ], buf2[BUFSZ], *explain;
5149 const char *dothat, *how;
5150 boolean prefixhandling, viawindow;
5151
5152 /* NHKF_ESC indicates that player asked for help at getdir prompt */
5153 viawindow = (spkey == NHKF_ESC || iflags.cmdassist);
5154 prefixhandling = (spkey != NHKF_ESC);
5155 /*
5156 * Handling for prefix keys that don't want special directions.
5157 * Delivered via pline if 'cmdassist' is off, or instead of the
5158 * general message if it's on.
5159 */
5160 dothat = "do that";
5161 how = " at"; /* for "<action> at yourself"; not used for up/down */
5162 switch (spkey) {
5163 case NHKF_NOPICKUP:
5164 dothat = "move";
5165 break;
5166 case NHKF_RUSH:
5167 dothat = "rush";
5168 break;
5169 case NHKF_RUN2:
5170 if (!Cmd.num_pad)
5171 break;
5172 /*FALLTHRU*/
5173 case NHKF_RUN:
5174 case NHKF_RUN_NOPICKUP:
5175 dothat = "run";
5176 break;
5177 case NHKF_FIGHT2:
5178 if (!Cmd.num_pad)
5179 break;
5180 /*FALLTHRU*/
5181 case NHKF_FIGHT:
5182 dothat = "fight";
5183 how = ""; /* avoid "fight at yourself" */
5184 break;
5185 default:
5186 prefixhandling = FALSE;
5187 break;
5188 }
5189
5190 buf[0] = '\0';
5191 /* for movement prefix followed by '.' or (numpad && 's') to mean 'self';
5192 note: '-' for hands (inventory form of 'self') is not handled here */
5193 if (prefixhandling
5194 && (sym == Cmd.spkeys[NHKF_GETDIR_SELF]
5195 || (Cmd.num_pad && sym == Cmd.spkeys[NHKF_GETDIR_SELF2]))) {
5196 Sprintf(buf, "You can't %s%s yourself.", dothat, how);
5197 /* for movement prefix followed by up or down */
5198 } else if (prefixhandling && (sym == '<' || sym == '>')) {
5199 Sprintf(buf, "You can't %s %s.", dothat,
5200 /* was "upwards" and "downwards", but they're considered
5201 to be variants of canonical "upward" and "downward" */
5202 (sym == '<') ? "upward" : "downward");
5203 }
5204
5205 /* if '!cmdassist', display via pline() and we're done (note: asking
5206 for help at getdir() prompt forces cmdassist for this operation) */
5207 if (!viawindow) {
5208 if (prefixhandling) {
5209 if (!*buf)
5210 Sprintf(buf, "Invalid direction for '%s' prefix.",
5211 visctrl(Cmd.spkeys[spkey]));
5212 pline("%s", buf);
5213 return TRUE;
5214 }
5215 /* when 'cmdassist' is off and caller doesn't insist, do nothing */
5216 return FALSE;
5217 }
5218
5219 win = create_nhwindow(NHW_TEXT);
5220 if (!win)
5221 return FALSE;
5222
5223 if (*buf) {
5224 /* show bad-prefix message instead of general invalid-direction one */
5225 putstr(win, 0, buf);
5226 putstr(win, 0, "");
5227 } else if (msg) {
5228 Sprintf(buf, "cmdassist: %s", msg);
5229 putstr(win, 0, buf);
5230 putstr(win, 0, "");
5231 }
5232
5233 if (!prefixhandling && (letter(sym) || sym == '[')) {
5234 /* '[': old 'cmdhelp' showed ESC as ^[ */
5235 sym = highc(sym); /* @A-Z[ (note: letter() accepts '@') */
5236 ctrl = (sym - 'A') + 1; /* 0-27 (note: 28-31 aren't applicable) */
5237 if ((explain = dowhatdoes_core(ctrl, buf2)) != 0
5238 && (!index(wiz_only_list, sym) || wizard)) {
5239 Sprintf(buf, "Are you trying to use ^%c%s?", sym,
5240 index(wiz_only_list, sym) ? ""
5241 : " as specified in the Guidebook");
5242 putstr(win, 0, buf);
5243 putstr(win, 0, "");
5244 putstr(win, 0, explain);
5245 putstr(win, 0, "");
5246 putstr(win, 0,
5247 "To use that command, hold down the <Ctrl> key as a shift");
5248 Sprintf(buf, "and press the <%c> key.", sym);
5249 putstr(win, 0, buf);
5250 putstr(win, 0, "");
5251 }
5252 }
5253
5254 Sprintf(buf, "Valid direction keys%s%s%s are:",
5255 prefixhandling ? " to " : "", prefixhandling ? dothat : "",
5256 NODIAG(u.umonnum) ? " in your current form" : "");
5257 putstr(win, 0, buf);
5258 show_direction_keys(win, !prefixhandling ? '.' : ' ', NODIAG(u.umonnum));
5259
5260 if (!prefixhandling || spkey == NHKF_NOPICKUP) {
5261 /* NOPICKUP: unlike the other prefix keys, 'm' allows up/down for
5262 stair traversal; we won't get here when "m<" or "m>" has been
5263 given but we include up and down for 'm'+invalid_direction;
5264 self is excluded as a viable direction for every prefix */
5265 putstr(win, 0, "");
5266 putstr(win, 0, " < up");
5267 putstr(win, 0, " > down");
5268 if (!prefixhandling) {
5269 int selfi = Cmd.num_pad ? NHKF_GETDIR_SELF2 : NHKF_GETDIR_SELF;
5270
5271 Sprintf(buf, " %4s direct at yourself",
5272 visctrl(Cmd.spkeys[selfi]));
5273 putstr(win, 0, buf);
5274 }
5275 }
5276
5277 if (msg) {
5278 /* non-null msg means that this wasn't an explicit user request */
5279 putstr(win, 0, "");
5280 putstr(win, 0,
5281 "(Suppress this message with !cmdassist in config file.)");
5282 }
5283 display_nhwindow(win, FALSE);
5284 destroy_nhwindow(win);
5285 return TRUE;
5286 }
5287
5288 void
confdir()5289 confdir()
5290 {
5291 register int x = NODIAG(u.umonnum) ? 2 * rn2(4) : rn2(8);
5292
5293 u.dx = xdir[x];
5294 u.dy = ydir[x];
5295 return;
5296 }
5297
5298 const char *
directionname(dir)5299 directionname(dir)
5300 int dir;
5301 {
5302 static NEARDATA const char *const dirnames[] = {
5303 "west", "northwest", "north", "northeast", "east",
5304 "southeast", "south", "southwest", "down", "up",
5305 };
5306
5307 if (dir < 0 || dir >= SIZE(dirnames))
5308 return "invalid";
5309 return dirnames[dir];
5310 }
5311
5312 int
isok(x,y)5313 isok(x, y)
5314 register int x, y;
5315 {
5316 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
5317 return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1;
5318 }
5319
5320 /* #herecmdmenu command */
5321 STATIC_PTR int
doherecmdmenu(VOID_ARGS)5322 doherecmdmenu(VOID_ARGS)
5323 {
5324 char ch = here_cmd_menu(TRUE);
5325
5326 return ch ? 1 : 0;
5327 }
5328
5329 /* #therecmdmenu command, a way to test there_cmd_menu without mouse */
5330 STATIC_PTR int
dotherecmdmenu(VOID_ARGS)5331 dotherecmdmenu(VOID_ARGS)
5332 {
5333 char ch;
5334
5335 if (!getdir((const char *) 0) || !isok(u.ux + u.dx, u.uy + u.dy))
5336 return 0;
5337
5338 if (u.dx || u.dy)
5339 ch = there_cmd_menu(TRUE, u.ux + u.dx, u.uy + u.dy);
5340 else
5341 ch = here_cmd_menu(TRUE);
5342
5343 return ch ? 1 : 0;
5344 }
5345
5346 STATIC_OVL void
add_herecmd_menuitem(win,func,text)5347 add_herecmd_menuitem(win, func, text)
5348 winid win;
5349 int NDECL((*func));
5350 const char *text;
5351 {
5352 char ch;
5353 anything any;
5354
5355 if ((ch = cmd_from_func(func)) != '\0') {
5356 any = zeroany;
5357 any.a_nfunc = func;
5358 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, text, MENU_UNSELECTED);
5359 }
5360 }
5361
5362 STATIC_OVL char
there_cmd_menu(doit,x,y)5363 there_cmd_menu(doit, x, y)
5364 boolean doit;
5365 int x, y;
5366 {
5367 winid win;
5368 char ch;
5369 char buf[BUFSZ];
5370 schar typ = levl[x][y].typ;
5371 int npick, K = 0;
5372 menu_item *picks = (menu_item *) 0;
5373 struct trap *ttmp;
5374 struct monst *mtmp;
5375
5376 win = create_nhwindow(NHW_MENU);
5377 start_menu(win);
5378
5379 if (IS_DOOR(typ)) {
5380 boolean key_or_pick, card;
5381 int dm = levl[x][y].doormask;
5382
5383 if ((dm & (D_CLOSED | D_LOCKED))) {
5384 add_herecmd_menuitem(win, doopen, "Open the door"), ++K;
5385 /* unfortunately there's no lknown flag for doors to
5386 remember the locked/unlocked state */
5387 key_or_pick = (carrying(SKELETON_KEY) || carrying(LOCK_PICK));
5388 card = (carrying(CREDIT_CARD) != 0);
5389 if (key_or_pick || card) {
5390 Sprintf(buf, "%sunlock the door",
5391 key_or_pick ? "lock or " : "");
5392 add_herecmd_menuitem(win, doapply, upstart(buf)), ++K;
5393 }
5394 /* unfortunately there's no tknown flag for doors (or chests)
5395 to remember whether a trap had been found */
5396 add_herecmd_menuitem(win, dountrap,
5397 "Search the door for a trap"), ++K;
5398 /* [what about #force?] */
5399 add_herecmd_menuitem(win, dokick, "Kick the door"), ++K;
5400 } else if ((dm & D_ISOPEN)) {
5401 add_herecmd_menuitem(win, doclose, "Close the door"), ++K;
5402 }
5403 }
5404
5405 if (typ <= SCORR)
5406 add_herecmd_menuitem(win, dosearch, "Search for secret doors"), ++K;
5407
5408 if ((ttmp = t_at(x, y)) != 0 && ttmp->tseen) {
5409 add_herecmd_menuitem(win, doidtrap, "Examine trap"), ++K;
5410 if (ttmp->ttyp != VIBRATING_SQUARE)
5411 add_herecmd_menuitem(win, dountrap, "Attempt to disarm trap"), ++K;
5412 }
5413
5414 mtmp = m_at(x, y);
5415 if (mtmp && !canspotmon(mtmp))
5416 mtmp = 0;
5417 if (mtmp && which_armor(mtmp, W_SADDLE)) {
5418 char *mnam = x_monnam(mtmp, ARTICLE_THE, (char *) 0,
5419 SUPPRESS_SADDLE, FALSE);
5420
5421 if (!u.usteed) {
5422 Sprintf(buf, "Ride %s", mnam);
5423 add_herecmd_menuitem(win, doride, buf), ++K;
5424 }
5425 Sprintf(buf, "Remove saddle from %s", mnam);
5426 add_herecmd_menuitem(win, doloot, buf), ++K;
5427 }
5428 if (mtmp && can_saddle(mtmp) && !which_armor(mtmp, W_SADDLE)
5429 && carrying(SADDLE)) {
5430 Sprintf(buf, "Put saddle on %s", mon_nam(mtmp)), ++K;
5431 add_herecmd_menuitem(win, doapply, buf);
5432 }
5433 #if 0
5434 if (mtmp || glyph_is_invisible(glyph_at(x, y))) {
5435 /* "Attack %s", mtmp ? mon_nam(mtmp) : "unseen creature" */
5436 } else {
5437 /* "Move %s", direction */
5438 }
5439 #endif
5440
5441 if (K) {
5442 end_menu(win, "What do you want to do?");
5443 npick = select_menu(win, PICK_ONE, &picks);
5444 } else {
5445 pline("No applicable actions.");
5446 npick = 0;
5447 }
5448 destroy_nhwindow(win);
5449 ch = '\0';
5450 if (npick > 0) {
5451 int NDECL((*func)) = picks->item.a_nfunc;
5452 free((genericptr_t) picks);
5453
5454 if (doit) {
5455 int ret = (*func)();
5456
5457 ch = (char) ret;
5458 } else {
5459 ch = cmd_from_func(func);
5460 }
5461 }
5462 return ch;
5463 }
5464
5465 STATIC_OVL char
here_cmd_menu(doit)5466 here_cmd_menu(doit)
5467 boolean doit;
5468 {
5469 winid win;
5470 char ch;
5471 char buf[BUFSZ];
5472 schar typ = levl[u.ux][u.uy].typ;
5473 int npick;
5474 menu_item *picks = (menu_item *) 0;
5475
5476 win = create_nhwindow(NHW_MENU);
5477 start_menu(win);
5478
5479 if (IS_FOUNTAIN(typ) || IS_SINK(typ)) {
5480 Sprintf(buf, "Drink from the %s",
5481 defsyms[IS_FOUNTAIN(typ) ? S_fountain : S_sink].explanation);
5482 add_herecmd_menuitem(win, dodrink, buf);
5483 }
5484 if (IS_FOUNTAIN(typ))
5485 add_herecmd_menuitem(win, dodip,
5486 "Dip something into the fountain");
5487 if (IS_THRONE(typ))
5488 add_herecmd_menuitem(win, dosit,
5489 "Sit on the throne");
5490
5491 if ((u.ux == xupstair && u.uy == yupstair)
5492 || (u.ux == sstairs.sx && u.uy == sstairs.sy && sstairs.up)
5493 || (u.ux == xupladder && u.uy == yupladder)) {
5494 Sprintf(buf, "Go up the %s",
5495 (u.ux == xupladder && u.uy == yupladder)
5496 ? "ladder" : "stairs");
5497 add_herecmd_menuitem(win, doup, buf);
5498 }
5499 if ((u.ux == xdnstair && u.uy == ydnstair)
5500 || (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up)
5501 || (u.ux == xdnladder && u.uy == ydnladder)) {
5502 Sprintf(buf, "Go down the %s",
5503 (u.ux == xupladder && u.uy == yupladder)
5504 ? "ladder" : "stairs");
5505 add_herecmd_menuitem(win, dodown, buf);
5506 }
5507 if (u.usteed) { /* another movement choice */
5508 Sprintf(buf, "Dismount %s",
5509 x_monnam(u.usteed, ARTICLE_THE, (char *) 0,
5510 SUPPRESS_SADDLE, FALSE));
5511 add_herecmd_menuitem(win, doride, buf);
5512 }
5513
5514 #if 0
5515 if (Upolyd) { /* before objects */
5516 Sprintf(buf, "Use %s special ability",
5517 s_suffix(mons[u.umonnum].mname));
5518 add_herecmd_menuitem(win, domonability, buf);
5519 }
5520 #endif
5521
5522 if (OBJ_AT(u.ux, u.uy)) {
5523 struct obj *otmp = level.objects[u.ux][u.uy];
5524
5525 Sprintf(buf, "Pick up %s", otmp->nexthere ? "items" : doname(otmp));
5526 add_herecmd_menuitem(win, dopickup, buf);
5527
5528 if (Is_container(otmp)) {
5529 Sprintf(buf, "Loot %s", doname(otmp));
5530 add_herecmd_menuitem(win, doloot, buf);
5531 }
5532 if (otmp->oclass == FOOD_CLASS) {
5533 Sprintf(buf, "Eat %s", doname(otmp));
5534 add_herecmd_menuitem(win, doeat, buf);
5535 }
5536 }
5537
5538 if (invent)
5539 add_herecmd_menuitem(win, dodrop, "Drop items");
5540
5541 add_herecmd_menuitem(win, donull, "Rest one turn");
5542 add_herecmd_menuitem(win, dosearch, "Search around you");
5543 add_herecmd_menuitem(win, dolook, "Look at what is here");
5544
5545 end_menu(win, "What do you want to do?");
5546 npick = select_menu(win, PICK_ONE, &picks);
5547 destroy_nhwindow(win);
5548 ch = '\0';
5549 if (npick > 0) {
5550 int NDECL((*func)) = picks->item.a_nfunc;
5551 free((genericptr_t) picks);
5552
5553 if (doit) {
5554 int ret = (*func)();
5555
5556 ch = (char) ret;
5557 } else {
5558 ch = cmd_from_func(func);
5559 }
5560 }
5561 return ch;
5562 }
5563
5564
5565 static NEARDATA int last_multi;
5566
5567 /*
5568 * convert a MAP window position into a movecmd
5569 */
5570 const char *
click_to_cmd(x,y,mod)5571 click_to_cmd(x, y, mod)
5572 int x, y, mod;
5573 {
5574 int dir;
5575 static char cmd[4];
5576 cmd[1] = 0;
5577
5578 if (iflags.clicklook && mod == CLICK_2) {
5579 clicklook_cc.x = x;
5580 clicklook_cc.y = y;
5581 cmd[0] = Cmd.spkeys[NHKF_CLICKLOOK];
5582 return cmd;
5583 }
5584
5585 x -= u.ux;
5586 y -= u.uy;
5587
5588 if (flags.travelcmd) {
5589 if (abs(x) <= 1 && abs(y) <= 1) {
5590 x = sgn(x), y = sgn(y);
5591 } else {
5592 u.tx = u.ux + x;
5593 u.ty = u.uy + y;
5594 cmd[0] = Cmd.spkeys[NHKF_TRAVEL];
5595 return cmd;
5596 }
5597
5598 if (x == 0 && y == 0) {
5599 if (iflags.herecmd_menu) {
5600 cmd[0] = here_cmd_menu(FALSE);
5601 return cmd;
5602 }
5603
5604 /* here */
5605 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)
5606 || IS_SINK(levl[u.ux][u.uy].typ)) {
5607 cmd[0] = cmd_from_func(mod == CLICK_1 ? dodrink : dodip);
5608 return cmd;
5609 } else if (IS_THRONE(levl[u.ux][u.uy].typ)) {
5610 cmd[0] = cmd_from_func(dosit);
5611 return cmd;
5612 } else if ((u.ux == xupstair && u.uy == yupstair)
5613 || (u.ux == sstairs.sx && u.uy == sstairs.sy
5614 && sstairs.up)
5615 || (u.ux == xupladder && u.uy == yupladder)) {
5616 cmd[0] = cmd_from_func(doup);
5617 return cmd;
5618 } else if ((u.ux == xdnstair && u.uy == ydnstair)
5619 || (u.ux == sstairs.sx && u.uy == sstairs.sy
5620 && !sstairs.up)
5621 || (u.ux == xdnladder && u.uy == ydnladder)) {
5622 cmd[0] = cmd_from_func(dodown);
5623 return cmd;
5624 } else if (OBJ_AT(u.ux, u.uy)) {
5625 cmd[0] = cmd_from_func(Is_container(level.objects[u.ux][u.uy])
5626 ? doloot : dopickup);
5627 return cmd;
5628 } else {
5629 cmd[0] = cmd_from_func(donull); /* just rest */
5630 return cmd;
5631 }
5632 }
5633
5634 /* directional commands */
5635
5636 dir = xytod(x, y);
5637
5638 if (!m_at(u.ux + x, u.uy + y)
5639 && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
5640 cmd[1] = Cmd.dirchars[dir];
5641 cmd[2] = '\0';
5642 if (iflags.herecmd_menu) {
5643 cmd[0] = there_cmd_menu(FALSE, u.ux + x, u.uy + y);
5644 if (cmd[0] == '\0')
5645 cmd[1] = '\0';
5646 return cmd;
5647 }
5648
5649 if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
5650 /* slight assistance to the player: choose kick/open for them
5651 */
5652 if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) {
5653 cmd[0] = cmd_from_func(dokick);
5654 return cmd;
5655 }
5656 if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) {
5657 cmd[0] = cmd_from_func(doopen);
5658 return cmd;
5659 }
5660 }
5661 if (levl[u.ux + x][u.uy + y].typ <= SCORR) {
5662 cmd[0] = cmd_from_func(dosearch);
5663 cmd[1] = 0;
5664 return cmd;
5665 }
5666 }
5667 } else {
5668 /* convert without using floating point, allowing sloppy clicking */
5669 if (x > 2 * abs(y))
5670 x = 1, y = 0;
5671 else if (y > 2 * abs(x))
5672 x = 0, y = 1;
5673 else if (x < -2 * abs(y))
5674 x = -1, y = 0;
5675 else if (y < -2 * abs(x))
5676 x = 0, y = -1;
5677 else
5678 x = sgn(x), y = sgn(y);
5679
5680 if (x == 0 && y == 0) {
5681 /* map click on player to "rest" command */
5682 cmd[0] = cmd_from_func(donull);
5683 return cmd;
5684 }
5685 dir = xytod(x, y);
5686 }
5687
5688 /* move, attack, etc. */
5689 cmd[1] = 0;
5690 if (mod == CLICK_1) {
5691 cmd[0] = Cmd.dirchars[dir];
5692 } else {
5693 cmd[0] = (Cmd.num_pad
5694 ? M(Cmd.dirchars[dir])
5695 : (Cmd.dirchars[dir] - 'a' + 'A')); /* run command */
5696 }
5697
5698 return cmd;
5699 }
5700
5701 char
get_count(allowchars,inkey,maxcount,count,historical)5702 get_count(allowchars, inkey, maxcount, count, historical)
5703 char *allowchars;
5704 char inkey;
5705 long maxcount;
5706 long *count;
5707 boolean historical; /* whether to include in message history: True => yes */
5708 {
5709 char qbuf[QBUFSZ];
5710 int key;
5711 long cnt = 0L;
5712 boolean backspaced = FALSE;
5713 /* this should be done in port code so that we have erase_char
5714 and kill_char available; we can at least fake erase_char */
5715 #define STANDBY_erase_char '\177'
5716
5717 for (;;) {
5718 if (inkey) {
5719 key = inkey;
5720 inkey = '\0';
5721 } else
5722 key = readchar();
5723
5724 if (digit(key)) {
5725 cnt = 10L * cnt + (long) (key - '0');
5726 if (cnt < 0)
5727 cnt = 0;
5728 else if (maxcount > 0 && cnt > maxcount)
5729 cnt = maxcount;
5730 } else if (cnt && (key == '\b' || key == STANDBY_erase_char)) {
5731 cnt = cnt / 10;
5732 backspaced = TRUE;
5733 } else if (key == Cmd.spkeys[NHKF_ESC]) {
5734 break;
5735 } else if (!allowchars || index(allowchars, key)) {
5736 *count = cnt;
5737 break;
5738 }
5739
5740 if (cnt > 9 || backspaced) {
5741 clear_nhwindow(WIN_MESSAGE);
5742 if (backspaced && !cnt) {
5743 Sprintf(qbuf, "Count: ");
5744 } else {
5745 Sprintf(qbuf, "Count: %ld", cnt);
5746 backspaced = FALSE;
5747 }
5748 custompline(SUPPRESS_HISTORY, "%s", qbuf);
5749 mark_synch();
5750 }
5751 }
5752
5753 if (historical) {
5754 Sprintf(qbuf, "Count: %ld ", *count);
5755 (void) key2txt((uchar) key, eos(qbuf));
5756 putmsghistory(qbuf, FALSE);
5757 }
5758
5759 return key;
5760 }
5761
5762
5763 STATIC_OVL char *
parse()5764 parse()
5765 {
5766 #ifdef LINT /* static char in_line[COLNO]; */
5767 char in_line[COLNO];
5768 #else
5769 static char in_line[COLNO];
5770 #endif
5771 register int foo;
5772
5773 iflags.in_parse = TRUE;
5774 multi = 0;
5775 context.move = 1;
5776 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
5777
5778 #ifdef ALTMETA
5779 alt_esc = iflags.altmeta; /* readchar() hack */
5780 #endif
5781 if (!Cmd.num_pad || (foo = readchar()) == Cmd.spkeys[NHKF_COUNT]) {
5782 long tmpmulti = multi;
5783
5784 foo = get_count((char *) 0, '\0', LARGEST_INT, &tmpmulti, FALSE);
5785 last_multi = multi = tmpmulti;
5786 }
5787 #ifdef ALTMETA
5788 alt_esc = FALSE; /* readchar() reset */
5789 #endif
5790
5791 if (iflags.debug_fuzzer /* if fuzzing, override '!' and ^Z */
5792 && (Cmd.commands[foo & 0x0ff]
5793 && (Cmd.commands[foo & 0x0ff]->ef_funct == dosuspend_core
5794 || Cmd.commands[foo & 0x0ff]->ef_funct == dosh_core)))
5795 foo = Cmd.spkeys[NHKF_ESC];
5796
5797 if (foo == Cmd.spkeys[NHKF_ESC]) { /* esc cancels count (TH) */
5798 clear_nhwindow(WIN_MESSAGE);
5799 multi = last_multi = 0;
5800 } else if (foo == Cmd.spkeys[NHKF_DOAGAIN] || in_doagain) {
5801 multi = last_multi;
5802 } else {
5803 last_multi = multi;
5804 savech(0); /* reset input queue */
5805 savech((char) foo);
5806 }
5807
5808 if (multi) {
5809 multi--;
5810 save_cm = in_line;
5811 } else {
5812 save_cm = (char *) 0;
5813 }
5814 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
5815 if (Cmd.pcHack_compat) {
5816 /* This handles very old inconsistent DOS/Windows behaviour
5817 in a different way: earlier, the keyboard handler mapped
5818 these, which caused counts to be strange when entered
5819 from the number pad. Now do not map them until here. */
5820 switch (foo) {
5821 case '5':
5822 foo = Cmd.spkeys[NHKF_RUSH];
5823 break;
5824 case M('5'):
5825 foo = Cmd.spkeys[NHKF_RUN];
5826 break;
5827 case M('0'):
5828 foo = Cmd.spkeys[NHKF_DOINV];
5829 break;
5830 default:
5831 break; /* as is */
5832 }
5833 }
5834
5835 in_line[0] = foo;
5836 in_line[1] = '\0';
5837 if (prefix_cmd(foo)) {
5838 foo = readchar();
5839 savech((char) foo);
5840 in_line[1] = foo;
5841 in_line[2] = 0;
5842 }
5843 clear_nhwindow(WIN_MESSAGE);
5844
5845 iflags.in_parse = FALSE;
5846 return in_line;
5847 }
5848
5849 #ifdef HANGUPHANDLING
5850 /* some very old systems, or descendents of such systems, expect signal
5851 handlers to have return type `int', but they don't actually inspect
5852 the return value so we should be safe using `void' unconditionally */
5853 /*ARGUSED*/
5854 void
hangup(sig_unused)5855 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
5856 int sig_unused UNUSED;
5857 {
5858 if (program_state.exiting)
5859 program_state.in_moveloop = 0;
5860 nhwindows_hangup();
5861 #ifdef SAFERHANGUP
5862 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
5863 and a couple of other places; actual hangup handling occurs then.
5864 This is 'safer' because it disallows certain cheats and also
5865 protects against losing objects in the process of being thrown,
5866 but also potentially riskier because the disconnected program
5867 must continue running longer before attempting a hangup save. */
5868 program_state.done_hup++;
5869 /* defer hangup iff game appears to be in progress */
5870 if (program_state.in_moveloop && program_state.something_worth_saving)
5871 return;
5872 #endif /* SAFERHANGUP */
5873 end_of_input();
5874 }
5875
5876 void
end_of_input()5877 end_of_input()
5878 {
5879 #ifdef NOSAVEONHANGUP
5880 #ifdef INSURANCE
5881 if (flags.ins_chkpt && program_state.something_worth_saving)
5882 program_state.preserve_locks = 1; /* keep files for recovery */
5883 #endif
5884 program_state.something_worth_saving = 0; /* don't save */
5885 #endif
5886
5887 #ifndef SAFERHANGUP
5888 if (!program_state.done_hup++)
5889 #endif
5890 if (program_state.something_worth_saving)
5891 (void) dosave0();
5892 if (iflags.window_inited)
5893 exit_nhwindows((char *) 0);
5894 clearlocks();
5895 nh_terminate(EXIT_SUCCESS);
5896 /*NOTREACHED*/ /* not necessarily true for vms... */
5897 return;
5898 }
5899 #endif /* HANGUPHANDLING */
5900
5901 char
readchar()5902 readchar()
5903 {
5904 register int sym;
5905 int x = u.ux, y = u.uy, mod = 0;
5906
5907 if (iflags.debug_fuzzer)
5908 return randomkey();
5909 if (*readchar_queue)
5910 sym = *readchar_queue++;
5911 else
5912 sym = in_doagain ? pgetchar() : nh_poskey(&x, &y, &mod);
5913
5914 #ifdef NR_OF_EOFS
5915 if (sym == EOF) {
5916 register int cnt = NR_OF_EOFS;
5917 /*
5918 * Some SYSV systems seem to return EOFs for various reasons
5919 * (?like when one hits break or for interrupted systemcalls?),
5920 * and we must see several before we quit.
5921 */
5922 do {
5923 clearerr(stdin); /* omit if clearerr is undefined */
5924 sym = pgetchar();
5925 } while (--cnt && sym == EOF);
5926 }
5927 #endif /* NR_OF_EOFS */
5928
5929 if (sym == EOF) {
5930 #ifdef HANGUPHANDLING
5931 hangup(0); /* call end_of_input() or set program_state.done_hup */
5932 #endif
5933 sym = '\033';
5934 #ifdef ALTMETA
5935 } else if (sym == '\033' && alt_esc) {
5936 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
5937 sym = *readchar_queue ? *readchar_queue++ : pgetchar();
5938 if (sym == EOF || sym == 0)
5939 sym = '\033';
5940 else if (sym != '\033')
5941 sym |= 0200; /* force 8th bit on */
5942 #endif /*ALTMETA*/
5943 } else if (sym == 0) {
5944 /* click event */
5945 readchar_queue = click_to_cmd(x, y, mod);
5946 sym = *readchar_queue++;
5947 }
5948 return (char) sym;
5949 }
5950
5951 /* '_' command, #travel, via keyboard rather than mouse click */
5952 STATIC_PTR int
dotravel(VOID_ARGS)5953 dotravel(VOID_ARGS)
5954 {
5955 static char cmd[2];
5956 coord cc;
5957
5958 /* [FIXME? Supporting the ability to disable traveling via mouse
5959 click makes some sense, depending upon overall mouse usage.
5960 Disabling '_' on a user by user basis makes no sense at all since
5961 even if it is typed by accident, aborting when picking a target
5962 destination is trivial. Travel via mouse predates travel via '_',
5963 and this use of OPTION=!travel is probably just a mistake....] */
5964 if (!flags.travelcmd)
5965 return 0;
5966
5967 cmd[1] = 0;
5968 cc.x = iflags.travelcc.x;
5969 cc.y = iflags.travelcc.y;
5970 if (cc.x == 0 && cc.y == 0) {
5971 /* No cached destination, start attempt from current position */
5972 cc.x = u.ux;
5973 cc.y = u.uy;
5974 }
5975 iflags.getloc_travelmode = TRUE;
5976 if (iflags.menu_requested) {
5977 int gf = iflags.getloc_filter;
5978 iflags.getloc_filter = GFILTER_VIEW;
5979 if (!getpos_menu(&cc, GLOC_INTERESTING)) {
5980 iflags.getloc_filter = gf;
5981 iflags.getloc_travelmode = FALSE;
5982 return 0;
5983 }
5984 iflags.getloc_filter = gf;
5985 } else {
5986 pline("Where do you want to travel to?");
5987 if (getpos(&cc, TRUE, "the desired destination") < 0) {
5988 /* user pressed ESC */
5989 iflags.getloc_travelmode = FALSE;
5990 return 0;
5991 }
5992 }
5993 iflags.getloc_travelmode = FALSE;
5994 iflags.travelcc.x = u.tx = cc.x;
5995 iflags.travelcc.y = u.ty = cc.y;
5996 cmd[0] = Cmd.spkeys[NHKF_TRAVEL];
5997 readchar_queue = cmd;
5998 return 0;
5999 }
6000
6001 /*
6002 * Parameter validator for generic yes/no function to prevent
6003 * the core from sending too long a prompt string to the
6004 * window port causing a buffer overflow there.
6005 */
6006 char
yn_function(query,resp,def)6007 yn_function(query, resp, def)
6008 const char *query, *resp;
6009 char def;
6010 {
6011 char res, qbuf[QBUFSZ];
6012 #ifdef DUMPLOG
6013 extern unsigned saved_pline_index; /* pline.c */
6014 unsigned idx = saved_pline_index;
6015 /* buffer to hold query+space+formatted_single_char_response */
6016 char dumplog_buf[QBUFSZ + 1 + 15]; /* [QBUFSZ+1+7] should suffice */
6017 #endif
6018
6019 iflags.last_msg = PLNMSG_UNKNOWN; /* most recent pline is clobbered */
6020
6021 /* maximum acceptable length is QBUFSZ-1 */
6022 if (strlen(query) >= QBUFSZ) {
6023 /* caller shouldn't have passed anything this long */
6024 paniclog("Query truncated: ", query);
6025 (void) strncpy(qbuf, query, QBUFSZ - 1 - 3);
6026 Strcpy(&qbuf[QBUFSZ - 1 - 3], "...");
6027 query = qbuf;
6028 }
6029 res = (*windowprocs.win_yn_function)(query, resp, def);
6030 #ifdef DUMPLOG
6031 if (idx == saved_pline_index) {
6032 /* when idx is still the same as saved_pline_index, the interface
6033 didn't put the prompt into saved_plines[]; we put a simplified
6034 version in there now (without response choices or default) */
6035 Sprintf(dumplog_buf, "%s ", query);
6036 (void) key2txt((uchar) res, eos(dumplog_buf));
6037 dumplogmsg(dumplog_buf);
6038 }
6039 #endif
6040 return res;
6041 }
6042
6043 /* for paranoid_confirm:quit,die,attack prompting */
6044 boolean
paranoid_query(be_paranoid,prompt)6045 paranoid_query(be_paranoid, prompt)
6046 boolean be_paranoid;
6047 const char *prompt;
6048 {
6049 boolean confirmed_ok;
6050
6051 /* when paranoid, player must respond with "yes" rather than just 'y'
6052 to give the go-ahead for this query; default is "no" unless the
6053 ParanoidConfirm flag is set in which case there's no default */
6054 if (be_paranoid) {
6055 char pbuf[BUFSZ], qbuf[QBUFSZ], ans[BUFSZ];
6056 const char *promptprefix = "",
6057 *responsetype = ParanoidConfirm ? "(yes|no)" : "(yes) [no]";
6058 int k, trylimit = 6; /* 1 normal, 5 more with "Yes or No:" prefix */
6059
6060 copynchars(pbuf, prompt, BUFSZ - 1);
6061 /* in addition to being paranoid about this particular
6062 query, we might be even more paranoid about all paranoia
6063 responses (ie, ParanoidConfirm is set) in which case we
6064 require "no" to reject in addition to "yes" to confirm
6065 (except we won't loop if response is ESC; it means no) */
6066 do {
6067 /* make sure we won't overflow a QBUFSZ sized buffer */
6068 k = (int) (strlen(promptprefix) + 1 + strlen(responsetype));
6069 if ((int) strlen(pbuf) + k > QBUFSZ - 1) {
6070 /* chop off some at the end */
6071 Strcpy(pbuf + (QBUFSZ - 1) - k - 4, "...?"); /* -4: "...?" */
6072 }
6073
6074 Sprintf(qbuf, "%s%s %s", promptprefix, pbuf, responsetype);
6075 *ans = '\0';
6076 getlin(qbuf, ans);
6077 (void) mungspaces(ans);
6078 confirmed_ok = !strcmpi(ans, "yes");
6079 if (confirmed_ok || *ans == '\033')
6080 break;
6081 promptprefix = "\"Yes\" or \"No\": ";
6082 } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit);
6083 } else
6084 confirmed_ok = (yn(prompt) == 'y');
6085
6086 return confirmed_ok;
6087 }
6088
6089 /* ^Z command, #suspend */
6090 STATIC_PTR int
dosuspend_core(VOID_ARGS)6091 dosuspend_core(VOID_ARGS)
6092 {
6093 #ifdef SUSPEND
6094 /* Does current window system support suspend? */
6095 if ((*windowprocs.win_can_suspend)()) {
6096 /* NB: SYSCF SHELLERS handled in port code. */
6097 dosuspend();
6098 } else
6099 #endif
6100 Norep(cmdnotavail, "#suspend");
6101 return 0;
6102 }
6103
6104 /* '!' command, #shell */
6105 STATIC_PTR int
dosh_core(VOID_ARGS)6106 dosh_core(VOID_ARGS)
6107 {
6108 #ifdef SHELL
6109 /* access restrictions, if any, are handled in port code */
6110 dosh();
6111 #else
6112 Norep(cmdnotavail, "#shell");
6113 #endif
6114 return 0;
6115 }
6116
6117 /*cmd.c*/
6118