1 /* NetHack 3.6 cmd.c $NHDT-Date: 1457207033 2016/03/05 19:43:53 $ $NHDT-Branch: chasonr $:$NHDT-Revision: 1.220 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
9 STATIC_VAR boolean alt_esc
= FALSE
;
12 struct cmd Cmd
= { 0 }; /* flag.h */
14 extern const char *hu_stat
[]; /* hunger status from eat.c */
15 extern const char *enc_stat
[]; /* encumbrance status from botl.c */
19 * Some systems may have getchar() return EOF for various reasons, and
20 * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
22 #if defined(SYSV) || defined(DGUX) || defined(HPUX)
27 #define CMD_TRAVEL (char) 0x90
28 #define CMD_CLICKLOOK (char) 0x8F
31 extern int NDECL(wiz_debug_cmd_bury
);
32 extern int NDECL(wiz_debug_cmd_traveldisplay
);
35 #ifdef DUMB /* stuff commented out in extern.h, but needed here */
36 extern int NDECL(doapply
); /**/
37 extern int NDECL(dorub
); /**/
38 extern int NDECL(dojump
); /**/
39 extern int NDECL(doextlist
); /**/
40 extern int NDECL(enter_explore_mode
); /**/
41 extern int NDECL(dodrop
); /**/
42 extern int NDECL(doddrop
); /**/
43 extern int NDECL(dodown
); /**/
44 extern int NDECL(doup
); /**/
45 extern int NDECL(donull
); /**/
46 extern int NDECL(dowipe
); /**/
47 extern int NDECL(docallcnd
); /**/
48 extern int NDECL(dotakeoff
); /**/
49 extern int NDECL(doremring
); /**/
50 extern int NDECL(dowear
); /**/
51 extern int NDECL(doputon
); /**/
52 extern int NDECL(doddoremarm
); /**/
53 extern int NDECL(dokick
); /**/
54 extern int NDECL(dofire
); /**/
55 extern int NDECL(dothrow
); /**/
56 extern int NDECL(doeat
); /**/
57 extern int NDECL(done2
); /**/
58 extern int NDECL(vanquished
); /**/
59 extern int NDECL(doengrave
); /**/
60 extern int NDECL(dopickup
); /**/
61 extern int NDECL(ddoinv
); /**/
62 extern int NDECL(dotypeinv
); /**/
63 extern int NDECL(dolook
); /**/
64 extern int NDECL(doprgold
); /**/
65 extern int NDECL(doprwep
); /**/
66 extern int NDECL(doprarm
); /**/
67 extern int NDECL(doprring
); /**/
68 extern int NDECL(dopramulet
); /**/
69 extern int NDECL(doprtool
); /**/
70 extern int NDECL(dosuspend
); /**/
71 extern int NDECL(doforce
); /**/
72 extern int NDECL(doopen
); /**/
73 extern int NDECL(doclose
); /**/
74 extern int NDECL(dosh
); /**/
75 extern int NDECL(dodiscovered
); /**/
76 extern int NDECL(doclassdisco
); /**/
77 extern int NDECL(doset
); /**/
78 extern int NDECL(dotogglepickup
); /**/
79 extern int NDECL(dowhatis
); /**/
80 extern int NDECL(doquickwhatis
); /**/
81 extern int NDECL(dowhatdoes
); /**/
82 extern int NDECL(dohelp
); /**/
83 extern int NDECL(dohistory
); /**/
84 extern int NDECL(doloot
); /**/
85 extern int NDECL(dodrink
); /**/
86 extern int NDECL(dodip
); /**/
87 extern int NDECL(dosacrifice
); /**/
88 extern int NDECL(dopray
); /**/
89 extern int NDECL(dotip
); /**/
90 extern int NDECL(doturn
); /**/
91 extern int NDECL(doredraw
); /**/
92 extern int NDECL(doread
); /**/
93 extern int NDECL(dosave
); /**/
94 extern int NDECL(dosearch
); /**/
95 extern int NDECL(doidtrap
); /**/
96 extern int NDECL(dopay
); /**/
97 extern int NDECL(dosit
); /**/
98 extern int NDECL(dotalk
); /**/
99 extern int NDECL(docast
); /**/
100 extern int NDECL(dovspell
); /**/
101 extern int NDECL(dotele
); /**/
102 extern int NDECL(dountrap
); /**/
103 extern int NDECL(doversion
); /**/
104 extern int NDECL(doextversion
); /**/
105 extern int NDECL(doswapweapon
); /**/
106 extern int NDECL(dowield
); /**/
107 extern int NDECL(dowieldquiver
); /**/
108 extern int NDECL(dozap
); /**/
109 extern int NDECL(doorganize
); /**/
112 static int NDECL(dosuspend_core
); /**/
114 static int NDECL((*timed_occ_fn
));
116 STATIC_PTR
int NDECL(doprev_message
);
117 STATIC_PTR
int NDECL(timed_occupation
);
118 STATIC_PTR
int NDECL(doextcmd
);
119 STATIC_PTR
int NDECL(domonability
);
120 STATIC_PTR
int NDECL(dooverview_or_wiz_where
);
121 STATIC_PTR
int NDECL(dotravel
);
122 STATIC_PTR
int NDECL(doterrain
);
123 STATIC_PTR
int NDECL(wiz_wish
);
124 STATIC_PTR
int NDECL(wiz_identify
);
125 STATIC_PTR
int NDECL(wiz_intrinsic
);
126 STATIC_PTR
int NDECL(wiz_map
);
127 STATIC_PTR
int NDECL(wiz_genesis
);
128 STATIC_PTR
int NDECL(wiz_where
);
129 STATIC_PTR
int NDECL(wiz_detect
);
130 STATIC_PTR
int NDECL(wiz_panic
);
131 STATIC_PTR
int NDECL(wiz_polyself
);
132 STATIC_PTR
int NDECL(wiz_level_tele
);
133 STATIC_PTR
int NDECL(wiz_level_change
);
134 STATIC_PTR
int NDECL(wiz_show_seenv
);
135 STATIC_PTR
int NDECL(wiz_show_vision
);
136 STATIC_PTR
int NDECL(wiz_smell
);
137 STATIC_PTR
int NDECL(wiz_mon_polycontrol
);
138 STATIC_PTR
int NDECL(wiz_show_wmodes
);
139 STATIC_DCL
void NDECL(wiz_map_levltyp
);
140 STATIC_DCL
void NDECL(wiz_levltyp_legend
);
141 #if defined(__BORLANDC__) && !defined(_WIN32)
142 extern void FDECL(show_borlandc_stats
, (winid
));
144 #ifdef DEBUG_MIGRATING_MONS
145 STATIC_PTR
int NDECL(wiz_migrate_mons
);
147 STATIC_DCL
int FDECL(size_monst
, (struct monst
*));
148 STATIC_DCL
int FDECL(size_obj
, (struct obj
*));
149 STATIC_DCL
void FDECL(count_obj
, (struct obj
*, long *, long *,
150 BOOLEAN_P
, BOOLEAN_P
));
151 STATIC_DCL
void FDECL(obj_chain
, (winid
, const char *, struct obj
*,
153 STATIC_DCL
void FDECL(mon_invent_chain
, (winid
, const char *, struct monst
*,
155 STATIC_DCL
void FDECL(mon_chain
, (winid
, const char *, struct monst
*,
157 STATIC_DCL
void FDECL(contained
, (winid
, const char *, long *, long *));
158 STATIC_PTR
int NDECL(wiz_show_stats
);
159 STATIC_DCL boolean
FDECL(accept_menu_prefix
, (int NDECL((*))));
161 STATIC_DCL
int NDECL(wiz_port_debug
);
163 STATIC_PTR
int NDECL(wiz_rumor_check
);
164 STATIC_DCL
char FDECL(cmd_from_func
, (int NDECL((*))));
165 STATIC_PTR
int NDECL(doattributes
);
166 STATIC_PTR
int NDECL(doconduct
); /**/
168 STATIC_DCL
void FDECL(enlght_line
, (const char *, const char *, const char *,
170 STATIC_DCL
char *FDECL(enlght_combatinc
, (const char *, int, int, char *));
171 STATIC_DCL
void FDECL(enlght_halfdmg
, (int, int));
172 STATIC_DCL boolean
NDECL(walking_on_water
);
173 STATIC_DCL boolean
FDECL(cause_known
, (int));
174 STATIC_DCL
char *FDECL(attrval
, (int, int, char *));
175 STATIC_DCL
void FDECL(background_enlightenment
, (int, int));
176 STATIC_DCL
void FDECL(characteristics_enlightenment
, (int, int));
177 STATIC_DCL
void FDECL(one_characteristic
, (int, int, int));
178 STATIC_DCL
void FDECL(status_enlightenment
, (int, int));
179 STATIC_DCL
void FDECL(attributes_enlightenment
, (int, int));
181 static const char *readchar_queue
= "";
182 static coord clicklook_cc
;
184 STATIC_DCL
char *NDECL(parse
);
185 STATIC_DCL boolean
FDECL(help_dir
, (CHAR_P
, const char *));
188 doprev_message(VOID_ARGS
)
190 return nh_doprev_message();
193 /* Count down by decrementing multi */
195 timed_occupation(VOID_ARGS
)
203 /* If you have moved since initially setting some occupations, they
204 * now shouldn't be able to restart.
206 * The basic rule is that if you are carrying it, you can continue
207 * since it is with you. If you are acting on something at a distance,
208 * your orientation to it must have changed when you moved.
210 * The exception to this is taking off items, since they can be taken
211 * off in a number of ways in the intervening time, screwing up ordering.
213 * Currently: Take off all armor.
214 * Picking Locks / Forcing Chests.
225 /* If a time is given, use it to timeout this function, otherwise the
226 * function times out by its own means.
229 set_occupation(fn
, txt
, xtime
)
235 occupation
= timed_occupation
;
244 STATIC_DCL
char NDECL(popch
);
246 /* Provide a means to redo the last command. The flag `in_doagain' is set
247 * to true while redoing the command. This flag is tested in commands that
248 * require additional input (like `throw' which requires a thing and a
249 * direction), and the input prompt is not shown. Also, while in_doagain is
250 * TRUE, no keystrokes can be saved into the saveq.
253 static char pushq
[BSIZE
], saveq
[BSIZE
];
254 static NEARDATA
int phead
, ptail
, shead
, stail
;
259 /* If occupied, return '\0', letting tgetch know a character should
260 * be read from the keyboard. If the character read is not the
261 * ABORT character (as checked in pcmain.c), that character will be
262 * pushed back on the pushq.
267 return (char) ((shead
!= stail
) ? saveq
[stail
++] : '\0');
269 return (char) ((phead
!= ptail
) ? pushq
[ptail
++] : '\0');
273 pgetchar() /* courtesy of aeb@cwi.nl */
282 /* A ch == 0 resets the pushq */
294 /* A ch == 0 resets the saveq. Only save keystrokes when not
295 * replaying a previous command.
303 phead
= ptail
= shead
= stail
= 0;
304 else if (shead
< BSIZE
)
310 /* here after # - now read a full-word command */
317 /* keep repeating until we don't run help or quit */
323 func
= extcmdlist
[idx
].ef_funct
;
324 if (iflags
.menu_requested
&& !accept_menu_prefix(func
)) {
325 pline("'m' prefix has no effect for this command.");
326 iflags
.menu_requested
= FALSE
;
329 } while (func
== doextlist
);
334 /* here after #? - now list all full-word commands */
338 register const struct ext_func_tab
*efp
;
342 datawin
= create_nhwindow(NHW_TEXT
);
343 putstr(datawin
, 0, "");
344 putstr(datawin
, 0, " Extended Commands List");
345 putstr(datawin
, 0, "");
346 putstr(datawin
, 0, " Press '#', then type:");
347 putstr(datawin
, 0, "");
349 for (efp
= extcmdlist
; efp
->ef_txt
; efp
++) {
350 Sprintf(buf
, " %-15s - %s.", efp
->ef_txt
, efp
->ef_desc
);
351 putstr(datawin
, 0, buf
);
353 display_nhwindow(datawin
, FALSE
);
354 destroy_nhwindow(datawin
);
358 #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
359 #define MAX_EXT_CMD 50 /* Change if we ever have > 50 ext cmds */
362 * This is currently used only by the tty port and is
363 * controlled via runtime option 'extmenu'.
364 * ``# ?'' is counted towards the limit of the number of commands,
365 * so we actually support MAX_EXT_CMD-1 "real" extended commands.
367 * Here after # - now show pick-list of possible commands.
372 const struct ext_func_tab
*efp
;
373 menu_item
*pick_list
= (menu_item
*) 0;
376 const struct ext_func_tab
*choices
[MAX_EXT_CMD
+ 1];
378 char cbuf
[QBUFSZ
], prompt
[QBUFSZ
], fmtstr
[20];
379 int i
, n
, nchoices
, acount
;
381 int accelerator
, prevaccelerator
;
390 /* populate choices */
391 for (efp
= extcmdlist
; efp
->ef_txt
; efp
++) {
392 if (!matchlevel
|| !strncmp(efp
->ef_txt
, cbuf
, matchlevel
)) {
394 if ((int) strlen(efp
->ef_desc
) > biggest
) {
395 biggest
= strlen(efp
->ef_desc
);
396 Sprintf(fmtstr
, "%%-%ds", biggest
+ 15);
398 if (++i
> MAX_EXT_CMD
) {
401 "Exceeded %d extended commands in doextcmd() menu; 'extmenu' disabled.",
409 choices
[i
] = (struct ext_func_tab
*) 0;
411 /* if we're down to one, we have our selection so get out of here */
413 for (i
= 0; extcmdlist
[i
].ef_txt
!= (char *) 0; i
++)
414 if (!strncmpi(extcmdlist
[i
].ef_txt
, cbuf
, matchlevel
)) {
422 win
= create_nhwindow(NHW_MENU
);
424 accelerator
= prevaccelerator
= 0;
426 for (i
= 0; choices
[i
]; ++i
) {
427 accelerator
= choices
[i
]->ef_txt
[matchlevel
];
428 if (accelerator
!= prevaccelerator
|| nchoices
< (ROWNO
- 3)) {
430 /* flush extended cmds for that letter already in buf */
431 Sprintf(buf
, fmtstr
, prompt
);
432 any
.a_char
= prevaccelerator
;
433 add_menu(win
, NO_GLYPH
, &any
, any
.a_char
, 0, ATR_NONE
,
438 prevaccelerator
= accelerator
;
439 if (!acount
|| nchoices
< (ROWNO
- 3)) {
440 Sprintf(prompt
, "%s [%s]", choices
[i
]->ef_txt
,
441 choices
[i
]->ef_desc
);
442 } else if (acount
== 1) {
443 Sprintf(prompt
, "%s or %s", choices
[i
- 1]->ef_txt
,
446 Strcat(prompt
, " or ");
447 Strcat(prompt
, choices
[i
]->ef_txt
);
453 Sprintf(buf
, fmtstr
, prompt
);
454 any
.a_char
= prevaccelerator
;
455 add_menu(win
, NO_GLYPH
, &any
, any
.a_char
, 0, ATR_NONE
, buf
,
458 Sprintf(prompt
, "Extended Command: %s", cbuf
);
459 end_menu(win
, prompt
);
460 n
= select_menu(win
, PICK_ONE
, &pick_list
);
461 destroy_nhwindow(win
);
463 if (matchlevel
> (QBUFSZ
- 2)) {
464 free((genericptr_t
) pick_list
);
466 impossible("Too many chars (%d) entered in extcmd_via_menu()",
471 cbuf
[matchlevel
++] = pick_list
[0].item
.a_char
;
472 cbuf
[matchlevel
] = '\0';
473 free((genericptr_t
) pick_list
);
485 #endif /* TTY_GRAPHICS */
487 /* #monster command - use special monster ability while polymorphed */
489 domonability(VOID_ARGS
)
491 if (can_breathe(youmonst
.data
))
493 else if (attacktype(youmonst
.data
, AT_SPIT
))
495 else if (youmonst
.data
->mlet
== S_NYMPH
)
497 else if (attacktype(youmonst
.data
, AT_GAZE
))
499 else if (is_were(youmonst
.data
))
501 else if (webmaker(youmonst
.data
))
503 else if (is_hider(youmonst
.data
))
505 else if (is_mind_flayer(youmonst
.data
))
506 return domindblast();
507 else if (u
.umonnum
== PM_GREMLIN
) {
508 if (IS_FOUNTAIN(levl
[u
.ux
][u
.uy
].typ
)) {
509 if (split_mon(&youmonst
, (struct monst
*) 0))
510 dryup(u
.ux
, u
.uy
, TRUE
);
512 There("is no fountain here.");
513 } else if (is_unicorn(youmonst
.data
)) {
514 use_unicorn_horn((struct obj
*) 0);
516 } else if (youmonst
.data
->msound
== MS_SHRIEK
) {
519 pline("Unfortunately sound does not carry well through rock.");
522 } else if (youmonst
.data
->mlet
== S_VAMPIRE
)
525 pline("Any special ability you may have is purely reflexive.");
527 You("don't have a special ability in your normal form!");
532 enter_explore_mode(VOID_ARGS
)
535 You("are in debug mode.");
536 } else if (discover
) {
537 You("are already in explore mode.");
541 if (!sysopt
.explorers
|| !sysopt
.explorers
[0]
542 || !check_user_string(sysopt
.explorers
)) {
543 You("cannot access explore mode.");
549 "Beware! From explore mode there will be no return to normal game.");
550 if (paranoid_query(ParanoidQuit
,
551 "Do you want to enter explore mode?")) {
552 clear_nhwindow(WIN_MESSAGE
);
553 You("are now in non-scoring explore mode.");
556 clear_nhwindow(WIN_MESSAGE
);
557 pline("Resuming normal game.");
564 dooverview_or_wiz_where(VOID_ARGS
)
573 /* ^W command - wish for something */
575 wiz_wish(VOID_ARGS
) /* Unlimited wishes for debug mode by Paul Polderman */
578 boolean save_verbose
= flags
.verbose
;
580 flags
.verbose
= FALSE
;
582 flags
.verbose
= save_verbose
;
583 (void) encumber_msg();
585 pline("Unavailable command '%s'.",
586 visctrl((int) cmd_from_func(wiz_wish
)));
590 /* ^I command - reveal and optionally identify hero's inventory */
592 wiz_identify(VOID_ARGS
)
595 iflags
.override_ID
= (int) cmd_from_func(wiz_identify
);
596 if (display_inventory((char *) 0, TRUE
) == -1)
597 identify_pack(0, FALSE
);
598 iflags
.override_ID
= 0;
600 pline("Unavailable command '%s'.",
601 visctrl((int) cmd_from_func(wiz_identify
)));
605 /* ^F command - reveal the level map and any traps on it */
611 long save_Hconf
= HConfusion
, save_Hhallu
= HHallucination
;
613 HConfusion
= HHallucination
= 0L;
614 for (t
= ftrap
; t
!= 0; t
= t
->ntrap
) {
619 HConfusion
= save_Hconf
;
620 HHallucination
= save_Hhallu
;
622 pline("Unavailable command '%s'.",
623 visctrl((int) cmd_from_func(wiz_map
)));
627 /* ^G command - generate monster(s); a count prefix will be honored */
629 wiz_genesis(VOID_ARGS
)
632 (void) create_particular();
634 pline("Unavailable command '%s'.",
635 visctrl((int) cmd_from_func(wiz_genesis
)));
639 /* ^O command - display dungeon layout */
644 (void) print_dungeon(FALSE
, (schar
*) 0, (xchar
*) 0);
646 pline("Unavailable command '%s'.",
647 visctrl((int) cmd_from_func(wiz_where
)));
651 /* ^E command - detect unseen (secret doors, traps, hidden monsters) */
653 wiz_detect(VOID_ARGS
)
658 pline("Unavailable command '%s'.",
659 visctrl((int) cmd_from_func(wiz_detect
)));
663 /* ^V command - level teleport */
665 wiz_level_tele(VOID_ARGS
)
670 pline("Unavailable command '%s'.",
671 visctrl((int) cmd_from_func(wiz_level_tele
)));
675 /* #monpolycontrol command - choose new form for shapechangers, polymorphees */
677 wiz_mon_polycontrol(VOID_ARGS
)
679 iflags
.mon_polycontrol
= !iflags
.mon_polycontrol
;
680 pline("Monster polymorph control is %s.",
681 iflags
.mon_polycontrol
? "on" : "off");
685 /* #levelchange command - adjust hero's experience level */
687 wiz_level_change(VOID_ARGS
)
693 getlin("To what experience level do you want to be set?", buf
);
694 (void) mungspaces(buf
);
695 if (buf
[0] == '\033' || buf
[0] == '\0')
698 ret
= sscanf(buf
, "%d", &newlevel
);
704 if (newlevel
== u
.ulevel
) {
705 You("are already that experienced.");
706 } else if (newlevel
< u
.ulevel
) {
708 You("are already as inexperienced as you can get.");
713 while (u
.ulevel
> newlevel
)
714 losexp("#levelchange");
716 if (u
.ulevel
>= MAXULEV
) {
717 You("are already as experienced as you can get.");
720 if (newlevel
> MAXULEV
)
722 while (u
.ulevel
< newlevel
)
725 u
.ulevelmax
= u
.ulevel
;
729 /* #panic command - test program's panic handling */
733 if (yn("Do you want to call panic() and end your game?") == 'y')
734 panic("Crash test.");
738 /* #polyself command - change hero's form */
740 wiz_polyself(VOID_ARGS
)
748 wiz_show_seenv(VOID_ARGS
)
751 int x
, y
, v
, startx
, stopx
, curx
;
754 win
= create_nhwindow(NHW_TEXT
);
756 * Each seenv description takes up 2 characters, so center
757 * the seenv display around the hero.
759 startx
= max(1, u
.ux
- (COLNO
/ 4));
760 stopx
= min(startx
+ (COLNO
/ 2), COLNO
);
761 /* can't have a line exactly 80 chars long */
762 if (stopx
- startx
== COLNO
/ 2)
765 for (y
= 0; y
< ROWNO
; y
++) {
766 for (x
= startx
, curx
= 0; x
< stopx
; x
++, curx
+= 2) {
767 if (x
== u
.ux
&& y
== u
.uy
) {
768 row
[curx
] = row
[curx
+ 1] = '@';
770 v
= levl
[x
][y
].seenv
& 0xff;
772 row
[curx
] = row
[curx
+ 1] = ' ';
774 Sprintf(&row
[curx
], "%02x", v
);
777 /* remove trailing spaces */
778 for (x
= curx
- 1; x
>= 0; x
--)
785 display_nhwindow(win
, TRUE
);
786 destroy_nhwindow(win
);
790 /* #vision command */
792 wiz_show_vision(VOID_ARGS
)
798 win
= create_nhwindow(NHW_TEXT
);
799 Sprintf(row
, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
800 COULD_SEE
, IN_SIGHT
, TEMP_LIT
);
803 for (y
= 0; y
< ROWNO
; y
++) {
804 for (x
= 1; x
< COLNO
; x
++) {
805 if (x
== u
.ux
&& y
== u
.uy
)
808 v
= viz_array
[y
][x
]; /* data access should be hidden */
812 row
[x
] = '0' + viz_array
[y
][x
];
815 /* remove trailing spaces */
816 for (x
= COLNO
- 1; x
>= 1; x
--)
821 putstr(win
, 0, &row
[1]);
823 display_nhwindow(win
, TRUE
);
824 destroy_nhwindow(win
);
830 wiz_show_wmodes(VOID_ARGS
)
836 boolean istty
= !strcmp(windowprocs
.name
, "tty");
838 win
= create_nhwindow(NHW_TEXT
);
840 putstr(win
, 0, ""); /* tty only: blank top line */
841 for (y
= 0; y
< ROWNO
; y
++) {
842 for (x
= 0; x
< COLNO
; x
++) {
844 if (x
== u
.ux
&& y
== u
.uy
)
846 else if (IS_WALL(lev
->typ
) || lev
->typ
== SDOOR
)
847 row
[x
] = '0' + (lev
->wall_info
& WM_MASK
);
848 else if (lev
->typ
== CORR
)
850 else if (IS_ROOM(lev
->typ
) || IS_DOOR(lev
->typ
))
856 /* map column 0, levl[0][], is off the left edge of the screen */
857 putstr(win
, 0, &row
[1]);
859 display_nhwindow(win
, TRUE
);
860 destroy_nhwindow(win
);
864 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
866 wiz_map_levltyp(VOID_ARGS
)
871 boolean istty
= !strcmp(windowprocs
.name
, "tty");
873 win
= create_nhwindow(NHW_TEXT
);
874 /* map row 0, levl[][0], is drawn on the second line of tty screen */
876 putstr(win
, 0, ""); /* tty only: blank top line */
877 for (y
= 0; y
< ROWNO
; y
++) {
878 /* map column 0, levl[0][], is off the left edge of the screen;
879 it should always have terrain type "undiggable stone" */
880 for (x
= 1; x
< COLNO
; x
++) {
881 terrain
= levl
[x
][y
].typ
;
882 /* assumes there aren't more than 10+26+26 terrain types */
883 row
[x
- 1] = (char) ((terrain
== 0 && !may_dig(x
, y
))
889 : 'A' + terrain
- 36);
891 if (levl
[0][y
].typ
!= 0 || may_dig(0, y
))
899 s_level
*slev
= Is_special(&u
.uz
);
901 Sprintf(dsc
, "D:%d,L:%d", u
.uz
.dnum
, u
.uz
.dlevel
);
902 /* [dungeon branch features currently omitted] */
903 /* special level features */
905 Sprintf(eos(dsc
), " \"%s\"", slev
->proto
);
906 /* special level flags (note: dungeon.def doesn't set `maze'
907 or `hell' for any specific levels so those never show up) */
908 if (slev
->flags
.maze_like
)
909 Strcat(dsc
, " mazelike");
910 if (slev
->flags
.hellish
)
911 Strcat(dsc
, " hellish");
912 if (slev
->flags
.town
)
913 Strcat(dsc
, " town");
914 if (slev
->flags
.rogue_like
)
915 Strcat(dsc
, " roguelike");
916 /* alignment currently omitted to save space */
919 if (level
.flags
.nfountains
)
920 Sprintf(eos(dsc
), " %c:%d", defsyms
[S_fountain
].sym
,
921 (int) level
.flags
.nfountains
);
922 if (level
.flags
.nsinks
)
923 Sprintf(eos(dsc
), " %c:%d", defsyms
[S_sink
].sym
,
924 (int) level
.flags
.nsinks
);
925 if (level
.flags
.has_vault
)
926 Strcat(dsc
, " vault");
927 if (level
.flags
.has_shop
)
928 Strcat(dsc
, " shop");
929 if (level
.flags
.has_temple
)
930 Strcat(dsc
, " temple");
931 if (level
.flags
.has_court
)
932 Strcat(dsc
, " throne");
933 if (level
.flags
.has_zoo
)
935 if (level
.flags
.has_morgue
)
936 Strcat(dsc
, " morgue");
937 if (level
.flags
.has_barracks
)
938 Strcat(dsc
, " barracks");
939 if (level
.flags
.has_beehive
)
940 Strcat(dsc
, " hive");
941 if (level
.flags
.has_swamp
)
942 Strcat(dsc
, " swamp");
944 if (level
.flags
.noteleport
)
945 Strcat(dsc
, " noTport");
946 if (level
.flags
.hardfloor
)
947 Strcat(dsc
, " noDig");
948 if (level
.flags
.nommap
)
949 Strcat(dsc
, " noMMap");
950 if (!level
.flags
.hero_memory
)
951 Strcat(dsc
, " noMem");
952 if (level
.flags
.shortsighted
)
953 Strcat(dsc
, " shortsight");
954 if (level
.flags
.graveyard
)
955 Strcat(dsc
, " graveyard");
956 if (level
.flags
.is_maze_lev
)
957 Strcat(dsc
, " maze");
958 if (level
.flags
.is_cavernous_lev
)
959 Strcat(dsc
, " cave");
960 if (level
.flags
.arboreal
)
961 Strcat(dsc
, " tree");
963 Strcat(dsc
, " sokoban-rules");
964 /* non-flag info; probably should include dungeon branching
965 checks (extra stairs and magic portals) here */
966 if (Invocation_lev(&u
.uz
))
967 Strcat(dsc
, " invoke");
968 if (On_W_tower_level(&u
.uz
))
969 Strcat(dsc
, " tower");
970 /* append a branch identifier for completeness' sake */
972 Strcat(dsc
, " dungeon");
973 else if (u
.uz
.dnum
== mines_dnum
)
974 Strcat(dsc
, " mines");
975 else if (In_sokoban(&u
.uz
))
976 Strcat(dsc
, " sokoban");
977 else if (u
.uz
.dnum
== quest_dnum
)
978 Strcat(dsc
, " quest");
979 else if (Is_knox(&u
.uz
))
980 Strcat(dsc
, " ludios");
981 else if (u
.uz
.dnum
== 1)
982 Strcat(dsc
, " gehennom");
983 else if (u
.uz
.dnum
== tower_dnum
)
984 Strcat(dsc
, " vlad");
985 else if (In_endgame(&u
.uz
))
986 Strcat(dsc
, " endgame");
988 /* somebody's added a dungeon branch we're not expecting */
989 const char *brname
= dungeons
[u
.uz
.dnum
].dname
;
991 if (!brname
|| !*brname
)
993 if (!strncmpi(brname
, "the ", 4))
995 Sprintf(eos(dsc
), " %s", brname
);
997 /* limit the line length to map width */
998 if (strlen(dsc
) >= COLNO
)
999 dsc
[COLNO
- 1] = '\0'; /* truncate */
1000 putstr(win
, 0, dsc
);
1003 display_nhwindow(win
, TRUE
);
1004 destroy_nhwindow(win
);
1008 /* temporary? hack, since level type codes aren't the same as screen
1009 symbols and only the latter have easily accessible descriptions */
1010 static const char *levltyp
[] = {
1011 "stone", "vertical wall", "horizontal wall", "top-left corner wall",
1012 "top-right corner wall", "bottom-left corner wall",
1013 "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall",
1014 "tee-left wall", "tee-right wall", "drawbridge wall", "tree",
1015 "secret door", "secret corridor", "pool", "moat", "water",
1016 "drawbridge up", "lava pool", "iron bars", "door", "corridor", "room",
1017 "stairs", "ladder", "fountain", "throne", "sink", "grave", "altar", "ice",
1018 "drawbridge down", "air", "cloud",
1019 /* not a real terrain type, but used for undiggable stone
1020 by wiz_map_levltyp() */
1021 "unreachable/undiggable",
1022 /* padding in case the number of entries above is odd */
1026 /* explanation of base-36 output from wiz_map_levltyp() */
1028 wiz_levltyp_legend(VOID_ARGS
)
1032 const char *dsc
, *fmt
;
1035 win
= create_nhwindow(NHW_TEXT
);
1036 putstr(win
, 0, "#terrain encodings:");
1038 fmt
= " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
1040 /* output in pairs, left hand column holds [0],[1],...,[N/2-1]
1041 and right hand column holds [N/2],[N/2+1],...,[N-1];
1042 N ('last') will always be even, and may or may not include
1043 the empty string entry to pad out the final pair, depending
1044 upon how many other entries are present in levltyp[] */
1045 last
= SIZE(levltyp
) & ~1;
1046 for (i
= 0; i
< last
/ 2; ++i
)
1047 for (j
= i
; j
< last
; j
+= last
/ 2) {
1050 : !strncmp(dsc
, "unreachable", 11) ? '*'
1051 /* same int-to-char conversion as wiz_map_levltyp() */
1052 : (j
< 10) ? '0' + j
1053 : (j
< 36) ? 'a' + j
- 10
1055 Sprintf(eos(buf
), fmt
, c
, dsc
);
1057 putstr(win
, 0, buf
);
1061 display_nhwindow(win
, TRUE
);
1062 destroy_nhwindow(win
);
1066 /* #wizsmell command - test usmellmon(). */
1068 wiz_smell(VOID_ARGS
)
1071 int mndx
; /* monster index */
1072 coord cc
; /* screen pos of unknown glyph */
1073 int glyph
; /* glyph at selected position */
1077 mndx
= 0; /* gcc -Wall lint */
1078 if (!olfaction(youmonst
.data
)) {
1079 You("are incapable of detecting odors in your present form.");
1083 pline("You can move the cursor to a monster that you want to smell.");
1085 pline("Pick a monster to smell.");
1086 ans
= getpos(&cc
, TRUE
, "a monster");
1087 if (ans
< 0 || cc
.x
< 0) {
1088 return 0; /* done */
1090 /* Convert the glyph at the selected position to a mndxbol. */
1091 glyph
= glyph_at(cc
.x
, cc
.y
);
1092 if (glyph_is_monster(glyph
))
1093 mndx
= glyph_to_mon(glyph
);
1096 /* Is it a monster? */
1098 if (!usmellmon(&mons
[mndx
]))
1099 pline("That monster seems to give off no smell.");
1101 pline("That is not a monster.");
1106 /* #wizinstrinsic command to set some intrinsics for testing */
1108 wiz_intrinsic(VOID_ARGS
)
1113 int i
, n
, accelerator
;
1114 menu_item
*pick_list
= (menu_item
*) 0;
1116 static const char *const intrinsics
[] = {
1120 win
= create_nhwindow(NHW_MENU
);
1124 for (i
= 0; i
< SIZE(intrinsics
); ++i
) {
1125 accelerator
= intrinsics
[i
][0];
1127 add_menu(win
, NO_GLYPH
, &any
, accelerator
, 0, ATR_NONE
, intrinsics
[i
], FALSE
);
1129 end_menu(win
, "Which intrinsic?");
1130 n
= select_menu(win
, PICK_ONE
, &pick_list
);
1131 destroy_nhwindow(win
);
1134 i
= pick_list
[0].item
.a_int
-1;
1135 free((genericptr_t
) pick_list
);
1140 if (!strcmp(intrinsics
[i
],"deafness")) {
1142 incr_itimeout(&HDeaf
, 30);
1143 context
.botl
= TRUE
;
1146 pline("Unavailable command '%s'.",
1147 visctrl((int) cmd_from_func(wiz_intrinsic
)));
1151 /* #wizrumorcheck command - verify each rumor access */
1153 wiz_rumor_check(VOID_ARGS
)
1159 /* #terrain command -- show known map, inspired by crawl's '|' command */
1161 doterrain(VOID_ARGS
)
1170 * normal play: choose between known map without mons, obj, and traps
1171 * (to see underlying terrain only), or
1172 * known map without mons and objs (to see traps under mons and objs), or
1173 * known map without mons (to see objects under monsters);
1174 * explore mode: normal choices plus full map (w/o mons, objs, traps);
1175 * wizard mode: normal and explore choices plus
1176 * a dump of the internal levl[][].typ codes w/ level flags, or
1177 * a legend for the levl[][].typ codes dump
1179 men
= create_nhwindow(NHW_MENU
);
1183 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1184 "known map without monsters, objects, and traps",
1187 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1188 "known map without monsters and objects",
1191 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1192 "known map without monsters",
1194 if (discover
|| wizard
) {
1196 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1197 "full map without monsters, objects, and traps",
1201 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1202 "internal levl[][].typ codes in base-36",
1205 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1206 "legend of base-36 levl[][].typ codes",
1210 end_menu(men
, "View which?");
1212 n
= select_menu(men
, PICK_ONE
, &sel
);
1213 destroy_nhwindow(men
);
1215 * n < 0: player used ESC to cancel;
1216 * n == 0: preselected entry was explicitly chosen and got toggled off;
1217 * n == 1: preselected entry was implicitly chosen via <space>|<enter>;
1218 * n == 2: another entry was explicitly chosen, so skip preselected one.
1220 which
= (n
< 0) ? -1 : (n
== 0) ? 1 : sel
[0].item
.a_int
;
1221 if (n
> 1 && which
== 1)
1222 which
= sel
[1].item
.a_int
;
1224 free((genericptr_t
) sel
);
1227 case 1: reveal_terrain(0, 0); break; /* known map */
1228 case 2: reveal_terrain(0, 1); break; /* known map with traps */
1229 case 3: reveal_terrain(0, 1|2); break; /* known map w/ traps & objs */
1230 case 4: reveal_terrain(1, 0); break; /* full map */
1231 case 5: wiz_map_levltyp(); break; /* map internals */
1232 case 6: wiz_levltyp_legend(); break; /* internal details */
1235 return 0; /* no time elapses */
1238 /* -enlightenment and conduct- */
1239 static winid en_win
= WIN_ERR
;
1240 static const char You_
[] = "You ", are
[] = "are ", were
[] = "were ",
1241 have
[] = "have ", had
[] = "had ", can
[] = "can ",
1243 static const char have_been
[] = "have been ", have_never
[] = "have never ",
1246 #define enl_msg(prefix, present, past, suffix, ps) \
1247 enlght_line(prefix, final ? past : present, suffix, ps)
1248 #define you_are(attr, ps) enl_msg(You_, are, were, attr, ps)
1249 #define you_have(attr, ps) enl_msg(You_, have, had, attr, ps)
1250 #define you_can(attr, ps) enl_msg(You_, can, could, attr, ps)
1251 #define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "")
1252 #define you_have_never(badthing) \
1253 enl_msg(You_, have_never, never, badthing, "")
1254 #define you_have_X(something) \
1255 enl_msg(You_, have, (const char *) "", something, "")
1258 enlght_line(start
, middle
, end
, ps
)
1259 const char *start
, *middle
, *end
, *ps
;
1263 Sprintf(buf
, " %s%s%s%s.", start
, middle
, end
, ps
);
1264 putstr(en_win
, 0, buf
);
1267 /* format increased chance to hit or damage or defense (Protection) */
1269 enlght_combatinc(inctyp
, incamt
, final
, outbuf
)
1274 const char *modif
, *bonus
;
1278 absamt
= abs(incamt
);
1279 /* Protection amount is typically larger than damage or to-hit;
1280 reduce magnitude by a third in order to stretch modifier ranges
1281 (small:1..5, moderate:6..10, large:11..19, huge:20+) */
1282 if (!strcmp(inctyp
, "defense"))
1283 absamt
= (absamt
* 2) / 3;
1287 else if (absamt
<= 6)
1289 else if (absamt
<= 12)
1294 modif
= !incamt
? "no" : an(modif
); /* ("no" case shouldn't happen) */
1295 bonus
= (incamt
>= 0) ? "bonus" : "penalty";
1296 /* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
1297 invrt
= strcmp(inctyp
, "to hit") ? TRUE
: FALSE
;
1299 Sprintf(outbuf
, "%s %s %s", modif
, invrt
? inctyp
: bonus
,
1300 invrt
? bonus
: inctyp
);
1301 if (final
|| wizard
)
1302 Sprintf(eos(outbuf
), " (%s%d)", (incamt
> 0) ? "+" : "", incamt
);
1307 /* report half physical or half spell damage */
1309 enlght_halfdmg(category
, final
)
1313 const char *category_name
;
1318 category_name
= "physical";
1321 category_name
= "spell";
1324 category_name
= "unknown";
1327 Sprintf(buf
, " %s %s damage", (final
|| wizard
) ? "half" : "reduced",
1329 enl_msg(You_
, "take", "took", buf
, from_what(category
));
1332 /* is hero actively using water walking capability on water (or lava)? */
1336 if (u
.uinwater
|| Levitation
|| Flying
)
1338 return (boolean
) (Wwalking
1339 && (is_pool(u
.ux
, u
.uy
) || is_lava(u
.ux
, u
.uy
)));
1342 /* check whether hero is wearing something that player definitely knows
1343 confers the target property; item must have been seen and its type
1344 discovered but it doesn't necessarily have to be fully identified */
1346 cause_known(propindx
)
1347 int propindx
; /* index of a property which can be conveyed by worn item */
1349 register struct obj
*o
;
1350 long mask
= W_ARMOR
| W_AMUL
| W_RING
| W_TOOL
;
1352 /* simpler than from_what()/what_gives(); we don't attempt to
1353 handle artifacts and we deliberately ignore wielded items */
1354 for (o
= invent
; o
; o
= o
->nobj
) {
1355 if (!(o
->owornmask
& mask
))
1357 if ((int) objects
[o
->otyp
].oc_oprop
== propindx
1358 && objects
[o
->otyp
].oc_name_known
&& o
->dknown
)
1364 /* format a characteristic value, accommodating Strength's strangeness */
1366 attrval(attrindx
, attrvalue
, resultbuf
)
1367 int attrindx
, attrvalue
;
1368 char resultbuf
[]; /* should be at least [7] to hold "18/100\0" */
1370 if (attrindx
!= A_STR
|| attrvalue
<= 18)
1371 Sprintf(resultbuf
, "%d", attrvalue
);
1372 else if (attrvalue
> STR18(100)) /* 19 to 25 */
1373 Sprintf(resultbuf
, "%d", attrvalue
- 100);
1374 else /* simplify "18/ **" to be "18/100" */
1375 Sprintf(resultbuf
, "18/%02d", attrvalue
- 18);
1380 enlightenment(mode
, final
)
1381 int mode
; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
1382 int final
; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
1384 char buf
[BUFSZ
], tmpbuf
[BUFSZ
];
1386 Strcpy(tmpbuf
, plname
);
1387 *tmpbuf
= highc(*tmpbuf
); /* same adjustment as bottom line */
1388 /* as in background_enlightenment, when poly'd we need to use the saved
1389 gender in u.mfemale rather than the current you-as-monster gender */
1390 Sprintf(buf
, "%s the %s's attributes:", tmpbuf
,
1391 ((Upolyd
? u
.mfemale
: flags
.female
) && urole
.name
.f
)
1395 en_win
= create_nhwindow(NHW_MENU
);
1397 putstr(en_win
, 0, buf
); /* "Conan the Archeologist's attributes:" */
1398 /* background and characteristics; ^X or end-of-game disclosure */
1399 if (mode
& BASICENLIGHTENMENT
) {
1400 /* role, race, alignment, deities */
1401 background_enlightenment(mode
, final
);
1402 /* strength, dexterity, &c */
1403 characteristics_enlightenment(mode
, final
);
1405 /* expanded status line information, including things which aren't
1406 included there due to space considerations--such as obvious
1407 alternative movement indicators (riding, levitation, &c), and
1408 various troubles (turning to stone, trapped, confusion, &c);
1409 shown for both basic and magic enlightenment */
1410 status_enlightenment(mode
, final
);
1411 /* remaining attributes; shown for potion,&c or wizard mode and
1412 explore mode ^X or end of game disclosure */
1413 if (mode
& MAGICENLIGHTENMENT
) {
1414 /* intrinsics and other traditional enlightenment feedback */
1415 attributes_enlightenment(mode
, final
);
1417 display_nhwindow(en_win
, TRUE
);
1418 destroy_nhwindow(en_win
);
1423 /* display role, race, alignment and such to en_win */
1425 background_enlightenment(unused_mode
, final
)
1426 int unused_mode UNUSED
;
1429 const char *role_titl
, *rank_titl
;
1430 int innategend
, difgend
, difalgn
;
1431 char buf
[BUFSZ
], tmpbuf
[BUFSZ
];
1433 /* note that if poly'd, we need to use u.mfemale instead of flags.female
1434 to access hero's saved gender-as-human/elf/&c rather than current one */
1435 innategend
= (Upolyd
? u
.mfemale
: flags
.female
) ? 1 : 0;
1436 role_titl
= (innategend
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
;
1437 rank_titl
= rank_of(u
.ulevel
, Role_switch
, innategend
);
1439 putstr(en_win
, 0, ""); /* separator after title */
1440 putstr(en_win
, 0, "Background:");
1442 /* if polymorphed, report current shape before underlying role;
1443 will be repeated as first status: "you are transformed" and also
1444 among various attributes: "you are in beast form" (after being
1445 told about lycanthropy) or "you are polymorphed into <a foo>"
1446 (with countdown timer appended for wizard mode); we really want
1447 the player to know he's not a samurai at the moment... */
1449 struct permonst
*uasmon
= youmonst
.data
;
1452 /* here we always use current gender, not saved role gender */
1453 if (!is_male(uasmon
) && !is_female(uasmon
) && !is_neuter(uasmon
))
1454 Sprintf(tmpbuf
, "%s ", genders
[flags
.female
? 1 : 0].adj
);
1455 Sprintf(buf
, "%sin %s%s form", !final
? "currently " : "", tmpbuf
,
1460 /* report role; omit gender if it's redundant (eg, "female priestess") */
1463 && ((urole
.allow
& ROLE_GENDMASK
) == (ROLE_MALE
| ROLE_FEMALE
)
1464 || innategend
!= flags
.initgend
))
1465 Sprintf(tmpbuf
, "%s ", genders
[innategend
].adj
);
1468 Strcpy(buf
, "actually "); /* "You are actually a ..." */
1469 if (!strcmpi(rank_titl
, role_titl
)) {
1470 /* omit role when rank title matches it */
1471 Sprintf(eos(buf
), "%s, level %d %s%s", an(rank_titl
), u
.ulevel
,
1472 tmpbuf
, urace
.noun
);
1474 Sprintf(eos(buf
), "%s, a level %d %s%s %s", an(rank_titl
), u
.ulevel
,
1475 tmpbuf
, urace
.adj
, role_titl
);
1479 /* report alignment (bypass you_are() in order to omit ending period) */
1480 Sprintf(buf
, " %s%s%s, %son a mission for %s",
1481 You_
, !final
? are
: were
,
1482 align_str(u
.ualign
.type
),
1483 /* helm of opposite alignment (might hide conversion) */
1484 (u
.ualign
.type
!= u
.ualignbase
[A_CURRENT
]) ? "temporarily "
1485 /* permanent conversion */
1486 : (u
.ualign
.type
!= u
.ualignbase
[A_ORIGINAL
]) ? "now "
1487 /* atheist (ignored in very early game) */
1488 : (!u
.uconduct
.gnostic
&& moves
> 1000L) ? "nominally "
1489 /* lastly, normal case */
1492 putstr(en_win
, 0, buf
);
1493 /* show the rest of this game's pantheon (finishes previous sentence)
1494 [appending "also Moloch" at the end would allow for straightforward
1495 trailing "and" on all three aligned entries but looks too verbose] */
1496 Sprintf(buf
, " who %s opposed by", !final
? "is" : "was");
1497 if (u
.ualign
.type
!= A_LAWFUL
)
1498 Sprintf(eos(buf
), " %s (%s) and", align_gname(A_LAWFUL
),
1499 align_str(A_LAWFUL
));
1500 if (u
.ualign
.type
!= A_NEUTRAL
)
1501 Sprintf(eos(buf
), " %s (%s)%s", align_gname(A_NEUTRAL
),
1502 align_str(A_NEUTRAL
),
1503 (u
.ualign
.type
!= A_CHAOTIC
) ? " and" : "");
1504 if (u
.ualign
.type
!= A_CHAOTIC
)
1505 Sprintf(eos(buf
), " %s (%s)", align_gname(A_CHAOTIC
),
1506 align_str(A_CHAOTIC
));
1507 Strcat(buf
, "."); /* terminate sentence */
1508 putstr(en_win
, 0, buf
);
1510 /* show original alignment,gender,race,role if any have been changed;
1511 giving separate message for temporary alignment change bypasses need
1512 for tricky phrasing otherwise necessitated by possibility of having
1513 helm of opposite alignment mask a permanent alignment conversion */
1514 difgend
= (innategend
!= flags
.initgend
);
1515 difalgn
= (((u
.ualign
.type
!= u
.ualignbase
[A_CURRENT
]) ? 1 : 0)
1516 + ((u
.ualignbase
[A_CURRENT
] != u
.ualignbase
[A_ORIGINAL
])
1518 if (difalgn
& 1) { /* have temporary alignment so report permanent one */
1519 Sprintf(buf
, "actually %s", align_str(u
.ualignbase
[A_CURRENT
]));
1521 difalgn
&= ~1; /* suppress helm from "started out <foo>" message */
1523 if (difgend
|| difalgn
) { /* sex change or perm align change or both */
1524 Sprintf(buf
, " You started out %s%s%s.",
1525 difgend
? genders
[flags
.initgend
].adj
: "",
1526 (difgend
&& difalgn
) ? " and " : "",
1527 difalgn
? align_str(u
.ualignbase
[A_ORIGINAL
]) : "");
1528 putstr(en_win
, 0, buf
);
1532 /* characteristics: expanded version of bottom line strength, dexterity, &c */
1534 characteristics_enlightenment(mode
, final
)
1538 putstr(en_win
, 0, ""); /* separator after background */
1540 final
? "Final Characteristics:" : "Current Characteristics:");
1542 /* bottom line order */
1543 one_characteristic(mode
, final
, A_STR
); /* strength */
1544 one_characteristic(mode
, final
, A_DEX
); /* dexterity */
1545 one_characteristic(mode
, final
, A_CON
); /* constitution */
1546 one_characteristic(mode
, final
, A_INT
); /* intelligence */
1547 one_characteristic(mode
, final
, A_WIS
); /* wisdom */
1548 one_characteristic(mode
, final
, A_CHA
); /* charisma */
1551 /* display one attribute value for characteristics_enlightenment() */
1553 one_characteristic(mode
, final
, attrindx
)
1554 int mode
, final
, attrindx
;
1556 boolean hide_innate_value
= FALSE
, interesting_alimit
;
1557 int acurrent
, abase
, apeak
, alimit
;
1558 const char *attrname
, *paren_pfx
;
1559 char subjbuf
[BUFSZ
], valubuf
[BUFSZ
], valstring
[32];
1561 /* being polymorphed or wearing certain cursed items prevents
1562 hero from reliably tracking changes to characteristics so
1563 we don't show base & peak values then; when the items aren't
1564 cursed, hero could take them off to check underlying values
1565 and we show those in such case so that player doesn't need
1566 to actually resort to doing that */
1568 hide_innate_value
= TRUE
;
1569 } else if (Fixed_abil
) {
1570 if (stuck_ring(uleft
, RIN_SUSTAIN_ABILITY
)
1571 || stuck_ring(uright
, RIN_SUSTAIN_ABILITY
))
1572 hide_innate_value
= TRUE
;
1576 attrname
= "strength";
1577 if (uarmg
&& uarmg
->otyp
== GAUNTLETS_OF_POWER
&& uarmg
->cursed
)
1578 hide_innate_value
= TRUE
;
1581 attrname
= "dexterity";
1584 attrname
= "constitution";
1587 attrname
= "intelligence";
1588 if (uarmh
&& uarmh
->otyp
== DUNCE_CAP
&& uarmh
->cursed
)
1589 hide_innate_value
= TRUE
;
1592 attrname
= "wisdom";
1593 if (uarmh
&& uarmh
->otyp
== DUNCE_CAP
&& uarmh
->cursed
)
1594 hide_innate_value
= TRUE
;
1597 attrname
= "charisma";
1600 return; /* impossible */
1602 /* note: final disclosure includes MAGICENLIGHTENTMENT */
1603 if ((mode
& MAGICENLIGHTENMENT
) && !Upolyd
)
1604 hide_innate_value
= FALSE
;
1606 acurrent
= ACURR(attrindx
);
1607 (void) attrval(attrindx
, acurrent
, valubuf
); /* Sprintf(valubuf,"%d",) */
1608 Sprintf(subjbuf
, "Your %s ", attrname
);
1610 if (!hide_innate_value
) {
1611 /* show abase, amax, and/or attrmax if acurr doesn't match abase
1612 (a magic bonus or penalty is in effect) or abase doesn't match
1613 amax (some points have been lost to poison or exercise abuse
1614 and are restorable) or attrmax is different from normal human
1615 (while game is in progress; trying to reduce dependency on
1616 spoilers to keep track of such stuff) or attrmax was different
1617 from abase (at end of game; this attribute wasn't maxed out) */
1618 abase
= ABASE(attrindx
);
1619 apeak
= AMAX(attrindx
);
1620 alimit
= ATTRMAX(attrindx
);
1621 /* criterium for whether the limit is interesting varies */
1622 interesting_alimit
=
1623 final
? TRUE
/* was originally `(abase != alimit)' */
1624 : (alimit
!= (attrindx
!= A_STR
? 18 : STR18(100)));
1625 paren_pfx
= final
? " (" : " (current; ";
1626 if (acurrent
!= abase
) {
1627 Sprintf(eos(valubuf
), "%sbase:%s", paren_pfx
,
1628 attrval(attrindx
, abase
, valstring
));
1631 if (abase
!= apeak
) {
1632 Sprintf(eos(valubuf
), "%speak:%s", paren_pfx
,
1633 attrval(attrindx
, apeak
, valstring
));
1636 if (interesting_alimit
) {
1637 Sprintf(eos(valubuf
), "%s%slimit:%s", paren_pfx
,
1638 /* more verbose if exceeding 'limit' due to magic bonus */
1639 (acurrent
> alimit
) ? "innate " : "",
1640 attrval(attrindx
, alimit
, valstring
));
1641 /* paren_pfx = ", "; */
1643 if (acurrent
!= abase
|| abase
!= apeak
|| interesting_alimit
)
1644 Strcat(valubuf
, ")");
1646 enl_msg(subjbuf
, "is ", "was ", valubuf
, "");
1649 /* status: selected obvious capabilities, assorted troubles */
1651 status_enlightenment(mode
, final
)
1655 boolean magic
= (mode
& MAGICENLIGHTENMENT
) ? TRUE
: FALSE
;
1657 char buf
[BUFSZ
], youtoo
[BUFSZ
];
1658 boolean Riding
= (u
.usteed
1659 /* if hero dies while dismounting, u.usteed will still
1660 be set; we want to ignore steed in that situation */
1661 && !(final
== ENL_GAMEOVERDEAD
1662 && !strcmp(killer
.name
, "riding accident")));
1663 const char *steedname
= (!Riding
? (char *) 0
1664 : x_monnam(u
.usteed
,
1665 u
.usteed
->mtame
? ARTICLE_YOUR
: ARTICLE_THE
,
1667 (SUPPRESS_SADDLE
| SUPPRESS_HALLUCINATION
),
1671 * Status (many are abbreviated on bottom line; others are or
1672 * should be discernible to the hero hence to the player)
1674 putstr(en_win
, 0, ""); /* separator after title or characteristics */
1675 putstr(en_win
, 0, final
? "Final Status:" : "Current Status:");
1677 Strcpy(youtoo
, You_
);
1678 /* not a traditional status but inherently obvious to player; more
1679 detail given below (attributes section) for magic enlightenment */
1681 you_are("transformed", "");
1682 /* not a trouble, but we want to display riding status before maybe
1683 reporting steed as trapped or hero stuck to cursed saddle */
1685 Sprintf(buf
, "riding %s", steedname
);
1687 Sprintf(eos(youtoo
), "and %s ", steedname
);
1689 /* other movement situations that hero should always know */
1691 if (Lev_at_will
&& magic
)
1692 you_are("levitating, at will", "");
1694 enl_msg(youtoo
, are
, were
, "levitating", from_what(LEVITATION
));
1695 } else if (Flying
) { /* can only fly when not levitating */
1696 enl_msg(youtoo
, are
, were
, "flying", from_what(FLYING
));
1699 you_are("underwater", "");
1700 } else if (u
.uinwater
) {
1701 you_are(Swimming
? "swimming" : "in water", from_what(SWIMMING
));
1702 } else if (walking_on_water()) {
1703 /* show active Wwalking here, potential Wwalking elsewhere */
1704 Sprintf(buf
, "walking on %s",
1705 is_pool(u
.ux
, u
.uy
) ? "water"
1706 : is_lava(u
.ux
, u
.uy
) ? "lava"
1707 : surface(u
.ux
, u
.uy
)); /* catchall; shouldn't happen */
1708 you_are(buf
, from_what(WWALKING
));
1710 if (Upolyd
&& (u
.uundetected
|| youmonst
.m_ap_type
!= M_AP_NOTHING
))
1711 youhiding(TRUE
, final
);
1713 /* internal troubles, mostly in the order that prayer ranks them */
1715 you_are("turning to stone", "");
1717 you_are("turning into slime", "");
1720 you_are("buried", "");
1722 Strcpy(buf
, "being strangled");
1724 Sprintf(eos(buf
), " (%ld)", (Strangled
& TIMEOUT
));
1725 you_are(buf
, from_what(STRANGLED
));
1729 /* prayer lumps these together; botl puts Ill before FoodPois */
1730 if (u
.usick_type
& SICK_NONVOMITABLE
)
1731 you_are("terminally sick from illness", "");
1732 if (u
.usick_type
& SICK_VOMITABLE
)
1733 you_are("terminally sick from food poisoning", "");
1736 you_are("nauseated", "");
1738 you_are("stunned", "");
1740 you_are("confused", "");
1742 you_are("hallucinating", "");
1744 /* from_what() (currently wizard-mode only) checks !haseyes()
1745 before u.uroleplay.blind, so we should too */
1746 Sprintf(buf
, "%s blind",
1747 !haseyes(youmonst
.data
) ? "innately"
1748 : u
.uroleplay
.blind
? "permanently"
1749 /* better phrasing desperately wanted... */
1750 : Blindfolded_only
? "deliberately"
1752 if (wizard
&& (Blinded
& TIMEOUT
) != 0L
1753 && !u
.uroleplay
.blind
&& haseyes(youmonst
.data
))
1754 Sprintf(eos(buf
), " (%ld)", (Blinded
& TIMEOUT
));
1755 /* !haseyes: avoid "you are innately blind innately" */
1756 you_are(buf
, !haseyes(youmonst
.data
) ? "" : from_what(BLINDED
));
1759 you_are("deaf", from_what(DEAF
));
1761 /* external troubles, more or less */
1764 Sprintf(buf
, "chained to %s", ansimpleoname(uball
));
1766 impossible("Punished without uball?");
1767 Strcpy(buf
, "punished");
1772 char predicament
[BUFSZ
];
1774 boolean anchored
= (u
.utraptype
== TT_BURIEDBALL
);
1777 Strcpy(predicament
, "tethered to something buried");
1778 } else if (u
.utraptype
== TT_INFLOOR
|| u
.utraptype
== TT_LAVA
) {
1779 Sprintf(predicament
, "stuck in %s", the(surface(u
.ux
, u
.uy
)));
1781 Strcpy(predicament
, "trapped");
1782 if ((t
= t_at(u
.ux
, u
.uy
)) != 0)
1783 Sprintf(eos(predicament
), " in %s",
1784 an(defsyms
[trap_to_defsym(t
->ttyp
)].explanation
));
1786 if (u
.usteed
) { /* not `Riding' here */
1787 Sprintf(buf
, "%s%s ", anchored
? "you and " : "", steedname
);
1789 enl_msg(buf
, (anchored
? "are " : "is "),
1790 (anchored
? "were " : "was "), predicament
, "");
1792 you_are(predicament
, "");
1795 Sprintf(buf
, "swallowed by %s", a_monnam(u
.ustuck
));
1797 Sprintf(eos(buf
), " (%u)", u
.uswldtim
);
1799 } else if (u
.ustuck
) {
1800 Sprintf(buf
, "%s %s",
1801 (Upolyd
&& sticks(youmonst
.data
)) ? "holding" : "held by",
1802 a_monnam(u
.ustuck
));
1806 struct obj
*saddle
= which_armor(u
.usteed
, W_SADDLE
);
1808 if (saddle
&& saddle
->cursed
) {
1809 Sprintf(buf
, "stuck to %s %s", s_suffix(steedname
),
1810 simpleonames(saddle
));
1815 /* when mounted, Wounded_legs applies to steed rather than to
1816 hero; we only report steed's wounded legs in wizard mode */
1817 if (u
.usteed
) { /* not `Riding' here */
1818 if (wizard
&& steedname
) {
1819 Strcpy(buf
, steedname
);
1821 enl_msg(buf
, " has", " had", " wounded legs", "");
1824 Sprintf(buf
, "wounded %s", makeplural(body_part(LEG
)));
1829 Sprintf(buf
, "slippery %s", makeplural(body_part(FINGER
)));
1833 if (magic
|| cause_known(FUMBLING
))
1834 enl_msg(You_
, "fumble", "fumbled", "", from_what(FUMBLING
));
1837 if (magic
|| cause_known(SLEEPY
)) {
1838 Strcpy(buf
, from_what(SLEEPY
));
1840 Sprintf(eos(buf
), " (%ld)", (HSleepy
& TIMEOUT
));
1841 enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf
);
1844 /* hunger/nutrition */
1846 if (magic
|| cause_known(HUNGER
))
1847 enl_msg(You_
, "hunger", "hungered", " rapidly",
1850 Strcpy(buf
, hu_stat
[u
.uhs
]); /* hunger status; omitted if "normal" */
1851 mungspaces(buf
); /* strip trailing spaces */
1853 *buf
= lowc(*buf
); /* override capitalization */
1854 if (!strcmp(buf
, "weak"))
1855 Strcat(buf
, " from severe hunger");
1856 else if (!strncmp(buf
, "faint", 5)) /* fainting, fainted */
1857 Strcat(buf
, " due to starvation");
1861 if ((cap
= near_capacity()) > UNENCUMBERED
) {
1862 const char *adj
= "?_?"; /* (should always get overridden) */
1864 Strcpy(buf
, enc_stat
[cap
]);
1869 break; /* burdened */
1872 break; /* stressed */
1875 break; /* strained */
1878 break; /* overtaxed */
1880 adj
= "not possible";
1883 Sprintf(eos(buf
), "; movement %s %s%s", !final
? "is" : "was", adj
,
1884 (cap
< OVERLOADED
) ? " slowed" : "");
1887 /* last resort entry, guarantees Status section is non-empty
1888 (no longer needed for that purpose since weapon status added;
1889 still useful though) */
1890 you_are("unencumbered", "");
1892 /* report being weaponless; distinguish whether gloves are worn */
1894 you_are(uarmg
? "empty handed" /* gloves imply hands */
1895 : humanoid(youmonst
.data
)
1896 /* hands but no weapon and no gloves */
1898 /* alternate phrasing for paws or lack of hands */
1899 : "not wielding anything",
1901 /* two-weaponing implies a weapon (not other odd stuff) in each hand */
1902 } else if (u
.twoweap
) {
1903 you_are("wielding two weapons at once", "");
1904 /* report most weapons by their skill class (so a katana will be
1905 described as a long sword, for instance; mattock and hook are
1906 exceptions), or wielded non-weapon item by its object class */
1908 const char *what
= weapon_descr(uwep
);
1910 if (!strcmpi(what
, "armor") || !strcmpi(what
, "food")
1911 || !strcmpi(what
, "venom"))
1912 Sprintf(buf
, "wielding some %s", what
);
1914 Sprintf(buf
, "wielding %s",
1915 (uwep
->quan
== 1L) ? an(what
) : makeplural(what
));
1918 /* report 'nudity' */
1919 if (!uarm
&& !uarmu
&& !uarmc
&& !uarmg
&& !uarmf
&& !uarmh
) {
1920 if (u
.uroleplay
.nudist
)
1921 enl_msg(You_
, "do", "did", " not wear any armor", "");
1923 you_are("not wearing any armor", "");
1927 /* attributes: intrinsics and the like, other non-obvious capabilities */
1929 attributes_enlightenment(unused_mode
, final
)
1930 int unused_mode UNUSED
;
1933 static NEARDATA
const char if_surroundings_permitted
[] =
1934 " if surroundings permitted";
1941 putstr(en_win
, 0, "");
1942 putstr(en_win
, 0, final
? "Final Attributes:" : "Current Attributes:");
1944 if (u
.uevent
.uhand_of_elbereth
) {
1945 static const char *const hofe_titles
[3] = { "the Hand of Elbereth",
1946 "the Envoy of Balance",
1947 "the Glory of Arioch" };
1948 you_are(hofe_titles
[u
.uevent
.uhand_of_elbereth
- 1], "");
1951 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
1952 if (u
.ualign
.record
>= 20)
1953 you_are("piously aligned", "");
1954 else if (u
.ualign
.record
> 13)
1955 you_are("devoutly aligned", "");
1956 else if (u
.ualign
.record
> 8)
1957 you_are("fervently aligned", "");
1958 else if (u
.ualign
.record
> 3)
1959 you_are("stridently aligned", "");
1960 else if (u
.ualign
.record
== 3)
1961 you_are("aligned", "");
1962 else if (u
.ualign
.record
> 0)
1963 you_are("haltingly aligned", "");
1964 else if (u
.ualign
.record
== 0)
1965 you_are("nominally aligned", "");
1966 else if (u
.ualign
.record
>= -3)
1967 you_have("strayed", "");
1968 else if (u
.ualign
.record
>= -8)
1969 you_have("sinned", "");
1971 you_have("transgressed", "");
1973 Sprintf(buf
, " %d", u
.ualign
.record
);
1974 enl_msg("Your alignment ", "is", "was", buf
, "");
1977 /*** Resistances to troubles ***/
1979 you_are("invulnerable", from_what(INVULNERABLE
));
1981 you_are("magic-protected", from_what(ANTIMAGIC
));
1982 if (Fire_resistance
)
1983 you_are("fire resistant", from_what(FIRE_RES
));
1984 if (Cold_resistance
)
1985 you_are("cold resistant", from_what(COLD_RES
));
1986 if (Sleep_resistance
)
1987 you_are("sleep resistant", from_what(SLEEP_RES
));
1988 if (Disint_resistance
)
1989 you_are("disintegration-resistant", from_what(DISINT_RES
));
1990 if (Shock_resistance
)
1991 you_are("shock resistant", from_what(SHOCK_RES
));
1992 if (Poison_resistance
)
1993 you_are("poison resistant", from_what(POISON_RES
));
1994 if (Acid_resistance
)
1995 you_are("acid resistant", from_what(ACID_RES
));
1996 if (Drain_resistance
)
1997 you_are("level-drain resistant", from_what(DRAIN_RES
));
1998 if (Sick_resistance
)
1999 you_are("immune to sickness", from_what(SICK_RES
));
2000 if (Stone_resistance
)
2001 you_are("petrification resistant", from_what(STONE_RES
));
2002 if (Halluc_resistance
)
2003 enl_msg(You_
, "resist", "resisted", " hallucinations",
2004 from_what(HALLUC_RES
));
2006 you_can("recognize detrimental food", "");
2008 /*** Vision and senses ***/
2009 if (!Blind
&& (Blinded
|| !haseyes(youmonst
.data
)))
2010 you_can("see", from_what(-BLINDED
)); /* Eyes of the Overworld */
2011 if (See_invisible
) {
2013 enl_msg(You_
, "see", "saw", " invisible", from_what(SEE_INVIS
));
2015 enl_msg(You_
, "will see", "would have seen",
2016 " invisible when not blind", from_what(SEE_INVIS
));
2019 you_are("telepathic", from_what(TELEPAT
));
2021 you_are("warned", from_what(WARNING
));
2022 if (Warn_of_mon
&& context
.warntype
.obj
) {
2023 Sprintf(buf
, "aware of the presence of %s",
2024 (context
.warntype
.obj
& M2_ORC
) ? "orcs"
2025 : (context
.warntype
.obj
& M2_ELF
) ? "elves"
2026 : (context
.warntype
.obj
& M2_DEMON
) ? "demons" : something
);
2027 you_are(buf
, from_what(WARN_OF_MON
));
2029 if (Warn_of_mon
&& context
.warntype
.polyd
) {
2030 Sprintf(buf
, "aware of the presence of %s",
2031 ((context
.warntype
.polyd
& (M2_HUMAN
| M2_ELF
))
2032 == (M2_HUMAN
| M2_ELF
))
2033 ? "humans and elves"
2034 : (context
.warntype
.polyd
& M2_HUMAN
)
2036 : (context
.warntype
.polyd
& M2_ELF
)
2038 : (context
.warntype
.polyd
& M2_ORC
)
2040 : (context
.warntype
.polyd
& M2_DEMON
)
2042 : "certain monsters");
2045 if (Warn_of_mon
&& context
.warntype
.speciesidx
>= LOW_PM
) {
2046 Sprintf(buf
, "aware of the presence of %s",
2047 makeplural(mons
[context
.warntype
.speciesidx
].mname
));
2048 you_are(buf
, from_what(WARN_OF_MON
));
2051 you_are("warned of undead", from_what(WARN_UNDEAD
));
2053 you_have("automatic searching", from_what(SEARCHING
));
2055 you_are("clairvoyant", from_what(CLAIRVOYANT
));
2056 else if ((HClairvoyant
|| EClairvoyant
) && BClairvoyant
) {
2057 Strcpy(buf
, from_what(-CLAIRVOYANT
));
2058 if (!strncmp(buf
, " because of ", 12))
2059 /* overwrite substring; strncpy doesn't add terminator */
2060 (void) strncpy(buf
, " if not for ", 12);
2061 enl_msg(You_
, "could be", "could have been", " clairvoyant", buf
);
2064 you_have("infravision", from_what(INFRAVISION
));
2065 if (Detect_monsters
)
2066 you_are("sensing the presence of monsters", "");
2068 you_are("going to confuse monsters", "");
2070 /*** Appearance and behavior ***/
2074 if (uleft
&& uleft
->otyp
== RIN_ADORNMENT
)
2075 adorn
+= uleft
->spe
;
2076 if (uright
&& uright
->otyp
== RIN_ADORNMENT
)
2077 adorn
+= uright
->spe
;
2078 /* the sum might be 0 (+0 ring or two which negate each other);
2079 that yields "you are charismatic" (which isn't pointless
2080 because it potentially impacts seduction attacks) */
2081 Sprintf(buf
, "%scharismatic",
2082 (adorn
> 0) ? "more " : (adorn
< 0) ? "less " : "");
2083 you_are(buf
, from_what(ADORNED
));
2086 you_are("invisible", from_what(INVIS
));
2088 you_are("invisible to others", from_what(INVIS
));
2089 /* ordinarily "visible" is redundant; this is a special case for
2090 the situation when invisibility would be an expected attribute */
2091 else if ((HInvis
|| EInvis
) && BInvis
)
2092 you_are("visible", from_what(-INVIS
));
2094 you_are("displaced", from_what(DISPLACED
));
2096 you_are("stealthy", from_what(STEALTH
));
2097 if (Aggravate_monster
)
2098 enl_msg("You aggravate", "", "d", " monsters",
2099 from_what(AGGRAVATE_MONSTER
));
2101 enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT
));
2103 /*** Transportation ***/
2105 you_can("jump", from_what(JUMPING
));
2107 you_can("teleport", from_what(TELEPORT
));
2108 if (Teleport_control
)
2109 you_have("teleport control", from_what(TELEPORT_CONTROL
));
2110 /* actively levitating handled earlier as a status condition */
2111 if (BLevitation
) { /* levitation is blocked */
2112 long save_BLev
= BLevitation
;
2116 enl_msg(You_
, "would levitate", "would have levitated",
2117 if_surroundings_permitted
, "");
2118 BLevitation
= save_BLev
;
2120 /* actively flying handled earlier as a status condition */
2121 if (BFlying
) { /* flight is blocked */
2122 long save_BFly
= BFlying
;
2126 enl_msg(You_
, "would fly", "would have flown",
2128 ? "if you weren't levitating"
2129 : (save_BFly
== FROMOUTSIDE
)
2130 ? if_surroundings_permitted
2131 /* both surroundings and [latent] levitation */
2132 : " if circumstances permitted",
2134 BFlying
= save_BFly
;
2136 /* actively walking on water handled earlier as a status condition */
2137 if (Wwalking
&& !walking_on_water())
2138 you_can("walk on water", from_what(WWALKING
));
2139 /* actively swimming (in water but not under it) handled earlier */
2140 if (Swimming
&& (Underwater
|| !u
.uinwater
))
2141 you_can("swim", from_what(SWIMMING
));
2143 you_can("survive without air", from_what(MAGICAL_BREATHING
));
2144 else if (Amphibious
)
2145 you_can("breathe water", from_what(MAGICAL_BREATHING
));
2147 you_can("walk through walls", from_what(PASSES_WALLS
));
2149 /*** Physical attributes ***/
2151 enl_msg("You regenerate", "", "d", "", from_what(REGENERATION
));
2153 you_have("slower digestion", from_what(SLOW_DIGESTION
));
2155 you_have(enlght_combatinc("to hit", u
.uhitinc
, final
, buf
), "");
2157 you_have(enlght_combatinc("damage", u
.udaminc
, final
, buf
), "");
2158 if (u
.uspellprot
|| Protection
) {
2161 if (uleft
&& uleft
->otyp
== RIN_PROTECTION
)
2163 if (uright
&& uright
->otyp
== RIN_PROTECTION
)
2164 prot
+= uright
->spe
;
2165 if (HProtection
& INTRINSIC
)
2167 prot
+= u
.uspellprot
;
2169 you_have(enlght_combatinc("defense", prot
, final
, buf
), "");
2171 if ((armpro
= magic_negation(&youmonst
)) > 0) {
2172 /* magic cancellation factor, conferred by worn armor */
2173 static const char *const mc_types
[] = {
2174 "" /*ordinary*/, "warded", "guarded", "protected",
2177 if (armpro
>= SIZE(mc_types
))
2178 armpro
= SIZE(mc_types
) - 1;
2179 you_are(mc_types
[armpro
], "");
2181 if (Half_physical_damage
)
2182 enlght_halfdmg(HALF_PHDAM
, final
);
2183 if (Half_spell_damage
)
2184 enlght_halfdmg(HALF_SPDAM
, final
);
2185 /* polymorph and other shape change */
2186 if (Protection_from_shape_changers
)
2187 you_are("protected from shape changers",
2188 from_what(PROT_FROM_SHAPE_CHANGERS
));
2190 const char *what
= 0;
2192 if (!Upolyd
) /* Upolyd handled below after current form */
2193 you_can("not change from your current form",
2194 from_what(UNCHANGING
));
2195 /* blocked shape changes */
2197 what
= !final
? "polymorph" : "have polymorphed";
2198 else if (u
.ulycn
>= LOW_PM
)
2199 what
= !final
? "change shape" : "have changed shape";
2201 Sprintf(buf
, "would %s periodically", what
);
2202 /* omit from_what(UNCHANGING); too verbose */
2203 enl_msg(You_
, buf
, buf
, " if not locked into your current form",
2206 } else if (Polymorph
) {
2207 you_are("polymorphing periodically", from_what(POLYMORPH
));
2209 if (Polymorph_control
)
2210 you_have("polymorph control", from_what(POLYMORPH_CONTROL
));
2211 if (Upolyd
&& u
.umonnum
!= u
.ulycn
) {
2212 /* foreign shape (except were-form which is handled below) */
2213 Sprintf(buf
, "polymorphed into %s", an(youmonst
.data
->mname
));
2215 Sprintf(eos(buf
), " (%d)", u
.mtimedone
);
2218 if (lays_eggs(youmonst
.data
) && flags
.female
) /* Upolyd */
2219 you_can("lay eggs", "");
2220 if (u
.ulycn
>= LOW_PM
) {
2221 /* "you are a werecreature [in beast form]" */
2222 Strcpy(buf
, an(mons
[u
.ulycn
].mname
));
2223 if (u
.umonnum
== u
.ulycn
) {
2224 Strcat(buf
, " in beast form");
2226 Sprintf(eos(buf
), " (%d)", u
.mtimedone
);
2230 if (Unchanging
&& Upolyd
) /* !Upolyd handled above */
2231 you_can("not change from your current form", from_what(UNCHANGING
));
2233 you_are("harmed by silver", "");
2234 /* movement and non-armor-based protection */
2236 you_are(Very_fast
? "very fast" : "fast", from_what(FAST
));
2238 you_have("reflection", from_what(REFLECTING
));
2240 you_have("free action", from_what(FREE_ACTION
));
2242 you_have("fixed abilities", from_what(FIXED_ABIL
));
2244 enl_msg("Your life ", "will be", "would have been", " saved", "");
2246 /*** Miscellany ***/
2248 ltmp
= abs((int) Luck
);
2249 Sprintf(buf
, "%s%slucky",
2250 ltmp
>= 10 ? "extremely " : ltmp
>= 5 ? "very " : "",
2251 Luck
< 0 ? "un" : "");
2253 Sprintf(eos(buf
), " (%d)", Luck
);
2256 enl_msg("Your luck ", "is", "was", " zero", "");
2258 you_have("extra luck", "");
2259 else if (u
.moreluck
< 0)
2260 you_have("reduced luck", "");
2261 if (carrying(LUCKSTONE
) || stone_luck(TRUE
)) {
2262 ltmp
= stone_luck(0);
2264 enl_msg("Bad luck ", "does", "did", " not time out for you", "");
2266 enl_msg("Good luck ", "does", "did", " not time out for you", "");
2270 Sprintf(buf
, " %sangry with you",
2271 u
.ugangr
> 6 ? "extremely " : u
.ugangr
> 3 ? "very " : "");
2273 Sprintf(eos(buf
), " (%d)", u
.ugangr
);
2274 enl_msg(u_gname(), " is", " was", buf
, "");
2277 * We need to suppress this when the game is over, because death
2278 * can change the value calculated by can_pray(), potentially
2279 * resulting in a false claim that you could have prayed safely.
2283 /* "can [not] safely pray" vs "could [not] have safely prayed" */
2284 Sprintf(buf
, "%s%ssafely pray%s", can_pray(FALSE
) ? "" : "not ",
2285 final
? "have " : "", final
? "ed" : "");
2287 Sprintf(buf
, "%ssafely pray", can_pray(FALSE
) ? "" : "not ");
2290 Sprintf(eos(buf
), " (%d)", u
.ublesscnt
);
2296 /* named fruit debugging (doesn't really belong here...); to enable,
2297 include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
2298 if (wizard
&& explicitdebug("fruit")) {
2303 for (f
= ffruit
; f
; f
= f
->nextf
) {
2304 Sprintf(buf
, "Fruit %d ", ++fcount
);
2305 Sprintf(buf2
, "%s (id %d)", f
->fname
, f
->fid
);
2306 enl_msg(buf
, "is ", "was ", buf2
, "");
2308 enl_msg("The current fruit ", "is ", "was ", pl_fruit
, "");
2309 Sprintf(buf
, "%d", flags
.made_fruit
);
2310 enl_msg("The made fruit flag ", "is ", "was ", buf
, "");
2318 if (final
< 2) { /* still in progress, or quit/escaped/ascended */
2319 p
= "survived after being killed ";
2320 switch (u
.umortality
) {
2322 p
= !final
? (char *) 0 : "survived";
2325 Strcpy(buf
, "once");
2328 Strcpy(buf
, "twice");
2331 Strcpy(buf
, "thrice");
2334 Sprintf(buf
, "%d times", u
.umortality
);
2337 } else { /* game ended in character's death */
2339 switch (u
.umortality
) {
2341 impossible("dead without dying?");
2343 break; /* just "are dead" */
2345 Sprintf(buf
, " (%d%s time!)", u
.umortality
,
2346 ordin(u
.umortality
));
2351 enl_msg(You_
, "have been killed ", p
, buf
, "");
2355 #if 0 /* no longer used */
2356 STATIC_DCL boolean
NDECL(minimal_enlightenment
);
2359 * Courtesy function for non-debug, non-explorer mode players
2360 * to help refresh them about who/what they are.
2361 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
2364 minimal_enlightenment()
2367 menu_item
*selected
;
2370 char buf
[BUFSZ
], buf2
[BUFSZ
];
2371 static const char untabbed_fmtstr
[] = "%-15s: %-12s";
2372 static const char untabbed_deity_fmtstr
[] = "%-17s%s";
2373 static const char tabbed_fmtstr
[] = "%s:\t%-12s";
2374 static const char tabbed_deity_fmtstr
[] = "%s\t%s";
2375 static const char *fmtstr
;
2376 static const char *deity_fmtstr
;
2378 fmtstr
= iflags
.menu_tab_sep
? tabbed_fmtstr
: untabbed_fmtstr
;
2379 deity_fmtstr
= iflags
.menu_tab_sep
? tabbed_deity_fmtstr
2380 : untabbed_deity_fmtstr
;
2382 buf
[0] = buf2
[0] = '\0';
2383 tmpwin
= create_nhwindow(NHW_MENU
);
2385 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, iflags
.menu_headings
,
2388 /* Starting name, race, role, gender */
2389 Sprintf(buf
, fmtstr
, "name", plname
);
2390 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2391 Sprintf(buf
, fmtstr
, "race", urace
.noun
);
2392 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2393 Sprintf(buf
, fmtstr
, "role",
2394 (flags
.initgend
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
);
2395 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2396 Sprintf(buf
, fmtstr
, "gender", genders
[flags
.initgend
].adj
);
2397 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2399 /* Starting alignment */
2400 Sprintf(buf
, fmtstr
, "alignment", align_str(u
.ualignbase
[A_ORIGINAL
]));
2401 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2403 /* Current name, race, role, gender */
2404 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", FALSE
);
2405 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, iflags
.menu_headings
,
2407 Sprintf(buf
, fmtstr
, "race", Upolyd
? youmonst
.data
->mname
: urace
.noun
);
2408 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2410 Sprintf(buf
, fmtstr
, "role (base)",
2411 (u
.mfemale
&& urole
.name
.f
) ? urole
.name
.f
2413 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2415 Sprintf(buf
, fmtstr
, "role",
2416 (flags
.female
&& urole
.name
.f
) ? urole
.name
.f
2418 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2420 /* don't want poly_gender() here; it forces `2' for non-humanoids */
2421 genidx
= is_neuter(youmonst
.data
) ? 2 : flags
.female
;
2422 Sprintf(buf
, fmtstr
, "gender", genders
[genidx
].adj
);
2423 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2424 if (Upolyd
&& (int) u
.mfemale
!= genidx
) {
2425 Sprintf(buf
, fmtstr
, "gender (base)", genders
[u
.mfemale
].adj
);
2426 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2429 /* Current alignment */
2430 Sprintf(buf
, fmtstr
, "alignment", align_str(u
.ualign
.type
));
2431 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2434 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", FALSE
);
2435 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, iflags
.menu_headings
,
2437 Sprintf(buf2
, deity_fmtstr
, align_gname(A_CHAOTIC
),
2438 (u
.ualignbase
[A_ORIGINAL
] == u
.ualign
.type
2439 && u
.ualign
.type
== A_CHAOTIC
) ? " (s,c)"
2440 : (u
.ualignbase
[A_ORIGINAL
] == A_CHAOTIC
) ? " (s)"
2441 : (u
.ualign
.type
== A_CHAOTIC
) ? " (c)" : "");
2442 Sprintf(buf
, fmtstr
, "Chaotic", buf2
);
2443 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2445 Sprintf(buf2
, deity_fmtstr
, align_gname(A_NEUTRAL
),
2446 (u
.ualignbase
[A_ORIGINAL
] == u
.ualign
.type
2447 && u
.ualign
.type
== A_NEUTRAL
) ? " (s,c)"
2448 : (u
.ualignbase
[A_ORIGINAL
] == A_NEUTRAL
) ? " (s)"
2449 : (u
.ualign
.type
== A_NEUTRAL
) ? " (c)" : "");
2450 Sprintf(buf
, fmtstr
, "Neutral", buf2
);
2451 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2453 Sprintf(buf2
, deity_fmtstr
, align_gname(A_LAWFUL
),
2454 (u
.ualignbase
[A_ORIGINAL
] == u
.ualign
.type
2455 && u
.ualign
.type
== A_LAWFUL
) ? " (s,c)"
2456 : (u
.ualignbase
[A_ORIGINAL
] == A_LAWFUL
) ? " (s)"
2457 : (u
.ualign
.type
== A_LAWFUL
) ? " (c)" : "");
2458 Sprintf(buf
, fmtstr
, "Lawful", buf2
);
2459 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2461 end_menu(tmpwin
, "Base Attributes");
2462 n
= select_menu(tmpwin
, PICK_NONE
, &selected
);
2463 destroy_nhwindow(tmpwin
);
2464 return (boolean
) (n
!= -1);
2470 doattributes(VOID_ARGS
)
2472 int mode
= BASICENLIGHTENMENT
;
2474 /* show more--as if final disclosure--for wizard and explore modes */
2475 if (wizard
|| discover
)
2476 mode
|= MAGICENLIGHTENMENT
;
2478 enlightenment(mode
, ENL_GAMEINPROGRESS
);
2483 youhiding(via_enlghtmt
, msgflag
)
2484 boolean via_enlghtmt
; /* englightment line vs topl message */
2485 int msgflag
; /* for variant message phrasing */
2487 char *bp
, buf
[BUFSZ
];
2489 Strcpy(buf
, "hiding");
2490 if (youmonst
.m_ap_type
!= M_AP_NOTHING
) {
2491 /* mimic; hero is only able to mimic a strange object or gold
2492 or hallucinatory alternative to gold, so we skip the details
2493 for the hypothetical furniture and monster cases */
2494 bp
= eos(strcpy(buf
, "mimicking"));
2495 if (youmonst
.m_ap_type
== M_AP_OBJECT
) {
2496 Sprintf(bp
, " %s", an(simple_typename(youmonst
.mappearance
)));
2497 } else if (youmonst
.m_ap_type
== M_AP_FURNITURE
) {
2498 Strcpy(bp
, " something");
2499 } else if (youmonst
.m_ap_type
== M_AP_MONSTER
) {
2500 Strcpy(bp
, " someone");
2502 ; /* something unexpected; leave 'buf' as-is */
2504 } else if (u
.uundetected
) {
2505 bp
= eos(buf
); /* points past "hiding" */
2506 if (youmonst
.data
->mlet
== S_EEL
) {
2507 if (is_pool(u
.ux
, u
.uy
))
2508 Sprintf(bp
, " in the %s", waterbody_name(u
.ux
, u
.uy
));
2509 } else if (hides_under(youmonst
.data
)) {
2510 struct obj
*o
= level
.objects
[u
.ux
][u
.uy
];
2513 Sprintf(bp
, " underneath %s", ansimpleoname(o
));
2514 } else if (is_clinger(youmonst
.data
) || Flying
) {
2515 /* Flying: 'lurker above' hides on ceiling but doesn't cling */
2516 Sprintf(bp
, " on the %s", ceiling(u
.ux
, u
.uy
));
2518 /* on floor; is_hider() but otherwise not special: 'trapper' */
2519 if (u
.utrap
&& u
.utraptype
== TT_PIT
) {
2520 struct trap
*t
= t_at(u
.ux
, u
.uy
);
2522 Sprintf(bp
, " in a %spit",
2523 (t
&& t
->ttyp
== SPIKED_PIT
) ? "spiked " : "");
2525 Sprintf(bp
, " on the %s", surface(u
.ux
, u
.uy
));
2528 ; /* shouldn't happen; will result in generic "you are hiding" */
2532 int final
= msgflag
; /* 'final' is used by you_are() macro */
2536 /* for dohide(), when player uses '#monster' command */
2537 You("are %s %s.", msgflag
? "already" : "now", buf
);
2542 * (shares enlightenment's tense handling)
2545 doconduct(VOID_ARGS
)
2558 /* Create the conduct window */
2559 en_win
= create_nhwindow(NHW_MENU
);
2560 putstr(en_win
, 0, "Voluntary challenges:");
2562 if (u
.uroleplay
.blind
)
2563 you_have_been("blind from birth");
2564 if (u
.uroleplay
.nudist
)
2565 you_have_been("faithfully nudist");
2567 if (!u
.uconduct
.food
)
2568 enl_msg(You_
, "have gone", "went", " without food", "");
2569 /* But beverages are okay */
2570 else if (!u
.uconduct
.unvegan
)
2571 you_have_X("followed a strict vegan diet");
2572 else if (!u
.uconduct
.unvegetarian
)
2573 you_have_been("vegetarian");
2575 if (!u
.uconduct
.gnostic
)
2576 you_have_been("an atheist");
2578 if (!u
.uconduct
.weaphit
) {
2579 you_have_never("hit with a wielded weapon");
2580 } else if (wizard
) {
2581 Sprintf(buf
, "used a wielded weapon %ld time%s", u
.uconduct
.weaphit
,
2582 plur(u
.uconduct
.weaphit
));
2585 if (!u
.uconduct
.killer
)
2586 you_have_been("a pacifist");
2588 if (!u
.uconduct
.literate
) {
2589 you_have_been("illiterate");
2590 } else if (wizard
) {
2591 Sprintf(buf
, "read items or engraved %ld time%s", u
.uconduct
.literate
,
2592 plur(u
.uconduct
.literate
));
2596 ngenocided
= num_genocides();
2597 if (ngenocided
== 0) {
2598 you_have_never("genocided any monsters");
2600 Sprintf(buf
, "genocided %d type%s of monster%s", ngenocided
,
2601 plur(ngenocided
), plur(ngenocided
));
2605 if (!u
.uconduct
.polypiles
) {
2606 you_have_never("polymorphed an object");
2607 } else if (wizard
) {
2608 Sprintf(buf
, "polymorphed %ld item%s", u
.uconduct
.polypiles
,
2609 plur(u
.uconduct
.polypiles
));
2613 if (!u
.uconduct
.polyselfs
) {
2614 you_have_never("changed form");
2615 } else if (wizard
) {
2616 Sprintf(buf
, "changed form %ld time%s", u
.uconduct
.polyselfs
,
2617 plur(u
.uconduct
.polyselfs
));
2621 if (!u
.uconduct
.wishes
) {
2622 you_have_X("used no wishes");
2624 Sprintf(buf
, "used %ld wish%s", u
.uconduct
.wishes
,
2625 (u
.uconduct
.wishes
> 1L) ? "es" : "");
2628 if (!u
.uconduct
.wisharti
)
2629 enl_msg(You_
, "have not wished", "did not wish",
2630 " for any artifacts", "");
2633 /* Pop up the window and wait for a key */
2634 display_nhwindow(en_win
, TRUE
);
2635 destroy_nhwindow(en_win
);
2641 #define M(c) (0x80 | (c))
2643 #define M(c) ((c) -128)
2647 #define C(c) (0x1f & (c))
2650 static const struct func_tab cmdlist
[] = {
2651 { C('d'), FALSE
, dokick
}, /* "D" is for door!...? Msg is in dokick.c */
2652 { C('e'), TRUE
, wiz_detect
},
2653 { C('f'), TRUE
, wiz_map
},
2654 { C('g'), TRUE
, wiz_genesis
},
2655 { C('i'), TRUE
, wiz_identify
},
2656 { C('l'), TRUE
, doredraw
}, /* if number_pad is set */
2657 { C('n'), TRUE
, donamelevel
}, /* if number_pad is set */
2658 { C('o'), TRUE
, dooverview_or_wiz_where
}, /* depends on wizard status */
2659 { C('p'), TRUE
, doprev_message
},
2660 { C('r'), TRUE
, doredraw
},
2661 { C('t'), TRUE
, dotele
},
2662 { C('v'), TRUE
, wiz_level_tele
},
2663 { C('w'), TRUE
, wiz_wish
},
2664 { C('x'), TRUE
, doattributes
},
2665 { C('z'), TRUE
, dosuspend_core
},
2666 { 'a', FALSE
, doapply
},
2667 { 'A', FALSE
, doddoremarm
},
2668 { M('a'), TRUE
, doorganize
},
2669 { M('A'), TRUE
, donamelevel
}, /* #annotate */
2670 /* 'b', 'B' : go sw */
2671 { 'c', FALSE
, doclose
},
2672 { 'C', TRUE
, docallcmd
},
2673 { M('c'), TRUE
, dotalk
},
2674 { M('C'), TRUE
, doconduct
}, /* #conduct */
2675 { 'd', FALSE
, dodrop
},
2676 { 'D', FALSE
, doddrop
},
2677 { M('d'), FALSE
, dodip
},
2678 { 'e', FALSE
, doeat
},
2679 { 'E', FALSE
, doengrave
},
2680 { M('e'), TRUE
, enhance_weapon_skill
},
2681 { 'f', FALSE
, dofire
},
2682 /* 'F' : fight (one time) */
2683 { M('f'), FALSE
, doforce
},
2684 /* 'g', 'G' : multiple go */
2685 /* 'h', 'H' : go west */
2686 { 'h', TRUE
, dohelp
}, /* if number_pad is set */
2687 { 'i', TRUE
, ddoinv
},
2688 { 'I', TRUE
, dotypeinv
}, /* Robert Viduya */
2689 { M('i'), TRUE
, doinvoke
},
2690 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */
2691 { 'j', FALSE
, dojump
}, /* if number_pad is on */
2692 { M('j'), FALSE
, dojump
},
2693 { 'k', FALSE
, dokick
}, /* if number_pad is on */
2694 { 'l', FALSE
, doloot
}, /* if number_pad is on */
2695 { M('l'), FALSE
, doloot
},
2696 /* 'n' prefixes a count if number_pad is on */
2697 { M('m'), TRUE
, domonability
},
2698 { 'N', TRUE
, docallcmd
}, /* if number_pad is on */
2699 { M('n'), TRUE
, docallcmd
},
2700 { M('N'), TRUE
, docallcmd
},
2701 { 'o', FALSE
, doopen
},
2702 { 'O', TRUE
, doset
},
2703 { M('o'), FALSE
, dosacrifice
},
2704 { M('O'), TRUE
, dooverview
}, /* #overview */
2705 { 'p', FALSE
, dopay
},
2706 { 'P', FALSE
, doputon
},
2707 { M('p'), TRUE
, dopray
},
2708 { 'q', FALSE
, dodrink
},
2709 { 'Q', FALSE
, dowieldquiver
},
2710 { M('q'), TRUE
, done2
},
2711 { 'r', FALSE
, doread
},
2712 { 'R', FALSE
, doremring
},
2713 { M('r'), FALSE
, dorub
},
2714 { M('R'), FALSE
, doride
}, /* #ride */
2715 { 's', TRUE
, dosearch
, "searching" },
2716 { 'S', TRUE
, dosave
},
2717 { M('s'), FALSE
, dosit
},
2718 { 't', FALSE
, dothrow
},
2719 { 'T', FALSE
, dotakeoff
},
2720 { M('t'), TRUE
, doturn
},
2721 { M('T'), FALSE
, dotip
}, /* #tip */
2722 /* 'u', 'U' : go ne */
2723 { 'u', FALSE
, dountrap
}, /* if number_pad is on */
2724 { M('u'), FALSE
, dountrap
},
2725 { 'v', TRUE
, doversion
},
2726 { 'V', TRUE
, dohistory
},
2727 { M('v'), TRUE
, doextversion
},
2728 { 'w', FALSE
, dowield
},
2729 { 'W', FALSE
, dowear
},
2730 { M('w'), FALSE
, dowipe
},
2731 { 'x', FALSE
, doswapweapon
},
2732 { 'X', FALSE
, dotwoweapon
},
2733 /* 'y', 'Y' : go nw */
2734 { 'z', FALSE
, dozap
},
2735 { 'Z', TRUE
, docast
},
2736 { '<', FALSE
, doup
},
2737 { '>', FALSE
, dodown
},
2738 { '/', TRUE
, dowhatis
},
2739 { '&', TRUE
, dowhatdoes
},
2740 { '?', TRUE
, dohelp
},
2741 { M('?'), TRUE
, doextlist
},
2743 { '!', TRUE
, dosh
},
2745 { '.', TRUE
, donull
, "waiting" },
2746 { ' ', TRUE
, donull
, "waiting" },
2747 { ',', FALSE
, dopickup
},
2748 { ':', TRUE
, dolook
},
2749 { ';', TRUE
, doquickwhatis
},
2750 { '^', TRUE
, doidtrap
},
2751 { '\\', TRUE
, dodiscovered
}, /* Robert Viduya */
2752 { '`', TRUE
, doclassdisco
},
2753 { '@', TRUE
, dotogglepickup
},
2754 { M('2'), FALSE
, dotwoweapon
},
2755 { WEAPON_SYM
, TRUE
, doprwep
},
2756 { ARMOR_SYM
, TRUE
, doprarm
},
2757 { RING_SYM
, TRUE
, doprring
},
2758 { AMULET_SYM
, TRUE
, dopramulet
},
2759 { TOOL_SYM
, TRUE
, doprtool
},
2760 { '*', TRUE
, doprinuse
}, /* inventory of all equipment in use */
2761 { GOLD_SYM
, TRUE
, doprgold
},
2762 { SPBOOK_SYM
, TRUE
, dovspell
}, /* Mike Stephenson */
2763 { '#', TRUE
, doextcmd
},
2764 { '_', TRUE
, dotravel
},
2768 struct ext_func_tab extcmdlist
[] = {
2769 { "adjust", "adjust inventory letters", doorganize
, TRUE
},
2770 { "annotate", "name current level", donamelevel
, TRUE
},
2771 { "chat", "talk to someone", dotalk
, TRUE
}, /* converse? */
2772 { "conduct", "list voluntary challenges you have maintained", doconduct
,
2774 { "dip", "dip an object into something", dodip
, FALSE
},
2775 { "enhance", "advance or check weapon and spell skills",
2776 enhance_weapon_skill
, TRUE
},
2777 { "exploremode", "enter explore mode", enter_explore_mode
, TRUE
},
2778 { "force", "force a lock", doforce
, FALSE
},
2779 { "invoke", "invoke an object's powers", doinvoke
, TRUE
},
2780 { "jump", "jump to a location", dojump
, FALSE
},
2781 { "kick", "kick something", dokick
, FALSE
},
2782 { "loot", "loot a box on the floor", doloot
, FALSE
},
2783 { "monster", "use a monster's special ability", domonability
, TRUE
},
2784 { "name", "name a monster or an object", docallcmd
, TRUE
},
2785 { "offer", "offer a sacrifice to the gods", dosacrifice
, FALSE
},
2786 { "overview", "show an overview of the dungeon", dooverview
, TRUE
},
2787 { "pray", "pray to the gods for help", dopray
, TRUE
},
2788 { "quit", "exit without saving current game", done2
, TRUE
},
2789 { "ride", "ride (or stop riding) a monster", doride
, FALSE
},
2790 { "rub", "rub a lamp or a stone", dorub
, FALSE
},
2791 { "sit", "sit down", dosit
, FALSE
},
2792 { "terrain", "show map without obstructions", doterrain
, TRUE
},
2793 { "tip", "empty a container", dotip
, FALSE
},
2794 { "turn", "turn undead", doturn
, TRUE
},
2795 { "twoweapon", "toggle two-weapon combat", dotwoweapon
, FALSE
},
2796 { "untrap", "untrap something", dountrap
, FALSE
},
2797 { "version", "list compile time options for this version of NetHack",
2798 doextversion
, TRUE
},
2799 { "wipe", "wipe off your face", dowipe
, FALSE
},
2800 { "?", "get this list of extended commands", doextlist
, TRUE
},
2802 * There must be a blank entry here for every entry in the table
2805 { (char *) 0, (char *) 0, donull
, TRUE
}, /* levelchange */
2806 { (char *) 0, (char *) 0, donull
, TRUE
}, /* lightsources */
2807 #ifdef DEBUG_MIGRATING_MONS
2808 { (char *) 0, (char *) 0, donull
, TRUE
}, /* migratemons */
2810 { (char *) 0, (char *) 0, donull
, TRUE
}, /* monpolycontrol */
2811 { (char *) 0, (char *) 0, donull
, TRUE
}, /* panic */
2812 { (char *) 0, (char *) 0, donull
, TRUE
}, /* polyself */
2814 { (char *) 0, (char *) 0, donull
, TRUE
}, /* portdebug */
2816 { (char *) 0, (char *) 0, donull
, TRUE
}, /* seenv */
2817 { (char *) 0, (char *) 0, donull
, TRUE
}, /* stats */
2818 { (char *) 0, (char *) 0, donull
, TRUE
}, /* timeout */
2819 { (char *) 0, (char *) 0, donull
, TRUE
}, /* vanquished */
2820 { (char *) 0, (char *) 0, donull
, TRUE
}, /* vision */
2821 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizsmell */
2822 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizintrinsic */
2824 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizdebug_traveldisplay */
2825 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizdebug_bury */
2827 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizrumorcheck */
2828 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wmode */
2829 { (char *) 0, (char *) 0, donull
, TRUE
} /* sentinel */
2832 /* there must be a placeholder in the table above for every entry here */
2833 static const struct ext_func_tab debug_extcmdlist
[] = {
2834 { "levelchange", "change experience level", wiz_level_change
, TRUE
},
2835 { "lightsources", "show mobile light sources", wiz_light_sources
, TRUE
},
2836 #ifdef DEBUG_MIGRATING_MONS
2837 { "migratemons", "migrate n random monsters", wiz_migrate_mons
, TRUE
},
2839 { "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol
,
2841 { "panic", "test panic routine (fatal to game)", wiz_panic
, TRUE
},
2842 { "polyself", "polymorph self", wiz_polyself
, TRUE
},
2844 { "portdebug", "wizard port debug command", wiz_port_debug
, TRUE
},
2846 { "seenv", "show seen vectors", wiz_show_seenv
, TRUE
},
2847 { "stats", "show memory statistics", wiz_show_stats
, TRUE
},
2848 { "timeout", "look at timeout queue", wiz_timeout_queue
, TRUE
},
2849 { "vanquished", "list vanquished monsters", dovanquished
, TRUE
},
2850 { "vision", "show vision array", wiz_show_vision
, TRUE
},
2851 { "wizsmell", "smell monster", wiz_smell
, TRUE
},
2852 { "wizintrinsic", "set intrinsic", wiz_intrinsic
, TRUE
},
2854 { "wizdebug_traveldisplay", "wizard debug: toggle travel display",
2855 wiz_debug_cmd_traveldisplay
, TRUE
},
2856 { "wizdebug_bury", "wizard debug: bury objs under and around you",
2857 wiz_debug_cmd_bury
, TRUE
},
2859 { "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check
, TRUE
},
2860 { "wmode", "show wall modes", wiz_show_wmodes
, TRUE
},
2861 { (char *) 0, (char *) 0, donull
, TRUE
}
2865 * Insert debug commands into the extended command list. This function
2866 * assumes that the last entry will be the help entry.
2868 * You must add entries in ext_func_tab every time you add one to the
2869 * debug_extcmdlist().
2872 add_debug_extended_commands()
2876 /* count the # of help entries */
2877 for (n
= 0; extcmdlist
[n
].ef_txt
[0] != '?'; n
++)
2880 for (i
= 0; debug_extcmdlist
[i
].ef_txt
; i
++) {
2881 /* need enough room for "?" entry plus terminator */
2882 if (n
+ 2 >= SIZE(extcmdlist
))
2883 panic("Too many debugging commands!");
2884 for (j
= 0; j
< n
; j
++)
2885 if (strcmp(debug_extcmdlist
[i
].ef_txt
, extcmdlist
[j
].ef_txt
) < 0)
2888 /* insert i'th debug entry into extcmdlist[j], pushing down */
2889 for (k
= n
; k
>= j
; --k
)
2890 extcmdlist
[k
+ 1] = extcmdlist
[k
];
2891 extcmdlist
[j
] = debug_extcmdlist
[i
];
2892 n
++; /* now an extra entry */
2901 for (i
= 0; i
< SIZE(cmdlist
); ++i
)
2902 if (cmdlist
[i
].f_funct
== fn
)
2903 return cmdlist
[i
].f_char
;
2907 static const char template[] = "%-18s %4ld %6ld";
2908 static const char count_str
[] = " count bytes";
2909 static const char separator
[] = "------------------ ----- ------";
2915 int sz
= (int) sizeof(struct obj
);
2918 sz
+= (int) sizeof(struct oextra
);
2920 sz
+= (int) strlen(ONAME(otmp
)) + 1;
2922 sz
+= (int) sizeof(struct monst
);
2924 sz
+= (int) sizeof(unsigned);
2926 sz
+= (int) sizeof(long);
2928 sz
+= (int) strlen(OMAILCMD(otmp
)) + 1;
2934 count_obj(chain
, total_count
, total_size
, top
, recurse
)
2944 for (count
= size
= 0, obj
= chain
; obj
; obj
= obj
->nobj
) {
2947 size
+= size_obj(obj
);
2949 if (recurse
&& obj
->cobj
)
2950 count_obj(obj
->cobj
, total_count
, total_size
, TRUE
, TRUE
);
2952 *total_count
+= count
;
2953 *total_size
+= size
;
2957 obj_chain(win
, src
, chain
, total_count
, total_size
)
2965 long count
= 0, size
= 0;
2967 count_obj(chain
, &count
, &size
, TRUE
, FALSE
);
2968 *total_count
+= count
;
2969 *total_size
+= size
;
2970 Sprintf(buf
, template, src
, count
, size
);
2971 putstr(win
, 0, buf
);
2975 mon_invent_chain(win
, src
, chain
, total_count
, total_size
)
2978 struct monst
*chain
;
2983 long count
= 0, size
= 0;
2986 for (mon
= chain
; mon
; mon
= mon
->nmon
)
2987 count_obj(mon
->minvent
, &count
, &size
, TRUE
, FALSE
);
2988 *total_count
+= count
;
2989 *total_size
+= size
;
2990 Sprintf(buf
, template, src
, count
, size
);
2991 putstr(win
, 0, buf
);
2995 contained(win
, src
, total_count
, total_size
)
3002 long count
= 0, size
= 0;
3005 count_obj(invent
, &count
, &size
, FALSE
, TRUE
);
3006 count_obj(fobj
, &count
, &size
, FALSE
, TRUE
);
3007 count_obj(level
.buriedobjlist
, &count
, &size
, FALSE
, TRUE
);
3008 count_obj(migrating_objs
, &count
, &size
, FALSE
, TRUE
);
3009 /* DEADMONSTER check not required in this loop since they have no
3011 for (mon
= fmon
; mon
; mon
= mon
->nmon
)
3012 count_obj(mon
->minvent
, &count
, &size
, FALSE
, TRUE
);
3013 for (mon
= migrating_mons
; mon
; mon
= mon
->nmon
)
3014 count_obj(mon
->minvent
, &count
, &size
, FALSE
, TRUE
);
3016 *total_count
+= count
;
3017 *total_size
+= size
;
3019 Sprintf(buf
, template, src
, count
, size
);
3020 putstr(win
, 0, buf
);
3027 int sz
= (int) sizeof(struct monst
);
3030 sz
+= (int) sizeof(struct mextra
);
3032 sz
+= (int) strlen(MNAME(mtmp
)) + 1;
3034 sz
+= (int) sizeof(struct egd
);
3036 sz
+= (int) sizeof(struct epri
);
3038 sz
+= (int) sizeof(struct eshk
);
3040 sz
+= (int) sizeof(struct emin
);
3042 sz
+= (int) sizeof(struct edog
);
3043 /* mextra->mcorpsenm doesn't point to more memory */
3049 mon_chain(win
, src
, chain
, total_count
, total_size
)
3052 struct monst
*chain
;
3060 for (count
= size
= 0, mon
= chain
; mon
; mon
= mon
->nmon
) {
3062 size
+= size_monst(mon
);
3064 *total_count
+= count
;
3065 *total_size
+= size
;
3066 Sprintf(buf
, template, src
, count
, size
);
3067 putstr(win
, 0, buf
);
3071 * Display memory usage of all monsters and objects on the level.
3078 long total_obj_size
= 0, total_obj_count
= 0;
3079 long total_mon_size
= 0, total_mon_count
= 0;
3081 win
= create_nhwindow(NHW_TEXT
);
3082 putstr(win
, 0, "Current memory statistics:");
3084 Sprintf(buf
, "Objects, size %d", (int) sizeof(struct obj
));
3085 putstr(win
, 0, buf
);
3087 putstr(win
, 0, count_str
);
3089 obj_chain(win
, "invent", invent
, &total_obj_count
, &total_obj_size
);
3090 obj_chain(win
, "fobj", fobj
, &total_obj_count
, &total_obj_size
);
3091 obj_chain(win
, "buried", level
.buriedobjlist
, &total_obj_count
,
3093 obj_chain(win
, "migrating obj", migrating_objs
, &total_obj_count
,
3095 mon_invent_chain(win
, "minvent", fmon
, &total_obj_count
, &total_obj_size
);
3096 mon_invent_chain(win
, "migrating minvent", migrating_mons
,
3097 &total_obj_count
, &total_obj_size
);
3099 contained(win
, "contained", &total_obj_count
, &total_obj_size
);
3101 putstr(win
, 0, separator
);
3102 Sprintf(buf
, template, "Total", total_obj_count
, total_obj_size
);
3103 putstr(win
, 0, buf
);
3107 Sprintf(buf
, "Monsters, size %d", (int) sizeof(struct monst
));
3108 putstr(win
, 0, buf
);
3111 mon_chain(win
, "fmon", fmon
, &total_mon_count
, &total_mon_size
);
3112 mon_chain(win
, "migrating", migrating_mons
, &total_mon_count
,
3115 putstr(win
, 0, separator
);
3116 Sprintf(buf
, template, "Total", total_mon_count
, total_mon_size
);
3117 putstr(win
, 0, buf
);
3119 #if defined(__BORLANDC__) && !defined(_WIN32)
3120 show_borlandc_stats(win
);
3123 display_nhwindow(win
, FALSE
);
3124 destroy_nhwindow(win
);
3132 timer_sanity_check();
3134 light_sources_sanity_check();
3137 #ifdef DEBUG_MIGRATING_MONS
3143 struct permonst
*ptr
;
3147 getlin("How many random monsters to migrate? [0]", inbuf
);
3148 if (*inbuf
== '\033')
3150 mcount
= atoi(inbuf
);
3151 if (mcount
< 0 || mcount
> (COLNO
* ROWNO
) || Is_botlevel(&u
.uz
))
3153 while (mcount
> 0) {
3154 if (Is_stronghold(&u
.uz
))
3155 assign_level(&tolevel
, &valley_level
);
3157 get_level(&tolevel
, depth(&u
.uz
) + 1);
3159 mtmp
= makemon(ptr
, 0, 0, NO_MM_FLAGS
);
3161 migrate_to_level(mtmp
, ledger_no(&tolevel
), MIGR_RANDOM
,
3169 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
3170 #define unmeta(c) (0x7f & (c))
3172 /* called at startup and after number_pad is twiddled */
3174 reset_commands(initial
)
3177 static const char sdir
[] = "hykulnjb><",
3178 sdir_swap_yz
[] = "hzkulnjb><",
3179 ndir
[] = "47896321><",
3180 ndir_phone_layout
[] = "41236987><";
3181 static const int ylist
[] = {
3182 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
3184 const struct func_tab
*cmdtmp
;
3186 int c
, i
, updated
= 0;
3190 for (i
= 0; i
< SIZE(cmdlist
); i
++) {
3191 c
= cmdlist
[i
].f_char
& 0xff;
3192 Cmd
.commands
[c
] = &cmdlist
[i
];
3194 Cmd
.num_pad
= FALSE
;
3195 Cmd
.pcHack_compat
= Cmd
.phone_layout
= Cmd
.swap_yz
= FALSE
;
3198 flagtemp
= iflags
.num_pad
;
3199 if (flagtemp
!= Cmd
.num_pad
) {
3200 Cmd
.num_pad
= flagtemp
;
3203 /* swap_yz mode (only applicable for !num_pad) */
3204 flagtemp
= (iflags
.num_pad_mode
& 1) ? !Cmd
.num_pad
: FALSE
;
3205 if (flagtemp
!= Cmd
.swap_yz
) {
3206 Cmd
.swap_yz
= flagtemp
;
3208 /* Cmd.swap_yz has been toggled;
3209 perform the swap (or reverse previous one) */
3210 for (i
= 0; i
< SIZE(ylist
); i
++) {
3211 c
= ylist
[i
] & 0xff;
3212 cmdtmp
= Cmd
.commands
[c
]; /* tmp = [y] */
3213 Cmd
.commands
[c
] = Cmd
.commands
[c
+ 1]; /* [y] = [z] */
3214 Cmd
.commands
[c
+ 1] = cmdtmp
; /* [z] = tmp */
3217 /* MSDOS compatibility mode (only applicable for num_pad) */
3218 flagtemp
= (iflags
.num_pad_mode
& 1) ? Cmd
.num_pad
: FALSE
;
3219 if (flagtemp
!= Cmd
.pcHack_compat
) {
3220 Cmd
.pcHack_compat
= flagtemp
;
3222 /* pcHack_compat has been toggled */
3224 cmdtmp
= Cmd
.commands
['5'];
3225 Cmd
.commands
['5'] = Cmd
.commands
[c
];
3226 Cmd
.commands
[c
] = cmdtmp
;
3228 Cmd
.commands
[c
] = Cmd
.pcHack_compat
? Cmd
.commands
['I'] : 0;
3230 /* phone keypad layout (only applicable for num_pad) */
3231 flagtemp
= (iflags
.num_pad_mode
& 2) ? Cmd
.num_pad
: FALSE
;
3232 if (flagtemp
!= Cmd
.phone_layout
) {
3233 Cmd
.phone_layout
= flagtemp
;
3235 /* phone_layout has been toggled */
3236 for (i
= 0; i
< 3; i
++) {
3237 c
= '1' + i
; /* 1,2,3 <-> 7,8,9 */
3238 cmdtmp
= Cmd
.commands
[c
]; /* tmp = [1] */
3239 Cmd
.commands
[c
] = Cmd
.commands
[c
+ 6]; /* [1] = [7] */
3240 Cmd
.commands
[c
+ 6] = cmdtmp
; /* [7] = tmp */
3241 c
= (M('1') & 0xff) + i
; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
3242 cmdtmp
= Cmd
.commands
[c
]; /* tmp = [M-1] */
3243 Cmd
.commands
[c
] = Cmd
.commands
[c
+ 6]; /* [M-1] = [M-7] */
3244 Cmd
.commands
[c
+ 6] = cmdtmp
; /* [M-7] = tmp */
3251 Cmd
.dirchars
= !Cmd
.num_pad
3252 ? (!Cmd
.swap_yz
? sdir
: sdir_swap_yz
)
3253 : (!Cmd
.phone_layout
? ndir
: ndir_phone_layout
);
3254 Cmd
.alphadirchars
= !Cmd
.num_pad
? Cmd
.dirchars
: sdir
;
3256 Cmd
.move_W
= Cmd
.dirchars
[0];
3257 Cmd
.move_NW
= Cmd
.dirchars
[1];
3258 Cmd
.move_N
= Cmd
.dirchars
[2];
3259 Cmd
.move_NE
= Cmd
.dirchars
[3];
3260 Cmd
.move_E
= Cmd
.dirchars
[4];
3261 Cmd
.move_SE
= Cmd
.dirchars
[5];
3262 Cmd
.move_S
= Cmd
.dirchars
[6];
3263 Cmd
.move_SW
= Cmd
.dirchars
[7];
3267 accept_menu_prefix(cmd_func
)
3268 int NDECL((*cmd_func
));
3270 if (cmd_func
== dopickup
|| cmd_func
== dotip
3271 || cmd_func
== doextcmd
|| cmd_func
== doextlist
)
3280 boolean do_walk
, do_rush
, prefix_seen
, bad_command
,
3281 firsttime
= (cmd
== 0);
3283 iflags
.menu_requested
= FALSE
;
3285 if (program_state
.done_hup
)
3292 if (*cmd
== '\033') {
3293 context
.move
= FALSE
;
3296 if (*cmd
== DOAGAIN
&& !in_doagain
&& saveq
[0]) {
3299 rhack((char *) 0); /* read and execute command */
3303 /* Special case of *cmd == ' ' handled better below */
3304 if (!*cmd
|| *cmd
== (char) 0377) {
3306 context
.move
= FALSE
;
3307 return; /* probably we just had an interrupt */
3310 /* handle most movement commands */
3311 do_walk
= do_rush
= prefix_seen
= FALSE
;
3312 context
.travel
= context
.travel1
= 0;
3315 if (movecmd(cmd
[1])) {
3323 break; /* else FALLTHRU */
3325 if (movecmd(lowc(cmd
[1]))) {
3333 break; /* else FALLTHRU */
3334 /* Effects of movement commands and invisible monsters:
3335 * m: always move onto space (even if 'I' remembered)
3336 * F: always attack space (even if 'I' not remembered)
3337 * normal movement: attack if 'I', move otherwise.
3340 if (movecmd(cmd
[1])) {
3341 context
.forcefight
= 1;
3347 if (movecmd(cmd
[1]) || u
.dz
) {
3353 cmd
[0] = cmd
[1]; /* "m<" or "m>" */
3358 if (movecmd(lowc(cmd
[1]))) {
3368 (void) ddoinv(); /* a convenience borrowed from the PC */
3369 context
.move
= FALSE
;
3373 if (iflags
.clicklook
) {
3374 context
.move
= FALSE
;
3375 do_look(2, &clicklook_cc
);
3379 if (flags
.travelcmd
) {
3381 context
.travel1
= 1;
3389 if (movecmd(*cmd
)) { /* ordinary movement */
3390 context
.run
= 0; /* only matters here if it was 8 */
3392 } else if (movecmd(Cmd
.num_pad
? unmeta(*cmd
) : lowc(*cmd
))) {
3395 } else if (movecmd(unctrl(*cmd
))) {
3402 /* some special prefix handling */
3403 /* overload 'm' prefix to mean "request a menu" */
3404 if (prefix_seen
&& cmd
[0] == 'm') {
3405 /* (for func_tab cast, see below) */
3406 const struct func_tab
*ft
= Cmd
.commands
[cmd
[1] & 0xff];
3407 int NDECL((*func
)) = ft
? ((struct func_tab
*) ft
)->f_funct
: 0;
3409 if (func
&& accept_menu_prefix(func
)) {
3410 iflags
.menu_requested
= TRUE
;
3415 if ((do_walk
|| do_rush
) && !context
.travel
&& !dxdy_moveok()) {
3416 /* trying to move diagonally as a grid bug;
3417 this used to be treated by movecmd() as not being
3418 a movement attempt, but that didn't provide for any
3419 feedback and led to strangeness if the key pressed
3420 ('u' in particular) was overloaded for num_pad use */
3421 You_cant("get there from here...");
3423 context
.nopick
= context
.forcefight
= FALSE
;
3424 context
.move
= context
.mv
= FALSE
;
3433 context
.forcefight
= 0;
3435 } else if (do_rush
) {
3438 multi
= max(COLNO
, ROWNO
);
3439 u
.last_str_turn
= 0;
3444 } else if (prefix_seen
&& cmd
[1] == '\033') { /* <prefix><escape> */
3445 /* don't report "unknown command" for change of heart... */
3446 bad_command
= FALSE
;
3447 } else if (*cmd
== ' ' && !flags
.rest_on_space
) {
3448 bad_command
= TRUE
; /* skip cmdlist[] loop */
3450 /* handle all other commands */
3452 register const struct func_tab
*tlist
;
3453 int res
, NDECL((*func
));
3455 /* current - use *cmd to directly index cmdlist array */
3456 if ((tlist
= Cmd
.commands
[*cmd
& 0xff]) != 0) {
3457 if (u
.uburied
&& !tlist
->can_if_buried
) {
3458 You_cant("do that while you are buried!");
3461 /* we discard 'const' because some compilers seem to have
3462 trouble with the pointer passed to set_occupation() */
3463 func
= ((struct func_tab
*) tlist
)->f_funct
;
3464 if (tlist
->f_text
&& !occupation
&& multi
)
3465 set_occupation(func
, tlist
->f_text
, multi
);
3466 res
= (*func
)(); /* perform the command */
3469 context
.move
= FALSE
;
3474 /* if we reach here, cmd wasn't found in cmdlist[] */
3479 char expcmd
[20]; /* we expect 'cmd' to point to 1 or 2 chars */
3483 while ((c
= *cmd
++) != '\0')
3484 Strcat(expcmd
, visctrl(c
)); /* add 1..4 chars plus terminator */
3486 if (!prefix_seen
|| !iflags
.cmdassist
3487 || !help_dir(0, "Invalid direction key!"))
3488 Norep("Unknown command '%s'.", expcmd
);
3491 context
.move
= FALSE
;
3496 /* convert an x,y pair into a direction code */
3503 for (dd
= 0; dd
< 8; dd
++)
3504 if (x
== xdir
[dd
] && y
== ydir
[dd
])
3509 /* convert a direction code into an x,y pair */
3520 /* also sets u.dz, but returns false for <> */
3525 register const char *dp
= index(Cmd
.dirchars
, sym
);
3530 u
.dx
= xdir
[dp
- Cmd
.dirchars
];
3531 u
.dy
= ydir
[dp
- Cmd
.dirchars
];
3532 u
.dz
= zdir
[dp
- Cmd
.dirchars
];
3533 #if 0 /* now handled elsewhere */
3534 if (u
.dx
&& u
.dy
&& NODIAG(u
.umonnum
)) {
3542 /* grid bug handling which used to be in movecmd() */
3546 if (u
.dx
&& u
.dy
&& NODIAG(u
.umonnum
))
3548 return u
.dx
|| u
.dy
;
3551 /* decide whether a character (user input keystroke) requests screen repaint */
3556 return (boolean
) (c
== C('r') || (Cmd
.num_pad
&& c
== C('l')));
3560 * uses getdir() but unlike getdir() it specifically
3561 * produces coordinates using the direction from getdir()
3562 * and verifies that those coordinates are ok.
3564 * If the call to getdir() returns 0, Never_mind is displayed.
3565 * If the resulting coordinates are not okay, emsg is displayed.
3567 * Returns non-zero if coordinates in cc are valid.
3570 get_adjacent_loc(prompt
, emsg
, x
, y
, cc
)
3571 const char *prompt
, *emsg
;
3576 if (!getdir(prompt
)) {
3582 if (cc
&& isok(new_x
, new_y
)) {
3601 if (in_doagain
|| *readchar_queue
)
3602 dirsym
= readchar();
3604 dirsym
= yn_function((s
&& *s
!= '^') ? s
: "In what direction?",
3606 /* remove the prompt string so caller won't have to */
3607 clear_nhwindow(WIN_MESSAGE
);
3609 if (redraw_cmd(dirsym
)) { /* ^R */
3610 docrt(); /* redraw */
3615 if (dirsym
== '.' || dirsym
== 's') {
3616 u
.dx
= u
.dy
= u
.dz
= 0;
3617 } else if (!(is_mov
= movecmd(dirsym
)) && !u
.dz
) {
3618 boolean did_help
= FALSE
, help_requested
;
3620 if (!index(quitchars
, dirsym
)) {
3621 help_requested
= (dirsym
== '?');
3622 if (help_requested
|| iflags
.cmdassist
) {
3623 did_help
= help_dir((s
&& *s
== '^') ? dirsym
: 0,
3624 help_requested
? (const char *) 0
3625 : "Invalid direction key!");
3630 pline("What a strange direction!");
3633 } else if (is_mov
&& !dxdy_moveok()) {
3634 You_cant("orient yourself that direction.");
3637 if (!u
.dz
&& (Stunned
|| (Confusion
&& !rn2(5))))
3649 static const char wiz_only_list
[] = "EFGIOVW";
3650 char buf
[BUFSZ
], buf2
[BUFSZ
], *explain
;
3652 win
= create_nhwindow(NHW_TEXT
);
3656 Sprintf(buf
, "cmdassist: %s", msg
);
3657 putstr(win
, 0, buf
);
3662 ctrl
= (sym
- 'A') + 1;
3663 if ((explain
= dowhatdoes_core(ctrl
, buf2
))
3664 && (!index(wiz_only_list
, sym
) || wizard
)) {
3665 Sprintf(buf
, "Are you trying to use ^%c%s?", sym
,
3666 index(wiz_only_list
, sym
)
3668 : " as specified in the Guidebook");
3669 putstr(win
, 0, buf
);
3671 putstr(win
, 0, explain
);
3673 putstr(win
, 0, "To use that command, you press");
3674 Sprintf(buf
, "the <Ctrl> key, and the <%c> key at the same time.",
3676 putstr(win
, 0, buf
);
3680 if (NODIAG(u
.umonnum
)) {
3681 putstr(win
, 0, "Valid direction keys in your current form are:");
3682 Sprintf(buf
, " %c ", Cmd
.move_N
);
3683 putstr(win
, 0, buf
);
3684 putstr(win
, 0, " | ");
3685 Sprintf(buf
, " %c- . -%c", Cmd
.move_W
, Cmd
.move_E
);
3686 putstr(win
, 0, buf
);
3687 putstr(win
, 0, " | ");
3688 Sprintf(buf
, " %c ", Cmd
.move_S
);
3689 putstr(win
, 0, buf
);
3691 putstr(win
, 0, "Valid direction keys are:");
3692 Sprintf(buf
, " %c %c %c", Cmd
.move_NW
, Cmd
.move_N
,
3694 putstr(win
, 0, buf
);
3695 putstr(win
, 0, " \\ | / ");
3696 Sprintf(buf
, " %c- . -%c", Cmd
.move_W
, Cmd
.move_E
);
3697 putstr(win
, 0, buf
);
3698 putstr(win
, 0, " / | \\ ");
3699 Sprintf(buf
, " %c %c %c", Cmd
.move_SW
, Cmd
.move_S
,
3701 putstr(win
, 0, buf
);
3704 putstr(win
, 0, " < up");
3705 putstr(win
, 0, " > down");
3706 putstr(win
, 0, " . direct at yourself");
3708 /* non-null msg means that this wasn't an explicit user request */
3711 "(Suppress this message with !cmdassist in config file.)");
3713 display_nhwindow(win
, FALSE
);
3714 destroy_nhwindow(win
);
3721 register int x
= NODIAG(u
.umonnum
) ? 2 * rn2(4) : rn2(8);
3732 static NEARDATA
const char *const dirnames
[] = {
3733 "west", "northwest", "north", "northeast", "east",
3734 "southeast", "south", "southwest", "down", "up",
3737 if (dir
< 0 || dir
>= SIZE(dirnames
))
3739 return dirnames
[dir
];
3746 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
3747 return x
>= 1 && x
<= COLNO
- 1 && y
>= 0 && y
<= ROWNO
- 1;
3750 static NEARDATA
int last_multi
;
3753 * convert a MAP window position into a movecmd
3756 click_to_cmd(x
, y
, mod
)
3763 if (iflags
.clicklook
&& mod
== CLICK_2
) {
3766 cmd
[0] = CMD_CLICKLOOK
;
3773 if (flags
.travelcmd
) {
3774 if (abs(x
) <= 1 && abs(y
) <= 1) {
3775 x
= sgn(x
), y
= sgn(y
);
3779 cmd
[0] = CMD_TRAVEL
;
3783 if (x
== 0 && y
== 0) {
3785 if (IS_FOUNTAIN(levl
[u
.ux
][u
.uy
].typ
)
3786 || IS_SINK(levl
[u
.ux
][u
.uy
].typ
)) {
3787 cmd
[0] = mod
== CLICK_1
? 'q' : M('d');
3789 } else if (IS_THRONE(levl
[u
.ux
][u
.uy
].typ
)) {
3792 } else if ((u
.ux
== xupstair
&& u
.uy
== yupstair
)
3793 || (u
.ux
== sstairs
.sx
&& u
.uy
== sstairs
.sy
3795 || (u
.ux
== xupladder
&& u
.uy
== yupladder
)) {
3797 } else if ((u
.ux
== xdnstair
&& u
.uy
== ydnstair
)
3798 || (u
.ux
== sstairs
.sx
&& u
.uy
== sstairs
.sy
3800 || (u
.ux
== xdnladder
&& u
.uy
== ydnladder
)) {
3802 } else if (OBJ_AT(u
.ux
, u
.uy
)) {
3804 Is_container(level
.objects
[u
.ux
][u
.uy
]) ? M('l') : ',';
3807 return "."; /* just rest */
3811 /* directional commands */
3815 if (!m_at(u
.ux
+ x
, u
.uy
+ y
)
3816 && !test_move(u
.ux
, u
.uy
, x
, y
, TEST_MOVE
)) {
3817 cmd
[1] = Cmd
.dirchars
[dir
];
3819 if (IS_DOOR(levl
[u
.ux
+ x
][u
.uy
+ y
].typ
)) {
3820 /* slight assistance to the player: choose kick/open for them
3822 if (levl
[u
.ux
+ x
][u
.uy
+ y
].doormask
& D_LOCKED
) {
3826 if (levl
[u
.ux
+ x
][u
.uy
+ y
].doormask
& D_CLOSED
) {
3831 if (levl
[u
.ux
+ x
][u
.uy
+ y
].typ
<= SCORR
) {
3838 /* convert without using floating point, allowing sloppy clicking */
3841 else if (y
> 2 * abs(x
))
3843 else if (x
< -2 * abs(y
))
3845 else if (y
< -2 * abs(x
))
3848 x
= sgn(x
), y
= sgn(y
);
3850 if (x
== 0 && y
== 0) /* map click on player to "rest" command */
3856 /* move, attack, etc. */
3858 if (mod
== CLICK_1
) {
3859 cmd
[0] = Cmd
.dirchars
[dir
];
3861 cmd
[0] = (Cmd
.num_pad
3862 ? M(Cmd
.dirchars
[dir
])
3863 : (Cmd
.dirchars
[dir
] - 'a' + 'A')); /* run command */
3870 get_count(allowchars
, inkey
, maxcount
, count
)
3879 boolean backspaced
= FALSE
;
3880 /* this should be done in port code so that we have erase_char
3881 and kill_char available; we can at least fake erase_char */
3882 #define STANDBY_erase_char '\177'
3892 cnt
= 10L * cnt
+ (long) (key
- '0');
3895 else if (maxcount
> 0 && cnt
> maxcount
)
3897 } else if (cnt
&& (key
== '\b' || key
== STANDBY_erase_char
)) {
3900 } else if (key
== '\033') {
3902 } else if (!allowchars
|| index(allowchars
, key
)) {
3907 if (cnt
> 9 || backspaced
) {
3908 clear_nhwindow(WIN_MESSAGE
);
3909 if (backspaced
&& !cnt
) {
3910 Sprintf(qbuf
, "Count: ");
3912 Sprintf(qbuf
, "Count: %ld", cnt
);
3926 #ifdef LINT /* static char in_line[COLNO]; */
3927 char in_line
[COLNO
];
3929 static char in_line
[COLNO
];
3932 boolean prezero
= FALSE
;
3936 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
3939 alt_esc
= iflags
.altmeta
; /* readchar() hack */
3941 if (!Cmd
.num_pad
|| (foo
= readchar()) == 'n') {
3942 long tmpmulti
= multi
;
3944 foo
= get_count(NULL
, '\0', LARGEST_INT
, &tmpmulti
);
3945 last_multi
= multi
= tmpmulti
;
3948 alt_esc
= FALSE
; /* readchar() reset */
3951 if (foo
== '\033') { /* esc cancels count (TH) */
3952 clear_nhwindow(WIN_MESSAGE
);
3953 multi
= last_multi
= 0;
3954 } else if (foo
== DOAGAIN
|| in_doagain
) {
3958 savech(0); /* reset input queue */
3966 save_cm
= (char *) 0;
3968 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
3969 if (Cmd
.pcHack_compat
) {
3970 /* This handles very old inconsistent DOS/Windows behaviour
3971 in a different way: earlier, the keyboard handler mapped
3972 these, which caused counts to be strange when entered
3973 from the number pad. Now do not map them until here. */
3991 if (foo
== 'g' || foo
== 'G' || foo
== 'm' || foo
== 'M' || foo
== 'F'
3992 || (Cmd
.num_pad
&& (foo
== '5' || foo
== '-'))) {
3998 clear_nhwindow(WIN_MESSAGE
);
4000 in_line
[0] = '\033';
4004 #ifdef HANGUPHANDLING
4005 /* some very old systems, or descendents of such systems, expect signal
4006 handlers to have return type `int', but they don't actually inspect
4007 the return value so we should be safe using `void' unconditionally */
4010 hangup(sig_unused
) /* called as signal() handler, so sent at least one arg */
4011 int sig_unused UNUSED
;
4013 if (program_state
.exiting
)
4014 program_state
.in_moveloop
= 0;
4017 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
4018 and a couple of other places; actual hangup handling occurs then.
4019 This is 'safer' because it disallows certain cheats and also
4020 protects against losing objects in the process of being thrown,
4021 but also potentially riskier because the disconnected program
4022 must continue running longer before attempting a hangup save. */
4023 program_state
.done_hup
++;
4024 /* defer hangup iff game appears to be in progress */
4025 if (program_state
.in_moveloop
&& program_state
.something_worth_saving
)
4027 #endif /* SAFERHANGUP */
4034 #ifdef NOSAVEONHANGUP
4036 if (flags
.ins_chkpt
&& program_state
.something_worth_saving
)
4037 program_statue
.preserve_locks
= 1; /* keep files for recovery */
4039 program_state
.something_worth_saving
= 0; /* don't save */
4043 if (!program_state
.done_hup
++)
4045 if (program_state
.something_worth_saving
)
4047 if (iflags
.window_inited
)
4048 exit_nhwindows((char *) 0);
4050 terminate(EXIT_SUCCESS
);
4051 /*NOTREACHED*/ /* not necessarily true for vms... */
4054 #endif /* HANGUPHANDLING */
4060 int x
= u
.ux
, y
= u
.uy
, mod
= 0;
4062 if (*readchar_queue
)
4063 sym
= *readchar_queue
++;
4065 sym
= in_doagain
? pgetchar() : nh_poskey(&x
, &y
, &mod
);
4069 register int cnt
= NR_OF_EOFS
;
4071 * Some SYSV systems seem to return EOFs for various reasons
4072 * (?like when one hits break or for interrupted systemcalls?),
4073 * and we must see several before we quit.
4076 clearerr(stdin
); /* omit if clearerr is undefined */
4078 } while (--cnt
&& sym
== EOF
);
4080 #endif /* NR_OF_EOFS */
4083 #ifdef HANGUPHANDLING
4084 hangup(0); /* call end_of_input() or set program_state.done_hup */
4088 } else if (sym
== '\033' && alt_esc
) {
4089 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
4090 sym
= *readchar_queue
? *readchar_queue
++ : pgetchar();
4091 if (sym
== EOF
|| sym
== 0)
4093 else if (sym
!= '\033')
4094 sym
|= 0200; /* force 8th bit on */
4096 } else if (sym
== 0) {
4098 readchar_queue
= click_to_cmd(x
, y
, mod
);
4099 sym
= *readchar_queue
++;
4107 /* Keyboard travel command */
4111 if (!flags
.travelcmd
)
4114 cc
.x
= iflags
.travelcc
.x
;
4115 cc
.y
= iflags
.travelcc
.y
;
4116 if (cc
.x
== -1 && cc
.y
== -1) {
4117 /* No cached destination, start attempt from current position */
4121 pline("Where do you want to travel to?");
4122 if (getpos(&cc
, TRUE
, "the desired destination") < 0) {
4123 /* user pressed ESC */
4126 iflags
.travelcc
.x
= u
.tx
= cc
.x
;
4127 iflags
.travelcc
.y
= u
.ty
= cc
.y
;
4128 cmd
[0] = CMD_TRAVEL
;
4129 readchar_queue
= cmd
;
4134 extern void NDECL(win32con_debug_keystrokes
);
4135 extern void NDECL(win32con_handler_info
);
4144 int num_menu_selections
;
4145 struct menu_selection_struct
{
4148 } menu_selections
[] = {
4150 { "test win32 keystrokes (tty only)", win32con_debug_keystrokes
},
4151 { "show keystroke handler information (tty only)",
4152 win32con_handler_info
},
4154 { (char *) 0, (void NDECL((*) )) 0 } /* array terminator */
4157 num_menu_selections
= SIZE(menu_selections
) - 1;
4158 if (num_menu_selections
> 0) {
4159 menu_item
*pick_list
;
4160 win
= create_nhwindow(NHW_MENU
);
4162 for (k
= 0; k
< num_menu_selections
; ++k
) {
4164 add_menu(win
, NO_GLYPH
, &any
, item
++, 0, ATR_NONE
,
4165 menu_selections
[k
].menutext
, MENU_UNSELECTED
);
4167 end_menu(win
, "Which port debugging feature?");
4168 n
= select_menu(win
, PICK_ONE
, &pick_list
);
4169 destroy_nhwindow(win
);
4171 n
= pick_list
[0].item
.a_int
- 1;
4172 free((genericptr_t
) pick_list
);
4173 /* execute the function */
4174 (*menu_selections
[n
].fn
)();
4177 pline("No port-specific debug capability defined.");
4180 #endif /*PORT_DEBUG*/
4183 * Parameter validator for generic yes/no function to prevent
4184 * the core from sending too long a prompt string to the
4185 * window port causing a buffer overflow there.
4188 yn_function(query
, resp
, def
)
4189 const char *query
, *resp
;
4194 iflags
.last_msg
= PLNMSG_UNKNOWN
; /* most recent pline is clobbered */
4196 /* maximum acceptable length is QBUFSZ-1 */
4197 if (strlen(query
) >= QBUFSZ
) {
4198 /* caller shouldn't have passed anything this long */
4199 paniclog("Query truncated: ", query
);
4200 (void) strncpy(qbuf
, query
, QBUFSZ
- 1 - 3);
4201 Strcpy(&qbuf
[QBUFSZ
- 1 - 3], "...");
4204 return (*windowprocs
.win_yn_function
)(query
, resp
, def
);
4207 /* for paranoid_confirm:quit,die,attack prompting */
4209 paranoid_query(be_paranoid
, prompt
)
4210 boolean be_paranoid
;
4213 boolean confirmed_ok
;
4215 /* when paranoid, player must respond with "yes" rather than just 'y'
4216 to give the go-ahead for this query; default is "no" unless the
4217 ParanoidConfirm flag is set in which case there's no default */
4219 char qbuf
[QBUFSZ
], ans
[BUFSZ
];
4220 const char *promptprefix
= "", *responsetype
= ParanoidConfirm
4223 int trylimit
= 6; /* 1 normal, 5 more with "Yes or No:" prefix */
4225 /* in addition to being paranoid about this particular
4226 query, we might be even more paranoid about all paranoia
4227 responses (ie, ParanoidConfirm is set) in which case we
4228 require "no" to reject in addition to "yes" to confirm
4229 (except we won't loop if response is ESC; it means no) */
4231 Sprintf(qbuf
, "%s%s %s", promptprefix
, prompt
, responsetype
);
4233 (void) mungspaces(ans
);
4234 confirmed_ok
= !strcmpi(ans
, "yes");
4235 if (confirmed_ok
|| *ans
== '\033')
4237 promptprefix
= "\"Yes\" or \"No\": ";
4238 } while (ParanoidConfirm
&& strcmpi(ans
, "no") && --trylimit
);
4240 confirmed_ok
= (yn(prompt
) == 'y');
4242 return confirmed_ok
;
4249 /* Does current window system support suspend? */
4250 if ((*windowprocs
.win_can_suspend
)()) {
4251 /* NB: SYSCF SHELLERS handled in port code. */
4255 Norep("Suspend command not available.");