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
*, BOOLEAN_P
));
148 STATIC_DCL
int FDECL(size_obj
, (struct obj
*));
149 STATIC_DCL
void FDECL(count_obj
, (struct obj
*, long *, long *,
150 BOOLEAN_P
, BOOLEAN_P
));
151 STATIC_DCL
void FDECL(obj_chain
, (winid
, const char *, struct obj
*,
152 BOOLEAN_P
, long *, long *));
153 STATIC_DCL
void FDECL(mon_invent_chain
, (winid
, const char *, struct monst
*,
155 STATIC_DCL
void FDECL(mon_chain
, (winid
, const char *, struct monst
*,
156 BOOLEAN_P
, long *, long *));
157 STATIC_DCL
void FDECL(contained_stats
, (winid
, const char *, long *, long *));
158 STATIC_DCL
void FDECL(misc_stats
, (winid
, long *, long *));
159 STATIC_PTR
int NDECL(wiz_show_stats
);
160 STATIC_DCL boolean
FDECL(accept_menu_prefix
, (int NDECL((*))));
162 STATIC_DCL
int NDECL(wiz_port_debug
);
164 STATIC_PTR
int NDECL(wiz_rumor_check
);
165 STATIC_DCL
char FDECL(cmd_from_func
, (int NDECL((*))));
166 STATIC_PTR
int NDECL(doattributes
);
167 STATIC_PTR
int NDECL(doconduct
); /**/
169 STATIC_DCL
void FDECL(enlght_line
, (const char *, const char *, const char *,
171 STATIC_DCL
char *FDECL(enlght_combatinc
, (const char *, int, int, char *));
172 STATIC_DCL
void FDECL(enlght_halfdmg
, (int, int));
173 STATIC_DCL boolean
NDECL(walking_on_water
);
174 STATIC_DCL boolean
FDECL(cause_known
, (int));
175 STATIC_DCL
char *FDECL(attrval
, (int, int, char *));
176 STATIC_DCL
void FDECL(background_enlightenment
, (int, int));
177 STATIC_DCL
void FDECL(characteristics_enlightenment
, (int, int));
178 STATIC_DCL
void FDECL(one_characteristic
, (int, int, int));
179 STATIC_DCL
void FDECL(status_enlightenment
, (int, int));
180 STATIC_DCL
void FDECL(attributes_enlightenment
, (int, int));
182 static const char *readchar_queue
= "";
183 static coord clicklook_cc
;
185 STATIC_DCL
char *NDECL(parse
);
186 STATIC_DCL
void FDECL(show_direction_keys
, (winid
, BOOLEAN_P
));
187 STATIC_DCL boolean
FDECL(help_dir
, (CHAR_P
, const char *));
190 doprev_message(VOID_ARGS
)
192 return nh_doprev_message();
195 /* Count down by decrementing multi */
197 timed_occupation(VOID_ARGS
)
205 /* If you have moved since initially setting some occupations, they
206 * now shouldn't be able to restart.
208 * The basic rule is that if you are carrying it, you can continue
209 * since it is with you. If you are acting on something at a distance,
210 * your orientation to it must have changed when you moved.
212 * The exception to this is taking off items, since they can be taken
213 * off in a number of ways in the intervening time, screwing up ordering.
215 * Currently: Take off all armor.
216 * Picking Locks / Forcing Chests.
227 /* If a time is given, use it to timeout this function, otherwise the
228 * function times out by its own means.
231 set_occupation(fn
, txt
, xtime
)
237 occupation
= timed_occupation
;
246 STATIC_DCL
char NDECL(popch
);
248 /* Provide a means to redo the last command. The flag `in_doagain' is set
249 * to true while redoing the command. This flag is tested in commands that
250 * require additional input (like `throw' which requires a thing and a
251 * direction), and the input prompt is not shown. Also, while in_doagain is
252 * TRUE, no keystrokes can be saved into the saveq.
255 static char pushq
[BSIZE
], saveq
[BSIZE
];
256 static NEARDATA
int phead
, ptail
, shead
, stail
;
261 /* If occupied, return '\0', letting tgetch know a character should
262 * be read from the keyboard. If the character read is not the
263 * ABORT character (as checked in pcmain.c), that character will be
264 * pushed back on the pushq.
269 return (char) ((shead
!= stail
) ? saveq
[stail
++] : '\0');
271 return (char) ((phead
!= ptail
) ? pushq
[ptail
++] : '\0');
275 pgetchar() /* courtesy of aeb@cwi.nl */
284 /* A ch == 0 resets the pushq */
296 /* A ch == 0 resets the saveq. Only save keystrokes when not
297 * replaying a previous command.
305 phead
= ptail
= shead
= stail
= 0;
306 else if (shead
< BSIZE
)
312 /* here after # - now read a full-word command */
319 /* keep repeating until we don't run help or quit */
325 func
= extcmdlist
[idx
].ef_funct
;
326 if (iflags
.menu_requested
&& !accept_menu_prefix(func
)) {
327 pline("'m' prefix has no effect for this command.");
328 iflags
.menu_requested
= FALSE
;
331 } while (func
== doextlist
);
336 /* here after #? - now list all full-word commands */
340 register const struct ext_func_tab
*efp
;
344 datawin
= create_nhwindow(NHW_TEXT
);
345 putstr(datawin
, 0, "");
346 putstr(datawin
, 0, " Extended Commands List");
347 putstr(datawin
, 0, "");
348 putstr(datawin
, 0, " Press '#', then type:");
349 putstr(datawin
, 0, "");
351 for (efp
= extcmdlist
; efp
->ef_txt
; efp
++) {
352 Sprintf(buf
, " %-15s - %s.", efp
->ef_txt
, efp
->ef_desc
);
353 putstr(datawin
, 0, buf
);
355 display_nhwindow(datawin
, FALSE
);
356 destroy_nhwindow(datawin
);
361 #define MAX_EXT_CMD 50 /* Change if we ever have > 50 ext cmds */
364 * This is currently used only by the tty port and is
365 * controlled via runtime option 'extmenu'.
366 * ``# ?'' is counted towards the limit of the number of commands,
367 * so we actually support MAX_EXT_CMD-1 "real" extended commands.
369 * Here after # - now show pick-list of possible commands.
374 const struct ext_func_tab
*efp
;
375 menu_item
*pick_list
= (menu_item
*) 0;
378 const struct ext_func_tab
*choices
[MAX_EXT_CMD
+ 1];
380 char cbuf
[QBUFSZ
], prompt
[QBUFSZ
], fmtstr
[20];
381 int i
, n
, nchoices
, acount
;
383 int accelerator
, prevaccelerator
;
392 /* populate choices */
393 for (efp
= extcmdlist
; efp
->ef_txt
; efp
++) {
394 if (!matchlevel
|| !strncmp(efp
->ef_txt
, cbuf
, matchlevel
)) {
396 if ((int) strlen(efp
->ef_desc
) > biggest
) {
397 biggest
= strlen(efp
->ef_desc
);
398 Sprintf(fmtstr
, "%%-%ds", biggest
+ 15);
400 if (++i
> MAX_EXT_CMD
) {
403 "Exceeded %d extended commands in doextcmd() menu; 'extmenu' disabled.",
411 choices
[i
] = (struct ext_func_tab
*) 0;
413 /* if we're down to one, we have our selection so get out of here */
415 for (i
= 0; extcmdlist
[i
].ef_txt
!= (char *) 0; i
++)
416 if (!strncmpi(extcmdlist
[i
].ef_txt
, cbuf
, matchlevel
)) {
424 win
= create_nhwindow(NHW_MENU
);
426 accelerator
= prevaccelerator
= 0;
428 for (i
= 0; choices
[i
]; ++i
) {
429 accelerator
= choices
[i
]->ef_txt
[matchlevel
];
430 if (accelerator
!= prevaccelerator
|| nchoices
< (ROWNO
- 3)) {
432 /* flush extended cmds for that letter already in buf */
433 Sprintf(buf
, fmtstr
, prompt
);
434 any
.a_char
= prevaccelerator
;
435 add_menu(win
, NO_GLYPH
, &any
, any
.a_char
, 0, ATR_NONE
,
440 prevaccelerator
= accelerator
;
441 if (!acount
|| nchoices
< (ROWNO
- 3)) {
442 Sprintf(prompt
, "%s [%s]", choices
[i
]->ef_txt
,
443 choices
[i
]->ef_desc
);
444 } else if (acount
== 1) {
445 Sprintf(prompt
, "%s or %s", choices
[i
- 1]->ef_txt
,
448 Strcat(prompt
, " or ");
449 Strcat(prompt
, choices
[i
]->ef_txt
);
455 Sprintf(buf
, fmtstr
, prompt
);
456 any
.a_char
= prevaccelerator
;
457 add_menu(win
, NO_GLYPH
, &any
, any
.a_char
, 0, ATR_NONE
, buf
,
460 Sprintf(prompt
, "Extended Command: %s", cbuf
);
461 end_menu(win
, prompt
);
462 n
= select_menu(win
, PICK_ONE
, &pick_list
);
463 destroy_nhwindow(win
);
465 if (matchlevel
> (QBUFSZ
- 2)) {
466 free((genericptr_t
) pick_list
);
468 impossible("Too many chars (%d) entered in extcmd_via_menu()",
473 cbuf
[matchlevel
++] = pick_list
[0].item
.a_char
;
474 cbuf
[matchlevel
] = '\0';
475 free((genericptr_t
) pick_list
);
487 #endif /* TTY_GRAPHICS */
489 /* #monster command - use special monster ability while polymorphed */
491 domonability(VOID_ARGS
)
493 if (can_breathe(youmonst
.data
))
495 else if (attacktype(youmonst
.data
, AT_SPIT
))
497 else if (youmonst
.data
->mlet
== S_NYMPH
)
499 else if (attacktype(youmonst
.data
, AT_GAZE
))
501 else if (is_were(youmonst
.data
))
503 else if (webmaker(youmonst
.data
))
505 else if (is_hider(youmonst
.data
))
507 else if (is_mind_flayer(youmonst
.data
))
508 return domindblast();
509 else if (u
.umonnum
== PM_GREMLIN
) {
510 if (IS_FOUNTAIN(levl
[u
.ux
][u
.uy
].typ
)) {
511 if (split_mon(&youmonst
, (struct monst
*) 0))
512 dryup(u
.ux
, u
.uy
, TRUE
);
514 There("is no fountain here.");
515 } else if (is_unicorn(youmonst
.data
)) {
516 use_unicorn_horn((struct obj
*) 0);
518 } else if (youmonst
.data
->msound
== MS_SHRIEK
) {
521 pline("Unfortunately sound does not carry well through rock.");
524 } else if (youmonst
.data
->mlet
== S_VAMPIRE
)
527 pline("Any special ability you may have is purely reflexive.");
529 You("don't have a special ability in your normal form!");
534 enter_explore_mode(VOID_ARGS
)
537 You("are in debug mode.");
538 } else if (discover
) {
539 You("are already in explore mode.");
543 if (!sysopt
.explorers
|| !sysopt
.explorers
[0]
544 || !check_user_string(sysopt
.explorers
)) {
545 You("cannot access explore mode.");
551 "Beware! From explore mode there will be no return to normal game.");
552 if (paranoid_query(ParanoidQuit
,
553 "Do you want to enter explore mode?")) {
554 clear_nhwindow(WIN_MESSAGE
);
555 You("are now in non-scoring explore mode.");
558 clear_nhwindow(WIN_MESSAGE
);
559 pline("Resuming normal game.");
566 dooverview_or_wiz_where(VOID_ARGS
)
575 /* ^W command - wish for something */
577 wiz_wish(VOID_ARGS
) /* Unlimited wishes for debug mode by Paul Polderman */
580 boolean save_verbose
= flags
.verbose
;
582 flags
.verbose
= FALSE
;
584 flags
.verbose
= save_verbose
;
585 (void) encumber_msg();
587 pline("Unavailable command '%s'.",
588 visctrl((int) cmd_from_func(wiz_wish
)));
592 /* ^I command - reveal and optionally identify hero's inventory */
594 wiz_identify(VOID_ARGS
)
597 iflags
.override_ID
= (int) cmd_from_func(wiz_identify
);
598 if (display_inventory((char *) 0, TRUE
) == -1)
599 identify_pack(0, FALSE
);
600 iflags
.override_ID
= 0;
602 pline("Unavailable command '%s'.",
603 visctrl((int) cmd_from_func(wiz_identify
)));
607 /* ^F command - reveal the level map and any traps on it */
613 long save_Hconf
= HConfusion
, save_Hhallu
= HHallucination
;
615 HConfusion
= HHallucination
= 0L;
616 for (t
= ftrap
; t
!= 0; t
= t
->ntrap
) {
621 HConfusion
= save_Hconf
;
622 HHallucination
= save_Hhallu
;
624 pline("Unavailable command '%s'.",
625 visctrl((int) cmd_from_func(wiz_map
)));
629 /* ^G command - generate monster(s); a count prefix will be honored */
631 wiz_genesis(VOID_ARGS
)
634 (void) create_particular();
636 pline("Unavailable command '%s'.",
637 visctrl((int) cmd_from_func(wiz_genesis
)));
641 /* ^O command - display dungeon layout */
646 (void) print_dungeon(FALSE
, (schar
*) 0, (xchar
*) 0);
648 pline("Unavailable command '%s'.",
649 visctrl((int) cmd_from_func(wiz_where
)));
653 /* ^E command - detect unseen (secret doors, traps, hidden monsters) */
655 wiz_detect(VOID_ARGS
)
660 pline("Unavailable command '%s'.",
661 visctrl((int) cmd_from_func(wiz_detect
)));
665 /* ^V command - level teleport */
667 wiz_level_tele(VOID_ARGS
)
672 pline("Unavailable command '%s'.",
673 visctrl((int) cmd_from_func(wiz_level_tele
)));
677 /* #monpolycontrol command - choose new form for shapechangers, polymorphees */
679 wiz_mon_polycontrol(VOID_ARGS
)
681 iflags
.mon_polycontrol
= !iflags
.mon_polycontrol
;
682 pline("Monster polymorph control is %s.",
683 iflags
.mon_polycontrol
? "on" : "off");
687 /* #levelchange command - adjust hero's experience level */
689 wiz_level_change(VOID_ARGS
)
695 getlin("To what experience level do you want to be set?", buf
);
696 (void) mungspaces(buf
);
697 if (buf
[0] == '\033' || buf
[0] == '\0')
700 ret
= sscanf(buf
, "%d", &newlevel
);
706 if (newlevel
== u
.ulevel
) {
707 You("are already that experienced.");
708 } else if (newlevel
< u
.ulevel
) {
710 You("are already as inexperienced as you can get.");
715 while (u
.ulevel
> newlevel
)
716 losexp("#levelchange");
718 if (u
.ulevel
>= MAXULEV
) {
719 You("are already as experienced as you can get.");
722 if (newlevel
> MAXULEV
)
724 while (u
.ulevel
< newlevel
)
727 u
.ulevelmax
= u
.ulevel
;
731 /* #panic command - test program's panic handling */
735 if (yn("Do you want to call panic() and end your game?") == 'y')
736 panic("Crash test.");
740 /* #polyself command - change hero's form */
742 wiz_polyself(VOID_ARGS
)
750 wiz_show_seenv(VOID_ARGS
)
753 int x
, y
, v
, startx
, stopx
, curx
;
756 win
= create_nhwindow(NHW_TEXT
);
758 * Each seenv description takes up 2 characters, so center
759 * the seenv display around the hero.
761 startx
= max(1, u
.ux
- (COLNO
/ 4));
762 stopx
= min(startx
+ (COLNO
/ 2), COLNO
);
763 /* can't have a line exactly 80 chars long */
764 if (stopx
- startx
== COLNO
/ 2)
767 for (y
= 0; y
< ROWNO
; y
++) {
768 for (x
= startx
, curx
= 0; x
< stopx
; x
++, curx
+= 2) {
769 if (x
== u
.ux
&& y
== u
.uy
) {
770 row
[curx
] = row
[curx
+ 1] = '@';
772 v
= levl
[x
][y
].seenv
& 0xff;
774 row
[curx
] = row
[curx
+ 1] = ' ';
776 Sprintf(&row
[curx
], "%02x", v
);
779 /* remove trailing spaces */
780 for (x
= curx
- 1; x
>= 0; x
--)
787 display_nhwindow(win
, TRUE
);
788 destroy_nhwindow(win
);
792 /* #vision command */
794 wiz_show_vision(VOID_ARGS
)
800 win
= create_nhwindow(NHW_TEXT
);
801 Sprintf(row
, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
802 COULD_SEE
, IN_SIGHT
, TEMP_LIT
);
805 for (y
= 0; y
< ROWNO
; y
++) {
806 for (x
= 1; x
< COLNO
; x
++) {
807 if (x
== u
.ux
&& y
== u
.uy
)
810 v
= viz_array
[y
][x
]; /* data access should be hidden */
814 row
[x
] = '0' + viz_array
[y
][x
];
817 /* remove trailing spaces */
818 for (x
= COLNO
- 1; x
>= 1; x
--)
823 putstr(win
, 0, &row
[1]);
825 display_nhwindow(win
, TRUE
);
826 destroy_nhwindow(win
);
832 wiz_show_wmodes(VOID_ARGS
)
838 boolean istty
= !strcmp(windowprocs
.name
, "tty");
840 win
= create_nhwindow(NHW_TEXT
);
842 putstr(win
, 0, ""); /* tty only: blank top line */
843 for (y
= 0; y
< ROWNO
; y
++) {
844 for (x
= 0; x
< COLNO
; x
++) {
846 if (x
== u
.ux
&& y
== u
.uy
)
848 else if (IS_WALL(lev
->typ
) || lev
->typ
== SDOOR
)
849 row
[x
] = '0' + (lev
->wall_info
& WM_MASK
);
850 else if (lev
->typ
== CORR
)
852 else if (IS_ROOM(lev
->typ
) || IS_DOOR(lev
->typ
))
858 /* map column 0, levl[0][], is off the left edge of the screen */
859 putstr(win
, 0, &row
[1]);
861 display_nhwindow(win
, TRUE
);
862 destroy_nhwindow(win
);
866 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
868 wiz_map_levltyp(VOID_ARGS
)
873 boolean istty
= !strcmp(windowprocs
.name
, "tty");
875 win
= create_nhwindow(NHW_TEXT
);
876 /* map row 0, levl[][0], is drawn on the second line of tty screen */
878 putstr(win
, 0, ""); /* tty only: blank top line */
879 for (y
= 0; y
< ROWNO
; y
++) {
880 /* map column 0, levl[0][], is off the left edge of the screen;
881 it should always have terrain type "undiggable stone" */
882 for (x
= 1; x
< COLNO
; x
++) {
883 terrain
= levl
[x
][y
].typ
;
884 /* assumes there aren't more than 10+26+26 terrain types */
885 row
[x
- 1] = (char) ((terrain
== 0 && !may_dig(x
, y
))
891 : 'A' + terrain
- 36);
893 if (levl
[0][y
].typ
!= 0 || may_dig(0, y
))
901 s_level
*slev
= Is_special(&u
.uz
);
903 Sprintf(dsc
, "D:%d,L:%d", u
.uz
.dnum
, u
.uz
.dlevel
);
904 /* [dungeon branch features currently omitted] */
905 /* special level features */
907 Sprintf(eos(dsc
), " \"%s\"", slev
->proto
);
908 /* special level flags (note: dungeon.def doesn't set `maze'
909 or `hell' for any specific levels so those never show up) */
910 if (slev
->flags
.maze_like
)
911 Strcat(dsc
, " mazelike");
912 if (slev
->flags
.hellish
)
913 Strcat(dsc
, " hellish");
914 if (slev
->flags
.town
)
915 Strcat(dsc
, " town");
916 if (slev
->flags
.rogue_like
)
917 Strcat(dsc
, " roguelike");
918 /* alignment currently omitted to save space */
921 if (level
.flags
.nfountains
)
922 Sprintf(eos(dsc
), " %c:%d", defsyms
[S_fountain
].sym
,
923 (int) level
.flags
.nfountains
);
924 if (level
.flags
.nsinks
)
925 Sprintf(eos(dsc
), " %c:%d", defsyms
[S_sink
].sym
,
926 (int) level
.flags
.nsinks
);
927 if (level
.flags
.has_vault
)
928 Strcat(dsc
, " vault");
929 if (level
.flags
.has_shop
)
930 Strcat(dsc
, " shop");
931 if (level
.flags
.has_temple
)
932 Strcat(dsc
, " temple");
933 if (level
.flags
.has_court
)
934 Strcat(dsc
, " throne");
935 if (level
.flags
.has_zoo
)
937 if (level
.flags
.has_morgue
)
938 Strcat(dsc
, " morgue");
939 if (level
.flags
.has_barracks
)
940 Strcat(dsc
, " barracks");
941 if (level
.flags
.has_beehive
)
942 Strcat(dsc
, " hive");
943 if (level
.flags
.has_swamp
)
944 Strcat(dsc
, " swamp");
946 if (level
.flags
.noteleport
)
947 Strcat(dsc
, " noTport");
948 if (level
.flags
.hardfloor
)
949 Strcat(dsc
, " noDig");
950 if (level
.flags
.nommap
)
951 Strcat(dsc
, " noMMap");
952 if (!level
.flags
.hero_memory
)
953 Strcat(dsc
, " noMem");
954 if (level
.flags
.shortsighted
)
955 Strcat(dsc
, " shortsight");
956 if (level
.flags
.graveyard
)
957 Strcat(dsc
, " graveyard");
958 if (level
.flags
.is_maze_lev
)
959 Strcat(dsc
, " maze");
960 if (level
.flags
.is_cavernous_lev
)
961 Strcat(dsc
, " cave");
962 if (level
.flags
.arboreal
)
963 Strcat(dsc
, " tree");
965 Strcat(dsc
, " sokoban-rules");
966 /* non-flag info; probably should include dungeon branching
967 checks (extra stairs and magic portals) here */
968 if (Invocation_lev(&u
.uz
))
969 Strcat(dsc
, " invoke");
970 if (On_W_tower_level(&u
.uz
))
971 Strcat(dsc
, " tower");
972 /* append a branch identifier for completeness' sake */
974 Strcat(dsc
, " dungeon");
975 else if (u
.uz
.dnum
== mines_dnum
)
976 Strcat(dsc
, " mines");
977 else if (In_sokoban(&u
.uz
))
978 Strcat(dsc
, " sokoban");
979 else if (u
.uz
.dnum
== quest_dnum
)
980 Strcat(dsc
, " quest");
981 else if (Is_knox(&u
.uz
))
982 Strcat(dsc
, " ludios");
983 else if (u
.uz
.dnum
== 1)
984 Strcat(dsc
, " gehennom");
985 else if (u
.uz
.dnum
== tower_dnum
)
986 Strcat(dsc
, " vlad");
987 else if (In_endgame(&u
.uz
))
988 Strcat(dsc
, " endgame");
990 /* somebody's added a dungeon branch we're not expecting */
991 const char *brname
= dungeons
[u
.uz
.dnum
].dname
;
993 if (!brname
|| !*brname
)
995 if (!strncmpi(brname
, "the ", 4))
997 Sprintf(eos(dsc
), " %s", brname
);
999 /* limit the line length to map width */
1000 if (strlen(dsc
) >= COLNO
)
1001 dsc
[COLNO
- 1] = '\0'; /* truncate */
1002 putstr(win
, 0, dsc
);
1005 display_nhwindow(win
, TRUE
);
1006 destroy_nhwindow(win
);
1010 /* temporary? hack, since level type codes aren't the same as screen
1011 symbols and only the latter have easily accessible descriptions */
1012 static const char *levltyp
[] = {
1013 "stone", "vertical wall", "horizontal wall", "top-left corner wall",
1014 "top-right corner wall", "bottom-left corner wall",
1015 "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall",
1016 "tee-left wall", "tee-right wall", "drawbridge wall", "tree",
1017 "secret door", "secret corridor", "pool", "moat", "water",
1018 "drawbridge up", "lava pool", "iron bars", "door", "corridor", "room",
1019 "stairs", "ladder", "fountain", "throne", "sink", "grave", "altar", "ice",
1020 "drawbridge down", "air", "cloud",
1021 /* not a real terrain type, but used for undiggable stone
1022 by wiz_map_levltyp() */
1023 "unreachable/undiggable",
1024 /* padding in case the number of entries above is odd */
1028 /* explanation of base-36 output from wiz_map_levltyp() */
1030 wiz_levltyp_legend(VOID_ARGS
)
1034 const char *dsc
, *fmt
;
1037 win
= create_nhwindow(NHW_TEXT
);
1038 putstr(win
, 0, "#terrain encodings:");
1040 fmt
= " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
1042 /* output in pairs, left hand column holds [0],[1],...,[N/2-1]
1043 and right hand column holds [N/2],[N/2+1],...,[N-1];
1044 N ('last') will always be even, and may or may not include
1045 the empty string entry to pad out the final pair, depending
1046 upon how many other entries are present in levltyp[] */
1047 last
= SIZE(levltyp
) & ~1;
1048 for (i
= 0; i
< last
/ 2; ++i
)
1049 for (j
= i
; j
< last
; j
+= last
/ 2) {
1052 : !strncmp(dsc
, "unreachable", 11) ? '*'
1053 /* same int-to-char conversion as wiz_map_levltyp() */
1054 : (j
< 10) ? '0' + j
1055 : (j
< 36) ? 'a' + j
- 10
1057 Sprintf(eos(buf
), fmt
, c
, dsc
);
1059 putstr(win
, 0, buf
);
1063 display_nhwindow(win
, TRUE
);
1064 destroy_nhwindow(win
);
1068 /* #wizsmell command - test usmellmon(). */
1070 wiz_smell(VOID_ARGS
)
1073 int mndx
; /* monster index */
1074 coord cc
; /* screen pos of unknown glyph */
1075 int glyph
; /* glyph at selected position */
1079 mndx
= 0; /* gcc -Wall lint */
1080 if (!olfaction(youmonst
.data
)) {
1081 You("are incapable of detecting odors in your present form.");
1085 pline("You can move the cursor to a monster that you want to smell.");
1087 pline("Pick a monster to smell.");
1088 ans
= getpos(&cc
, TRUE
, "a monster");
1089 if (ans
< 0 || cc
.x
< 0) {
1090 return 0; /* done */
1092 /* Convert the glyph at the selected position to a mndxbol. */
1093 glyph
= glyph_at(cc
.x
, cc
.y
);
1094 if (glyph_is_monster(glyph
))
1095 mndx
= glyph_to_mon(glyph
);
1098 /* Is it a monster? */
1100 if (!usmellmon(&mons
[mndx
]))
1101 pline("That monster seems to give off no smell.");
1103 pline("That is not a monster.");
1108 /* #wizinstrinsic command to set some intrinsics for testing */
1110 wiz_intrinsic(VOID_ARGS
)
1115 int i
, n
, accelerator
;
1116 menu_item
*pick_list
= (menu_item
*) 0;
1118 static const char *const intrinsics
[] = {
1122 win
= create_nhwindow(NHW_MENU
);
1126 for (i
= 0; i
< SIZE(intrinsics
); ++i
) {
1127 accelerator
= intrinsics
[i
][0];
1129 add_menu(win
, NO_GLYPH
, &any
, accelerator
, 0,
1130 ATR_NONE
, intrinsics
[i
], FALSE
);
1132 end_menu(win
, "Which intrinsic?");
1133 n
= select_menu(win
, PICK_ONE
, &pick_list
);
1134 destroy_nhwindow(win
);
1137 i
= pick_list
[0].item
.a_int
-1;
1138 free((genericptr_t
) pick_list
);
1143 if (!strcmp(intrinsics
[i
], "deafness")) {
1145 incr_itimeout(&HDeaf
, 30);
1146 context
.botl
= TRUE
;
1149 pline("Unavailable command '%s'.",
1150 visctrl((int) cmd_from_func(wiz_intrinsic
)));
1154 /* #wizrumorcheck command - verify each rumor access */
1156 wiz_rumor_check(VOID_ARGS
)
1162 /* #terrain command -- show known map, inspired by crawl's '|' command */
1164 doterrain(VOID_ARGS
)
1173 * normal play: choose between known map without mons, obj, and traps
1174 * (to see underlying terrain only), or
1175 * known map without mons and objs (to see traps under mons and objs), or
1176 * known map without mons (to see objects under monsters);
1177 * explore mode: normal choices plus full map (w/o mons, objs, traps);
1178 * wizard mode: normal and explore choices plus
1179 * a dump of the internal levl[][].typ codes w/ level flags, or
1180 * a legend for the levl[][].typ codes dump
1182 men
= create_nhwindow(NHW_MENU
);
1186 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1187 "known map without monsters, objects, and traps",
1190 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1191 "known map without monsters and objects",
1194 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1195 "known map without monsters",
1197 if (discover
|| wizard
) {
1199 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1200 "full map without monsters, objects, and traps",
1204 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1205 "internal levl[][].typ codes in base-36",
1208 add_menu(men
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1209 "legend of base-36 levl[][].typ codes",
1213 end_menu(men
, "View which?");
1215 n
= select_menu(men
, PICK_ONE
, &sel
);
1216 destroy_nhwindow(men
);
1218 * n < 0: player used ESC to cancel;
1219 * n == 0: preselected entry was explicitly chosen and got toggled off;
1220 * n == 1: preselected entry was implicitly chosen via <space>|<enter>;
1221 * n == 2: another entry was explicitly chosen, so skip preselected one.
1223 which
= (n
< 0) ? -1 : (n
== 0) ? 1 : sel
[0].item
.a_int
;
1224 if (n
> 1 && which
== 1)
1225 which
= sel
[1].item
.a_int
;
1227 free((genericptr_t
) sel
);
1230 case 1: /* known map */
1231 reveal_terrain(0, TER_MAP
);
1233 case 2: /* known map with known traps */
1234 reveal_terrain(0, TER_MAP
| TER_TRP
);
1236 case 3: /* known map with known traps and objects */
1237 reveal_terrain(0, TER_MAP
| TER_TRP
| TER_OBJ
);
1239 case 4: /* full map */
1240 reveal_terrain(1, TER_MAP
);
1242 case 5: /* map internals */
1245 case 6: /* internal details */
1246 wiz_levltyp_legend();
1251 return 0; /* no time elapses */
1254 /* -enlightenment and conduct- */
1255 static winid en_win
= WIN_ERR
;
1256 static const char You_
[] = "You ", are
[] = "are ", were
[] = "were ",
1257 have
[] = "have ", had
[] = "had ", can
[] = "can ",
1259 static const char have_been
[] = "have been ", have_never
[] = "have never ",
1262 #define enl_msg(prefix, present, past, suffix, ps) \
1263 enlght_line(prefix, final ? past : present, suffix, ps)
1264 #define you_are(attr, ps) enl_msg(You_, are, were, attr, ps)
1265 #define you_have(attr, ps) enl_msg(You_, have, had, attr, ps)
1266 #define you_can(attr, ps) enl_msg(You_, can, could, attr, ps)
1267 #define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "")
1268 #define you_have_never(badthing) \
1269 enl_msg(You_, have_never, never, badthing, "")
1270 #define you_have_X(something) \
1271 enl_msg(You_, have, (const char *) "", something, "")
1274 enlght_line(start
, middle
, end
, ps
)
1275 const char *start
, *middle
, *end
, *ps
;
1279 Sprintf(buf
, " %s%s%s%s.", start
, middle
, end
, ps
);
1280 putstr(en_win
, 0, buf
);
1283 /* format increased chance to hit or damage or defense (Protection) */
1285 enlght_combatinc(inctyp
, incamt
, final
, outbuf
)
1290 const char *modif
, *bonus
;
1294 absamt
= abs(incamt
);
1295 /* Protection amount is typically larger than damage or to-hit;
1296 reduce magnitude by a third in order to stretch modifier ranges
1297 (small:1..5, moderate:6..10, large:11..19, huge:20+) */
1298 if (!strcmp(inctyp
, "defense"))
1299 absamt
= (absamt
* 2) / 3;
1303 else if (absamt
<= 6)
1305 else if (absamt
<= 12)
1310 modif
= !incamt
? "no" : an(modif
); /* ("no" case shouldn't happen) */
1311 bonus
= (incamt
>= 0) ? "bonus" : "penalty";
1312 /* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
1313 invrt
= strcmp(inctyp
, "to hit") ? TRUE
: FALSE
;
1315 Sprintf(outbuf
, "%s %s %s", modif
, invrt
? inctyp
: bonus
,
1316 invrt
? bonus
: inctyp
);
1317 if (final
|| wizard
)
1318 Sprintf(eos(outbuf
), " (%s%d)", (incamt
> 0) ? "+" : "", incamt
);
1323 /* report half physical or half spell damage */
1325 enlght_halfdmg(category
, final
)
1329 const char *category_name
;
1334 category_name
= "physical";
1337 category_name
= "spell";
1340 category_name
= "unknown";
1343 Sprintf(buf
, " %s %s damage", (final
|| wizard
) ? "half" : "reduced",
1345 enl_msg(You_
, "take", "took", buf
, from_what(category
));
1348 /* is hero actively using water walking capability on water (or lava)? */
1352 if (u
.uinwater
|| Levitation
|| Flying
)
1354 return (boolean
) (Wwalking
1355 && (is_pool(u
.ux
, u
.uy
) || is_lava(u
.ux
, u
.uy
)));
1358 /* check whether hero is wearing something that player definitely knows
1359 confers the target property; item must have been seen and its type
1360 discovered but it doesn't necessarily have to be fully identified */
1362 cause_known(propindx
)
1363 int propindx
; /* index of a property which can be conveyed by worn item */
1365 register struct obj
*o
;
1366 long mask
= W_ARMOR
| W_AMUL
| W_RING
| W_TOOL
;
1368 /* simpler than from_what()/what_gives(); we don't attempt to
1369 handle artifacts and we deliberately ignore wielded items */
1370 for (o
= invent
; o
; o
= o
->nobj
) {
1371 if (!(o
->owornmask
& mask
))
1373 if ((int) objects
[o
->otyp
].oc_oprop
== propindx
1374 && objects
[o
->otyp
].oc_name_known
&& o
->dknown
)
1380 /* format a characteristic value, accommodating Strength's strangeness */
1382 attrval(attrindx
, attrvalue
, resultbuf
)
1383 int attrindx
, attrvalue
;
1384 char resultbuf
[]; /* should be at least [7] to hold "18/100\0" */
1386 if (attrindx
!= A_STR
|| attrvalue
<= 18)
1387 Sprintf(resultbuf
, "%d", attrvalue
);
1388 else if (attrvalue
> STR18(100)) /* 19 to 25 */
1389 Sprintf(resultbuf
, "%d", attrvalue
- 100);
1390 else /* simplify "18/ **" to be "18/100" */
1391 Sprintf(resultbuf
, "18/%02d", attrvalue
- 18);
1396 enlightenment(mode
, final
)
1397 int mode
; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
1398 int final
; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
1400 char buf
[BUFSZ
], tmpbuf
[BUFSZ
];
1402 Strcpy(tmpbuf
, plname
);
1403 *tmpbuf
= highc(*tmpbuf
); /* same adjustment as bottom line */
1404 /* as in background_enlightenment, when poly'd we need to use the saved
1405 gender in u.mfemale rather than the current you-as-monster gender */
1406 Sprintf(buf
, "%s the %s's attributes:", tmpbuf
,
1407 ((Upolyd
? u
.mfemale
: flags
.female
) && urole
.name
.f
)
1411 en_win
= create_nhwindow(NHW_MENU
);
1413 putstr(en_win
, 0, buf
); /* "Conan the Archeologist's attributes:" */
1414 /* background and characteristics; ^X or end-of-game disclosure */
1415 if (mode
& BASICENLIGHTENMENT
) {
1416 /* role, race, alignment, deities */
1417 background_enlightenment(mode
, final
);
1418 /* strength, dexterity, &c */
1419 characteristics_enlightenment(mode
, final
);
1421 /* expanded status line information, including things which aren't
1422 included there due to space considerations--such as obvious
1423 alternative movement indicators (riding, levitation, &c), and
1424 various troubles (turning to stone, trapped, confusion, &c);
1425 shown for both basic and magic enlightenment */
1426 status_enlightenment(mode
, final
);
1427 /* remaining attributes; shown for potion,&c or wizard mode and
1428 explore mode ^X or end of game disclosure */
1429 if (mode
& MAGICENLIGHTENMENT
) {
1430 /* intrinsics and other traditional enlightenment feedback */
1431 attributes_enlightenment(mode
, final
);
1433 display_nhwindow(en_win
, TRUE
);
1434 destroy_nhwindow(en_win
);
1439 /* display role, race, alignment and such to en_win */
1441 background_enlightenment(unused_mode
, final
)
1442 int unused_mode UNUSED
;
1445 const char *role_titl
, *rank_titl
;
1446 int innategend
, difgend
, difalgn
;
1447 char buf
[BUFSZ
], tmpbuf
[BUFSZ
];
1449 /* note that if poly'd, we need to use u.mfemale instead of flags.female
1450 to access hero's saved gender-as-human/elf/&c rather than current one */
1451 innategend
= (Upolyd
? u
.mfemale
: flags
.female
) ? 1 : 0;
1452 role_titl
= (innategend
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
;
1453 rank_titl
= rank_of(u
.ulevel
, Role_switch
, innategend
);
1455 putstr(en_win
, 0, ""); /* separator after title */
1456 putstr(en_win
, 0, "Background:");
1458 /* if polymorphed, report current shape before underlying role;
1459 will be repeated as first status: "you are transformed" and also
1460 among various attributes: "you are in beast form" (after being
1461 told about lycanthropy) or "you are polymorphed into <a foo>"
1462 (with countdown timer appended for wizard mode); we really want
1463 the player to know he's not a samurai at the moment... */
1465 struct permonst
*uasmon
= youmonst
.data
;
1468 /* here we always use current gender, not saved role gender */
1469 if (!is_male(uasmon
) && !is_female(uasmon
) && !is_neuter(uasmon
))
1470 Sprintf(tmpbuf
, "%s ", genders
[flags
.female
? 1 : 0].adj
);
1471 Sprintf(buf
, "%sin %s%s form", !final
? "currently " : "", tmpbuf
,
1476 /* report role; omit gender if it's redundant (eg, "female priestess") */
1479 && ((urole
.allow
& ROLE_GENDMASK
) == (ROLE_MALE
| ROLE_FEMALE
)
1480 || innategend
!= flags
.initgend
))
1481 Sprintf(tmpbuf
, "%s ", genders
[innategend
].adj
);
1484 Strcpy(buf
, "actually "); /* "You are actually a ..." */
1485 if (!strcmpi(rank_titl
, role_titl
)) {
1486 /* omit role when rank title matches it */
1487 Sprintf(eos(buf
), "%s, level %d %s%s", an(rank_titl
), u
.ulevel
,
1488 tmpbuf
, urace
.noun
);
1490 Sprintf(eos(buf
), "%s, a level %d %s%s %s", an(rank_titl
), u
.ulevel
,
1491 tmpbuf
, urace
.adj
, role_titl
);
1495 /* report alignment (bypass you_are() in order to omit ending period);
1496 adverb is used to distinguish between temporary change (helm of opp.
1497 alignment), permanent change (one-time conversion), and original */
1498 Sprintf(buf
, " %s%s%s, %son a mission for %s",
1499 You_
, !final
? are
: were
,
1500 align_str(u
.ualign
.type
),
1501 /* helm of opposite alignment (might hide conversion) */
1502 (u
.ualign
.type
!= u
.ualignbase
[A_CURRENT
])
1503 /* what's the past tense of "currently"? if we used "formerly"
1504 it would sound like a reference to the original alignment */
1505 ? (!final
? "currently " : "temporarily ")
1506 /* permanent conversion */
1507 : (u
.ualign
.type
!= u
.ualignbase
[A_ORIGINAL
])
1508 /* and what's the past tense of "now"? certainly not "then"
1509 in a context like this...; "belatedly" == weren't that
1510 way sooner (in other words, didn't start that way) */
1511 ? (!final
? "now " : "belatedly ")
1512 /* atheist (ignored in very early game) */
1513 : (!u
.uconduct
.gnostic
&& moves
> 1000L)
1515 /* lastly, normal case */
1518 putstr(en_win
, 0, buf
);
1519 /* show the rest of this game's pantheon (finishes previous sentence)
1520 [appending "also Moloch" at the end would allow for straightforward
1521 trailing "and" on all three aligned entries but looks too verbose] */
1522 Sprintf(buf
, " who %s opposed by", !final
? "is" : "was");
1523 if (u
.ualign
.type
!= A_LAWFUL
)
1524 Sprintf(eos(buf
), " %s (%s) and", align_gname(A_LAWFUL
),
1525 align_str(A_LAWFUL
));
1526 if (u
.ualign
.type
!= A_NEUTRAL
)
1527 Sprintf(eos(buf
), " %s (%s)%s", align_gname(A_NEUTRAL
),
1528 align_str(A_NEUTRAL
),
1529 (u
.ualign
.type
!= A_CHAOTIC
) ? " and" : "");
1530 if (u
.ualign
.type
!= A_CHAOTIC
)
1531 Sprintf(eos(buf
), " %s (%s)", align_gname(A_CHAOTIC
),
1532 align_str(A_CHAOTIC
));
1533 Strcat(buf
, "."); /* terminate sentence */
1534 putstr(en_win
, 0, buf
);
1536 /* show original alignment,gender,race,role if any have been changed;
1537 giving separate message for temporary alignment change bypasses need
1538 for tricky phrasing otherwise necessitated by possibility of having
1539 helm of opposite alignment mask a permanent alignment conversion */
1540 difgend
= (innategend
!= flags
.initgend
);
1541 difalgn
= (((u
.ualign
.type
!= u
.ualignbase
[A_CURRENT
]) ? 1 : 0)
1542 + ((u
.ualignbase
[A_CURRENT
] != u
.ualignbase
[A_ORIGINAL
])
1544 if (difalgn
& 1) { /* have temporary alignment so report permanent one */
1545 Sprintf(buf
, "actually %s", align_str(u
.ualignbase
[A_CURRENT
]));
1547 difalgn
&= ~1; /* suppress helm from "started out <foo>" message */
1549 if (difgend
|| difalgn
) { /* sex change or perm align change or both */
1550 Sprintf(buf
, " You started out %s%s%s.",
1551 difgend
? genders
[flags
.initgend
].adj
: "",
1552 (difgend
&& difalgn
) ? " and " : "",
1553 difalgn
? align_str(u
.ualignbase
[A_ORIGINAL
]) : "");
1554 putstr(en_win
, 0, buf
);
1558 /* characteristics: expanded version of bottom line strength, dexterity, &c */
1560 characteristics_enlightenment(mode
, final
)
1564 putstr(en_win
, 0, ""); /* separator after background */
1566 final
? "Final Characteristics:" : "Current Characteristics:");
1568 /* bottom line order */
1569 one_characteristic(mode
, final
, A_STR
); /* strength */
1570 one_characteristic(mode
, final
, A_DEX
); /* dexterity */
1571 one_characteristic(mode
, final
, A_CON
); /* constitution */
1572 one_characteristic(mode
, final
, A_INT
); /* intelligence */
1573 one_characteristic(mode
, final
, A_WIS
); /* wisdom */
1574 one_characteristic(mode
, final
, A_CHA
); /* charisma */
1577 /* display one attribute value for characteristics_enlightenment() */
1579 one_characteristic(mode
, final
, attrindx
)
1580 int mode
, final
, attrindx
;
1582 boolean hide_innate_value
= FALSE
, interesting_alimit
;
1583 int acurrent
, abase
, apeak
, alimit
;
1584 const char *attrname
, *paren_pfx
;
1585 char subjbuf
[BUFSZ
], valubuf
[BUFSZ
], valstring
[32];
1587 /* being polymorphed or wearing certain cursed items prevents
1588 hero from reliably tracking changes to characteristics so
1589 we don't show base & peak values then; when the items aren't
1590 cursed, hero could take them off to check underlying values
1591 and we show those in such case so that player doesn't need
1592 to actually resort to doing that */
1594 hide_innate_value
= TRUE
;
1595 } else if (Fixed_abil
) {
1596 if (stuck_ring(uleft
, RIN_SUSTAIN_ABILITY
)
1597 || stuck_ring(uright
, RIN_SUSTAIN_ABILITY
))
1598 hide_innate_value
= TRUE
;
1602 attrname
= "strength";
1603 if (uarmg
&& uarmg
->otyp
== GAUNTLETS_OF_POWER
&& uarmg
->cursed
)
1604 hide_innate_value
= TRUE
;
1607 attrname
= "dexterity";
1610 attrname
= "constitution";
1613 attrname
= "intelligence";
1614 if (uarmh
&& uarmh
->otyp
== DUNCE_CAP
&& uarmh
->cursed
)
1615 hide_innate_value
= TRUE
;
1618 attrname
= "wisdom";
1619 if (uarmh
&& uarmh
->otyp
== DUNCE_CAP
&& uarmh
->cursed
)
1620 hide_innate_value
= TRUE
;
1623 attrname
= "charisma";
1626 return; /* impossible */
1628 /* note: final disclosure includes MAGICENLIGHTENTMENT */
1629 if ((mode
& MAGICENLIGHTENMENT
) && !Upolyd
)
1630 hide_innate_value
= FALSE
;
1632 acurrent
= ACURR(attrindx
);
1633 (void) attrval(attrindx
, acurrent
, valubuf
); /* Sprintf(valubuf,"%d",) */
1634 Sprintf(subjbuf
, "Your %s ", attrname
);
1636 if (!hide_innate_value
) {
1637 /* show abase, amax, and/or attrmax if acurr doesn't match abase
1638 (a magic bonus or penalty is in effect) or abase doesn't match
1639 amax (some points have been lost to poison or exercise abuse
1640 and are restorable) or attrmax is different from normal human
1641 (while game is in progress; trying to reduce dependency on
1642 spoilers to keep track of such stuff) or attrmax was different
1643 from abase (at end of game; this attribute wasn't maxed out) */
1644 abase
= ABASE(attrindx
);
1645 apeak
= AMAX(attrindx
);
1646 alimit
= ATTRMAX(attrindx
);
1647 /* criterium for whether the limit is interesting varies */
1648 interesting_alimit
=
1649 final
? TRUE
/* was originally `(abase != alimit)' */
1650 : (alimit
!= (attrindx
!= A_STR
? 18 : STR18(100)));
1651 paren_pfx
= final
? " (" : " (current; ";
1652 if (acurrent
!= abase
) {
1653 Sprintf(eos(valubuf
), "%sbase:%s", paren_pfx
,
1654 attrval(attrindx
, abase
, valstring
));
1657 if (abase
!= apeak
) {
1658 Sprintf(eos(valubuf
), "%speak:%s", paren_pfx
,
1659 attrval(attrindx
, apeak
, valstring
));
1662 if (interesting_alimit
) {
1663 Sprintf(eos(valubuf
), "%s%slimit:%s", paren_pfx
,
1664 /* more verbose if exceeding 'limit' due to magic bonus */
1665 (acurrent
> alimit
) ? "innate " : "",
1666 attrval(attrindx
, alimit
, valstring
));
1667 /* paren_pfx = ", "; */
1669 if (acurrent
!= abase
|| abase
!= apeak
|| interesting_alimit
)
1670 Strcat(valubuf
, ")");
1672 enl_msg(subjbuf
, "is ", "was ", valubuf
, "");
1675 /* status: selected obvious capabilities, assorted troubles */
1677 status_enlightenment(mode
, final
)
1681 boolean magic
= (mode
& MAGICENLIGHTENMENT
) ? TRUE
: FALSE
;
1683 char buf
[BUFSZ
], youtoo
[BUFSZ
];
1684 boolean Riding
= (u
.usteed
1685 /* if hero dies while dismounting, u.usteed will still
1686 be set; we want to ignore steed in that situation */
1687 && !(final
== ENL_GAMEOVERDEAD
1688 && !strcmp(killer
.name
, "riding accident")));
1689 const char *steedname
= (!Riding
? (char *) 0
1690 : x_monnam(u
.usteed
,
1691 u
.usteed
->mtame
? ARTICLE_YOUR
: ARTICLE_THE
,
1693 (SUPPRESS_SADDLE
| SUPPRESS_HALLUCINATION
),
1697 * Status (many are abbreviated on bottom line; others are or
1698 * should be discernible to the hero hence to the player)
1700 putstr(en_win
, 0, ""); /* separator after title or characteristics */
1701 putstr(en_win
, 0, final
? "Final Status:" : "Current Status:");
1703 Strcpy(youtoo
, You_
);
1704 /* not a traditional status but inherently obvious to player; more
1705 detail given below (attributes section) for magic enlightenment */
1707 you_are("transformed", "");
1708 /* not a trouble, but we want to display riding status before maybe
1709 reporting steed as trapped or hero stuck to cursed saddle */
1711 Sprintf(buf
, "riding %s", steedname
);
1713 Sprintf(eos(youtoo
), "and %s ", steedname
);
1715 /* other movement situations that hero should always know */
1717 if (Lev_at_will
&& magic
)
1718 you_are("levitating, at will", "");
1720 enl_msg(youtoo
, are
, were
, "levitating", from_what(LEVITATION
));
1721 } else if (Flying
) { /* can only fly when not levitating */
1722 enl_msg(youtoo
, are
, were
, "flying", from_what(FLYING
));
1725 you_are("underwater", "");
1726 } else if (u
.uinwater
) {
1727 you_are(Swimming
? "swimming" : "in water", from_what(SWIMMING
));
1728 } else if (walking_on_water()) {
1729 /* show active Wwalking here, potential Wwalking elsewhere */
1730 Sprintf(buf
, "walking on %s",
1731 is_pool(u
.ux
, u
.uy
) ? "water"
1732 : is_lava(u
.ux
, u
.uy
) ? "lava"
1733 : surface(u
.ux
, u
.uy
)); /* catchall; shouldn't happen */
1734 you_are(buf
, from_what(WWALKING
));
1736 if (Upolyd
&& (u
.uundetected
|| youmonst
.m_ap_type
!= M_AP_NOTHING
))
1737 youhiding(TRUE
, final
);
1739 /* internal troubles, mostly in the order that prayer ranks them */
1741 you_are("turning to stone", "");
1743 you_are("turning into slime", "");
1746 you_are("buried", "");
1748 Strcpy(buf
, "being strangled");
1750 Sprintf(eos(buf
), " (%ld)", (Strangled
& TIMEOUT
));
1751 you_are(buf
, from_what(STRANGLED
));
1755 /* prayer lumps these together; botl puts Ill before FoodPois */
1756 if (u
.usick_type
& SICK_NONVOMITABLE
)
1757 you_are("terminally sick from illness", "");
1758 if (u
.usick_type
& SICK_VOMITABLE
)
1759 you_are("terminally sick from food poisoning", "");
1762 you_are("nauseated", "");
1764 you_are("stunned", "");
1766 you_are("confused", "");
1768 you_are("hallucinating", "");
1770 /* from_what() (currently wizard-mode only) checks !haseyes()
1771 before u.uroleplay.blind, so we should too */
1772 Sprintf(buf
, "%s blind",
1773 !haseyes(youmonst
.data
) ? "innately"
1774 : u
.uroleplay
.blind
? "permanently"
1775 /* better phrasing desperately wanted... */
1776 : Blindfolded_only
? "deliberately"
1778 if (wizard
&& (Blinded
& TIMEOUT
) != 0L
1779 && !u
.uroleplay
.blind
&& haseyes(youmonst
.data
))
1780 Sprintf(eos(buf
), " (%ld)", (Blinded
& TIMEOUT
));
1781 /* !haseyes: avoid "you are innately blind innately" */
1782 you_are(buf
, !haseyes(youmonst
.data
) ? "" : from_what(BLINDED
));
1785 you_are("deaf", from_what(DEAF
));
1787 /* external troubles, more or less */
1790 Sprintf(buf
, "chained to %s", ansimpleoname(uball
));
1792 impossible("Punished without uball?");
1793 Strcpy(buf
, "punished");
1798 char predicament
[BUFSZ
];
1800 boolean anchored
= (u
.utraptype
== TT_BURIEDBALL
);
1803 Strcpy(predicament
, "tethered to something buried");
1804 } else if (u
.utraptype
== TT_INFLOOR
|| u
.utraptype
== TT_LAVA
) {
1805 Sprintf(predicament
, "stuck in %s", the(surface(u
.ux
, u
.uy
)));
1807 Strcpy(predicament
, "trapped");
1808 if ((t
= t_at(u
.ux
, u
.uy
)) != 0)
1809 Sprintf(eos(predicament
), " in %s",
1810 an(defsyms
[trap_to_defsym(t
->ttyp
)].explanation
));
1812 if (u
.usteed
) { /* not `Riding' here */
1813 Sprintf(buf
, "%s%s ", anchored
? "you and " : "", steedname
);
1815 enl_msg(buf
, (anchored
? "are " : "is "),
1816 (anchored
? "were " : "was "), predicament
, "");
1818 you_are(predicament
, "");
1821 Sprintf(buf
, "swallowed by %s", a_monnam(u
.ustuck
));
1823 Sprintf(eos(buf
), " (%u)", u
.uswldtim
);
1825 } else if (u
.ustuck
) {
1826 Sprintf(buf
, "%s %s",
1827 (Upolyd
&& sticks(youmonst
.data
)) ? "holding" : "held by",
1828 a_monnam(u
.ustuck
));
1832 struct obj
*saddle
= which_armor(u
.usteed
, W_SADDLE
);
1834 if (saddle
&& saddle
->cursed
) {
1835 Sprintf(buf
, "stuck to %s %s", s_suffix(steedname
),
1836 simpleonames(saddle
));
1841 /* when mounted, Wounded_legs applies to steed rather than to
1842 hero; we only report steed's wounded legs in wizard mode */
1843 if (u
.usteed
) { /* not `Riding' here */
1844 if (wizard
&& steedname
) {
1845 Strcpy(buf
, steedname
);
1847 enl_msg(buf
, " has", " had", " wounded legs", "");
1850 Sprintf(buf
, "wounded %s", makeplural(body_part(LEG
)));
1855 Sprintf(buf
, "slippery %s", makeplural(body_part(FINGER
)));
1859 if (magic
|| cause_known(FUMBLING
))
1860 enl_msg(You_
, "fumble", "fumbled", "", from_what(FUMBLING
));
1863 if (magic
|| cause_known(SLEEPY
)) {
1864 Strcpy(buf
, from_what(SLEEPY
));
1866 Sprintf(eos(buf
), " (%ld)", (HSleepy
& TIMEOUT
));
1867 enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf
);
1870 /* hunger/nutrition */
1872 if (magic
|| cause_known(HUNGER
))
1873 enl_msg(You_
, "hunger", "hungered", " rapidly",
1876 Strcpy(buf
, hu_stat
[u
.uhs
]); /* hunger status; omitted if "normal" */
1877 mungspaces(buf
); /* strip trailing spaces */
1879 *buf
= lowc(*buf
); /* override capitalization */
1880 if (!strcmp(buf
, "weak"))
1881 Strcat(buf
, " from severe hunger");
1882 else if (!strncmp(buf
, "faint", 5)) /* fainting, fainted */
1883 Strcat(buf
, " due to starvation");
1887 if ((cap
= near_capacity()) > UNENCUMBERED
) {
1888 const char *adj
= "?_?"; /* (should always get overridden) */
1890 Strcpy(buf
, enc_stat
[cap
]);
1895 break; /* burdened */
1898 break; /* stressed */
1901 break; /* strained */
1904 break; /* overtaxed */
1906 adj
= "not possible";
1909 Sprintf(eos(buf
), "; movement %s %s%s", !final
? "is" : "was", adj
,
1910 (cap
< OVERLOADED
) ? " slowed" : "");
1913 /* last resort entry, guarantees Status section is non-empty
1914 (no longer needed for that purpose since weapon status added;
1915 still useful though) */
1916 you_are("unencumbered", "");
1918 /* report being weaponless; distinguish whether gloves are worn */
1920 you_are(uarmg
? "empty handed" /* gloves imply hands */
1921 : humanoid(youmonst
.data
)
1922 /* hands but no weapon and no gloves */
1924 /* alternate phrasing for paws or lack of hands */
1925 : "not wielding anything",
1927 /* two-weaponing implies a weapon (not other odd stuff) in each hand */
1928 } else if (u
.twoweap
) {
1929 you_are("wielding two weapons at once", "");
1930 /* report most weapons by their skill class (so a katana will be
1931 described as a long sword, for instance; mattock and hook are
1932 exceptions), or wielded non-weapon item by its object class */
1934 const char *what
= weapon_descr(uwep
);
1936 if (!strcmpi(what
, "armor") || !strcmpi(what
, "food")
1937 || !strcmpi(what
, "venom"))
1938 Sprintf(buf
, "wielding some %s", what
);
1940 Sprintf(buf
, "wielding %s",
1941 (uwep
->quan
== 1L) ? an(what
) : makeplural(what
));
1944 /* report 'nudity' */
1945 if (!uarm
&& !uarmu
&& !uarmc
&& !uarmg
&& !uarmf
&& !uarmh
) {
1946 if (u
.uroleplay
.nudist
)
1947 enl_msg(You_
, "do", "did", " not wear any armor", "");
1949 you_are("not wearing any armor", "");
1953 /* attributes: intrinsics and the like, other non-obvious capabilities */
1955 attributes_enlightenment(unused_mode
, final
)
1956 int unused_mode UNUSED
;
1959 static NEARDATA
const char if_surroundings_permitted
[] =
1960 " if surroundings permitted";
1967 putstr(en_win
, 0, "");
1968 putstr(en_win
, 0, final
? "Final Attributes:" : "Current Attributes:");
1970 if (u
.uevent
.uhand_of_elbereth
) {
1971 static const char *const hofe_titles
[3] = { "the Hand of Elbereth",
1972 "the Envoy of Balance",
1973 "the Glory of Arioch" };
1974 you_are(hofe_titles
[u
.uevent
.uhand_of_elbereth
- 1], "");
1977 Sprintf(buf
, "%s", piousness(TRUE
, "aligned"));
1978 if (u
.ualign
.record
>= 0)
1984 Sprintf(buf
, " %d", u
.ualign
.record
);
1985 enl_msg("Your alignment ", "is", "was", buf
, "");
1988 /*** Resistances to troubles ***/
1990 you_are("invulnerable", from_what(INVULNERABLE
));
1992 you_are("magic-protected", from_what(ANTIMAGIC
));
1993 if (Fire_resistance
)
1994 you_are("fire resistant", from_what(FIRE_RES
));
1995 if (Cold_resistance
)
1996 you_are("cold resistant", from_what(COLD_RES
));
1997 if (Sleep_resistance
)
1998 you_are("sleep resistant", from_what(SLEEP_RES
));
1999 if (Disint_resistance
)
2000 you_are("disintegration-resistant", from_what(DISINT_RES
));
2001 if (Shock_resistance
)
2002 you_are("shock resistant", from_what(SHOCK_RES
));
2003 if (Poison_resistance
)
2004 you_are("poison resistant", from_what(POISON_RES
));
2005 if (Acid_resistance
)
2006 you_are("acid resistant", from_what(ACID_RES
));
2007 if (Drain_resistance
)
2008 you_are("level-drain resistant", from_what(DRAIN_RES
));
2009 if (Sick_resistance
)
2010 you_are("immune to sickness", from_what(SICK_RES
));
2011 if (Stone_resistance
)
2012 you_are("petrification resistant", from_what(STONE_RES
));
2013 if (Halluc_resistance
)
2014 enl_msg(You_
, "resist", "resisted", " hallucinations",
2015 from_what(HALLUC_RES
));
2017 you_can("recognize detrimental food", "");
2019 /*** Vision and senses ***/
2020 if (!Blind
&& (Blinded
|| !haseyes(youmonst
.data
)))
2021 you_can("see", from_what(-BLINDED
)); /* Eyes of the Overworld */
2022 if (See_invisible
) {
2024 enl_msg(You_
, "see", "saw", " invisible", from_what(SEE_INVIS
));
2026 enl_msg(You_
, "will see", "would have seen",
2027 " invisible when not blind", from_what(SEE_INVIS
));
2030 you_are("telepathic", from_what(TELEPAT
));
2032 you_are("warned", from_what(WARNING
));
2033 if (Warn_of_mon
&& context
.warntype
.obj
) {
2034 Sprintf(buf
, "aware of the presence of %s",
2035 (context
.warntype
.obj
& M2_ORC
) ? "orcs"
2036 : (context
.warntype
.obj
& M2_ELF
) ? "elves"
2037 : (context
.warntype
.obj
& M2_DEMON
) ? "demons" : something
);
2038 you_are(buf
, from_what(WARN_OF_MON
));
2040 if (Warn_of_mon
&& context
.warntype
.polyd
) {
2041 Sprintf(buf
, "aware of the presence of %s",
2042 ((context
.warntype
.polyd
& (M2_HUMAN
| M2_ELF
))
2043 == (M2_HUMAN
| M2_ELF
))
2044 ? "humans and elves"
2045 : (context
.warntype
.polyd
& M2_HUMAN
)
2047 : (context
.warntype
.polyd
& M2_ELF
)
2049 : (context
.warntype
.polyd
& M2_ORC
)
2051 : (context
.warntype
.polyd
& M2_DEMON
)
2053 : "certain monsters");
2056 if (Warn_of_mon
&& context
.warntype
.speciesidx
>= LOW_PM
) {
2057 Sprintf(buf
, "aware of the presence of %s",
2058 makeplural(mons
[context
.warntype
.speciesidx
].mname
));
2059 you_are(buf
, from_what(WARN_OF_MON
));
2062 you_are("warned of undead", from_what(WARN_UNDEAD
));
2064 you_have("automatic searching", from_what(SEARCHING
));
2066 you_are("clairvoyant", from_what(CLAIRVOYANT
));
2067 else if ((HClairvoyant
|| EClairvoyant
) && BClairvoyant
) {
2068 Strcpy(buf
, from_what(-CLAIRVOYANT
));
2069 if (!strncmp(buf
, " because of ", 12))
2070 /* overwrite substring; strncpy doesn't add terminator */
2071 (void) strncpy(buf
, " if not for ", 12);
2072 enl_msg(You_
, "could be", "could have been", " clairvoyant", buf
);
2075 you_have("infravision", from_what(INFRAVISION
));
2076 if (Detect_monsters
)
2077 you_are("sensing the presence of monsters", "");
2079 you_are("going to confuse monsters", "");
2081 /*** Appearance and behavior ***/
2085 if (uleft
&& uleft
->otyp
== RIN_ADORNMENT
)
2086 adorn
+= uleft
->spe
;
2087 if (uright
&& uright
->otyp
== RIN_ADORNMENT
)
2088 adorn
+= uright
->spe
;
2089 /* the sum might be 0 (+0 ring or two which negate each other);
2090 that yields "you are charismatic" (which isn't pointless
2091 because it potentially impacts seduction attacks) */
2092 Sprintf(buf
, "%scharismatic",
2093 (adorn
> 0) ? "more " : (adorn
< 0) ? "less " : "");
2094 you_are(buf
, from_what(ADORNED
));
2097 you_are("invisible", from_what(INVIS
));
2099 you_are("invisible to others", from_what(INVIS
));
2100 /* ordinarily "visible" is redundant; this is a special case for
2101 the situation when invisibility would be an expected attribute */
2102 else if ((HInvis
|| EInvis
) && BInvis
)
2103 you_are("visible", from_what(-INVIS
));
2105 you_are("displaced", from_what(DISPLACED
));
2107 you_are("stealthy", from_what(STEALTH
));
2108 if (Aggravate_monster
)
2109 enl_msg("You aggravate", "", "d", " monsters",
2110 from_what(AGGRAVATE_MONSTER
));
2112 enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT
));
2114 /*** Transportation ***/
2116 you_can("jump", from_what(JUMPING
));
2118 you_can("teleport", from_what(TELEPORT
));
2119 if (Teleport_control
)
2120 you_have("teleport control", from_what(TELEPORT_CONTROL
));
2121 /* actively levitating handled earlier as a status condition */
2122 if (BLevitation
) { /* levitation is blocked */
2123 long save_BLev
= BLevitation
;
2127 enl_msg(You_
, "would levitate", "would have levitated",
2128 if_surroundings_permitted
, "");
2129 BLevitation
= save_BLev
;
2131 /* actively flying handled earlier as a status condition */
2132 if (BFlying
) { /* flight is blocked */
2133 long save_BFly
= BFlying
;
2137 enl_msg(You_
, "would fly", "would have flown",
2139 ? "if you weren't levitating"
2140 : (save_BFly
== FROMOUTSIDE
)
2141 ? if_surroundings_permitted
2142 /* both surroundings and [latent] levitation */
2143 : " if circumstances permitted",
2145 BFlying
= save_BFly
;
2147 /* actively walking on water handled earlier as a status condition */
2148 if (Wwalking
&& !walking_on_water())
2149 you_can("walk on water", from_what(WWALKING
));
2150 /* actively swimming (in water but not under it) handled earlier */
2151 if (Swimming
&& (Underwater
|| !u
.uinwater
))
2152 you_can("swim", from_what(SWIMMING
));
2154 you_can("survive without air", from_what(MAGICAL_BREATHING
));
2155 else if (Amphibious
)
2156 you_can("breathe water", from_what(MAGICAL_BREATHING
));
2158 you_can("walk through walls", from_what(PASSES_WALLS
));
2160 /*** Physical attributes ***/
2162 enl_msg("You regenerate", "", "d", "", from_what(REGENERATION
));
2164 you_have("slower digestion", from_what(SLOW_DIGESTION
));
2166 you_have(enlght_combatinc("to hit", u
.uhitinc
, final
, buf
), "");
2168 you_have(enlght_combatinc("damage", u
.udaminc
, final
, buf
), "");
2169 if (u
.uspellprot
|| Protection
) {
2172 if (uleft
&& uleft
->otyp
== RIN_PROTECTION
)
2174 if (uright
&& uright
->otyp
== RIN_PROTECTION
)
2175 prot
+= uright
->spe
;
2176 if (HProtection
& INTRINSIC
)
2178 prot
+= u
.uspellprot
;
2180 you_have(enlght_combatinc("defense", prot
, final
, buf
), "");
2182 if ((armpro
= magic_negation(&youmonst
)) > 0) {
2183 /* magic cancellation factor, conferred by worn armor */
2184 static const char *const mc_types
[] = {
2185 "" /*ordinary*/, "warded", "guarded", "protected",
2188 if (armpro
>= SIZE(mc_types
))
2189 armpro
= SIZE(mc_types
) - 1;
2190 you_are(mc_types
[armpro
], "");
2192 if (Half_physical_damage
)
2193 enlght_halfdmg(HALF_PHDAM
, final
);
2194 if (Half_spell_damage
)
2195 enlght_halfdmg(HALF_SPDAM
, final
);
2196 /* polymorph and other shape change */
2197 if (Protection_from_shape_changers
)
2198 you_are("protected from shape changers",
2199 from_what(PROT_FROM_SHAPE_CHANGERS
));
2201 const char *what
= 0;
2203 if (!Upolyd
) /* Upolyd handled below after current form */
2204 you_can("not change from your current form",
2205 from_what(UNCHANGING
));
2206 /* blocked shape changes */
2208 what
= !final
? "polymorph" : "have polymorphed";
2209 else if (u
.ulycn
>= LOW_PM
)
2210 what
= !final
? "change shape" : "have changed shape";
2212 Sprintf(buf
, "would %s periodically", what
);
2213 /* omit from_what(UNCHANGING); too verbose */
2214 enl_msg(You_
, buf
, buf
, " if not locked into your current form",
2217 } else if (Polymorph
) {
2218 you_are("polymorphing periodically", from_what(POLYMORPH
));
2220 if (Polymorph_control
)
2221 you_have("polymorph control", from_what(POLYMORPH_CONTROL
));
2222 if (Upolyd
&& u
.umonnum
!= u
.ulycn
) {
2223 /* foreign shape (except were-form which is handled below) */
2224 Sprintf(buf
, "polymorphed into %s", an(youmonst
.data
->mname
));
2226 Sprintf(eos(buf
), " (%d)", u
.mtimedone
);
2229 if (lays_eggs(youmonst
.data
) && flags
.female
) /* Upolyd */
2230 you_can("lay eggs", "");
2231 if (u
.ulycn
>= LOW_PM
) {
2232 /* "you are a werecreature [in beast form]" */
2233 Strcpy(buf
, an(mons
[u
.ulycn
].mname
));
2234 if (u
.umonnum
== u
.ulycn
) {
2235 Strcat(buf
, " in beast form");
2237 Sprintf(eos(buf
), " (%d)", u
.mtimedone
);
2241 if (Unchanging
&& Upolyd
) /* !Upolyd handled above */
2242 you_can("not change from your current form", from_what(UNCHANGING
));
2244 you_are("harmed by silver", "");
2245 /* movement and non-armor-based protection */
2247 you_are(Very_fast
? "very fast" : "fast", from_what(FAST
));
2249 you_have("reflection", from_what(REFLECTING
));
2251 you_have("free action", from_what(FREE_ACTION
));
2253 you_have("fixed abilities", from_what(FIXED_ABIL
));
2255 enl_msg("Your life ", "will be", "would have been", " saved", "");
2257 /*** Miscellany ***/
2259 ltmp
= abs((int) Luck
);
2260 Sprintf(buf
, "%s%slucky",
2261 ltmp
>= 10 ? "extremely " : ltmp
>= 5 ? "very " : "",
2262 Luck
< 0 ? "un" : "");
2264 Sprintf(eos(buf
), " (%d)", Luck
);
2267 enl_msg("Your luck ", "is", "was", " zero", "");
2269 you_have("extra luck", "");
2270 else if (u
.moreluck
< 0)
2271 you_have("reduced luck", "");
2272 if (carrying(LUCKSTONE
) || stone_luck(TRUE
)) {
2273 ltmp
= stone_luck(0);
2275 enl_msg("Bad luck ", "does", "did", " not time out for you", "");
2277 enl_msg("Good luck ", "does", "did", " not time out for you", "");
2281 Sprintf(buf
, " %sangry with you",
2282 u
.ugangr
> 6 ? "extremely " : u
.ugangr
> 3 ? "very " : "");
2284 Sprintf(eos(buf
), " (%d)", u
.ugangr
);
2285 enl_msg(u_gname(), " is", " was", buf
, "");
2288 * We need to suppress this when the game is over, because death
2289 * can change the value calculated by can_pray(), potentially
2290 * resulting in a false claim that you could have prayed safely.
2294 /* "can [not] safely pray" vs "could [not] have safely prayed" */
2295 Sprintf(buf
, "%s%ssafely pray%s", can_pray(FALSE
) ? "" : "not ",
2296 final
? "have " : "", final
? "ed" : "");
2298 Sprintf(buf
, "%ssafely pray", can_pray(FALSE
) ? "" : "not ");
2301 Sprintf(eos(buf
), " (%d)", u
.ublesscnt
);
2307 /* named fruit debugging (doesn't really belong here...); to enable,
2308 include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
2309 if (wizard
&& explicitdebug("fruit")) {
2314 for (f
= ffruit
; f
; f
= f
->nextf
) {
2315 Sprintf(buf
, "Fruit %d ", ++fcount
);
2316 Sprintf(buf2
, "%s (id %d)", f
->fname
, f
->fid
);
2317 enl_msg(buf
, "is ", "was ", buf2
, "");
2319 enl_msg("The current fruit ", "is ", "was ", pl_fruit
, "");
2320 Sprintf(buf
, "%d", flags
.made_fruit
);
2321 enl_msg("The made fruit flag ", "is ", "was ", buf
, "");
2329 if (final
< 2) { /* still in progress, or quit/escaped/ascended */
2330 p
= "survived after being killed ";
2331 switch (u
.umortality
) {
2333 p
= !final
? (char *) 0 : "survived";
2336 Strcpy(buf
, "once");
2339 Strcpy(buf
, "twice");
2342 Strcpy(buf
, "thrice");
2345 Sprintf(buf
, "%d times", u
.umortality
);
2348 } else { /* game ended in character's death */
2350 switch (u
.umortality
) {
2352 impossible("dead without dying?");
2354 break; /* just "are dead" */
2356 Sprintf(buf
, " (%d%s time!)", u
.umortality
,
2357 ordin(u
.umortality
));
2362 enl_msg(You_
, "have been killed ", p
, buf
, "");
2366 #if 0 /* no longer used */
2367 STATIC_DCL boolean
NDECL(minimal_enlightenment
);
2370 * Courtesy function for non-debug, non-explorer mode players
2371 * to help refresh them about who/what they are.
2372 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
2375 minimal_enlightenment()
2378 menu_item
*selected
;
2381 char buf
[BUFSZ
], buf2
[BUFSZ
];
2382 static const char untabbed_fmtstr
[] = "%-15s: %-12s";
2383 static const char untabbed_deity_fmtstr
[] = "%-17s%s";
2384 static const char tabbed_fmtstr
[] = "%s:\t%-12s";
2385 static const char tabbed_deity_fmtstr
[] = "%s\t%s";
2386 static const char *fmtstr
;
2387 static const char *deity_fmtstr
;
2389 fmtstr
= iflags
.menu_tab_sep
? tabbed_fmtstr
: untabbed_fmtstr
;
2390 deity_fmtstr
= iflags
.menu_tab_sep
? tabbed_deity_fmtstr
2391 : untabbed_deity_fmtstr
;
2393 buf
[0] = buf2
[0] = '\0';
2394 tmpwin
= create_nhwindow(NHW_MENU
);
2396 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, iflags
.menu_headings
,
2399 /* Starting name, race, role, gender */
2400 Sprintf(buf
, fmtstr
, "name", plname
);
2401 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2402 Sprintf(buf
, fmtstr
, "race", urace
.noun
);
2403 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2404 Sprintf(buf
, fmtstr
, "role",
2405 (flags
.initgend
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
);
2406 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2407 Sprintf(buf
, fmtstr
, "gender", genders
[flags
.initgend
].adj
);
2408 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2410 /* Starting alignment */
2411 Sprintf(buf
, fmtstr
, "alignment", align_str(u
.ualignbase
[A_ORIGINAL
]));
2412 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2414 /* Current name, race, role, gender */
2415 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", FALSE
);
2416 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, iflags
.menu_headings
,
2418 Sprintf(buf
, fmtstr
, "race", Upolyd
? youmonst
.data
->mname
: urace
.noun
);
2419 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2421 Sprintf(buf
, fmtstr
, "role (base)",
2422 (u
.mfemale
&& urole
.name
.f
) ? urole
.name
.f
2424 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2426 Sprintf(buf
, fmtstr
, "role",
2427 (flags
.female
&& urole
.name
.f
) ? urole
.name
.f
2429 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2431 /* don't want poly_gender() here; it forces `2' for non-humanoids */
2432 genidx
= is_neuter(youmonst
.data
) ? 2 : flags
.female
;
2433 Sprintf(buf
, fmtstr
, "gender", genders
[genidx
].adj
);
2434 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2435 if (Upolyd
&& (int) u
.mfemale
!= genidx
) {
2436 Sprintf(buf
, fmtstr
, "gender (base)", genders
[u
.mfemale
].adj
);
2437 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2440 /* Current alignment */
2441 Sprintf(buf
, fmtstr
, "alignment", align_str(u
.ualign
.type
));
2442 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2445 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", FALSE
);
2446 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, iflags
.menu_headings
,
2448 Sprintf(buf2
, deity_fmtstr
, align_gname(A_CHAOTIC
),
2449 (u
.ualignbase
[A_ORIGINAL
] == u
.ualign
.type
2450 && u
.ualign
.type
== A_CHAOTIC
) ? " (s,c)"
2451 : (u
.ualignbase
[A_ORIGINAL
] == A_CHAOTIC
) ? " (s)"
2452 : (u
.ualign
.type
== A_CHAOTIC
) ? " (c)" : "");
2453 Sprintf(buf
, fmtstr
, "Chaotic", buf2
);
2454 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2456 Sprintf(buf2
, deity_fmtstr
, align_gname(A_NEUTRAL
),
2457 (u
.ualignbase
[A_ORIGINAL
] == u
.ualign
.type
2458 && u
.ualign
.type
== A_NEUTRAL
) ? " (s,c)"
2459 : (u
.ualignbase
[A_ORIGINAL
] == A_NEUTRAL
) ? " (s)"
2460 : (u
.ualign
.type
== A_NEUTRAL
) ? " (c)" : "");
2461 Sprintf(buf
, fmtstr
, "Neutral", buf2
);
2462 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2464 Sprintf(buf2
, deity_fmtstr
, align_gname(A_LAWFUL
),
2465 (u
.ualignbase
[A_ORIGINAL
] == u
.ualign
.type
2466 && u
.ualign
.type
== A_LAWFUL
) ? " (s,c)"
2467 : (u
.ualignbase
[A_ORIGINAL
] == A_LAWFUL
) ? " (s)"
2468 : (u
.ualign
.type
== A_LAWFUL
) ? " (c)" : "");
2469 Sprintf(buf
, fmtstr
, "Lawful", buf2
);
2470 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, buf
, FALSE
);
2472 end_menu(tmpwin
, "Base Attributes");
2473 n
= select_menu(tmpwin
, PICK_NONE
, &selected
);
2474 destroy_nhwindow(tmpwin
);
2475 return (boolean
) (n
!= -1);
2481 doattributes(VOID_ARGS
)
2483 int mode
= BASICENLIGHTENMENT
;
2485 /* show more--as if final disclosure--for wizard and explore modes */
2486 if (wizard
|| discover
)
2487 mode
|= MAGICENLIGHTENMENT
;
2489 enlightenment(mode
, ENL_GAMEINPROGRESS
);
2494 youhiding(via_enlghtmt
, msgflag
)
2495 boolean via_enlghtmt
; /* englightment line vs topl message */
2496 int msgflag
; /* for variant message phrasing */
2498 char *bp
, buf
[BUFSZ
];
2500 Strcpy(buf
, "hiding");
2501 if (youmonst
.m_ap_type
!= M_AP_NOTHING
) {
2502 /* mimic; hero is only able to mimic a strange object or gold
2503 or hallucinatory alternative to gold, so we skip the details
2504 for the hypothetical furniture and monster cases */
2505 bp
= eos(strcpy(buf
, "mimicking"));
2506 if (youmonst
.m_ap_type
== M_AP_OBJECT
) {
2507 Sprintf(bp
, " %s", an(simple_typename(youmonst
.mappearance
)));
2508 } else if (youmonst
.m_ap_type
== M_AP_FURNITURE
) {
2509 Strcpy(bp
, " something");
2510 } else if (youmonst
.m_ap_type
== M_AP_MONSTER
) {
2511 Strcpy(bp
, " someone");
2513 ; /* something unexpected; leave 'buf' as-is */
2515 } else if (u
.uundetected
) {
2516 bp
= eos(buf
); /* points past "hiding" */
2517 if (youmonst
.data
->mlet
== S_EEL
) {
2518 if (is_pool(u
.ux
, u
.uy
))
2519 Sprintf(bp
, " in the %s", waterbody_name(u
.ux
, u
.uy
));
2520 } else if (hides_under(youmonst
.data
)) {
2521 struct obj
*o
= level
.objects
[u
.ux
][u
.uy
];
2524 Sprintf(bp
, " underneath %s", ansimpleoname(o
));
2525 } else if (is_clinger(youmonst
.data
) || Flying
) {
2526 /* Flying: 'lurker above' hides on ceiling but doesn't cling */
2527 Sprintf(bp
, " on the %s", ceiling(u
.ux
, u
.uy
));
2529 /* on floor; is_hider() but otherwise not special: 'trapper' */
2530 if (u
.utrap
&& u
.utraptype
== TT_PIT
) {
2531 struct trap
*t
= t_at(u
.ux
, u
.uy
);
2533 Sprintf(bp
, " in a %spit",
2534 (t
&& t
->ttyp
== SPIKED_PIT
) ? "spiked " : "");
2536 Sprintf(bp
, " on the %s", surface(u
.ux
, u
.uy
));
2539 ; /* shouldn't happen; will result in generic "you are hiding" */
2543 int final
= msgflag
; /* 'final' is used by you_are() macro */
2547 /* for dohide(), when player uses '#monster' command */
2548 You("are %s %s.", msgflag
? "already" : "now", buf
);
2553 * (shares enlightenment's tense handling)
2556 doconduct(VOID_ARGS
)
2569 /* Create the conduct window */
2570 en_win
= create_nhwindow(NHW_MENU
);
2571 putstr(en_win
, 0, "Voluntary challenges:");
2573 if (u
.uroleplay
.blind
)
2574 you_have_been("blind from birth");
2575 if (u
.uroleplay
.nudist
)
2576 you_have_been("faithfully nudist");
2578 if (!u
.uconduct
.food
)
2579 enl_msg(You_
, "have gone", "went", " without food", "");
2580 /* But beverages are okay */
2581 else if (!u
.uconduct
.unvegan
)
2582 you_have_X("followed a strict vegan diet");
2583 else if (!u
.uconduct
.unvegetarian
)
2584 you_have_been("vegetarian");
2586 if (!u
.uconduct
.gnostic
)
2587 you_have_been("an atheist");
2589 if (!u
.uconduct
.weaphit
) {
2590 you_have_never("hit with a wielded weapon");
2591 } else if (wizard
) {
2592 Sprintf(buf
, "used a wielded weapon %ld time%s", u
.uconduct
.weaphit
,
2593 plur(u
.uconduct
.weaphit
));
2596 if (!u
.uconduct
.killer
)
2597 you_have_been("a pacifist");
2599 if (!u
.uconduct
.literate
) {
2600 you_have_been("illiterate");
2601 } else if (wizard
) {
2602 Sprintf(buf
, "read items or engraved %ld time%s", u
.uconduct
.literate
,
2603 plur(u
.uconduct
.literate
));
2607 ngenocided
= num_genocides();
2608 if (ngenocided
== 0) {
2609 you_have_never("genocided any monsters");
2611 Sprintf(buf
, "genocided %d type%s of monster%s", ngenocided
,
2612 plur(ngenocided
), plur(ngenocided
));
2616 if (!u
.uconduct
.polypiles
) {
2617 you_have_never("polymorphed an object");
2618 } else if (wizard
) {
2619 Sprintf(buf
, "polymorphed %ld item%s", u
.uconduct
.polypiles
,
2620 plur(u
.uconduct
.polypiles
));
2624 if (!u
.uconduct
.polyselfs
) {
2625 you_have_never("changed form");
2626 } else if (wizard
) {
2627 Sprintf(buf
, "changed form %ld time%s", u
.uconduct
.polyselfs
,
2628 plur(u
.uconduct
.polyselfs
));
2632 if (!u
.uconduct
.wishes
) {
2633 you_have_X("used no wishes");
2635 Sprintf(buf
, "used %ld wish%s", u
.uconduct
.wishes
,
2636 (u
.uconduct
.wishes
> 1L) ? "es" : "");
2637 if (u
.uconduct
.wisharti
) {
2638 /* if wisharti == wishes
2639 * 1 wish (for an artifact)
2640 * 2 wishes (both for artifacts)
2641 * N wishes (all for artifacts)
2642 * else (N is at least 2 in order to get here; M < N)
2643 * N wishes (1 for an artifact)
2644 * N wishes (M for artifacts)
2646 if (u
.uconduct
.wisharti
== u
.uconduct
.wishes
)
2647 Sprintf(eos(buf
), " (%s",
2648 (u
.uconduct
.wisharti
> 2L) ? "all "
2649 : (u
.uconduct
.wisharti
== 2L) ? "both " : "");
2651 Sprintf(eos(buf
), " (%ld ", u
.uconduct
.wisharti
);
2653 Sprintf(eos(buf
), "for %s)",
2654 (u
.uconduct
.wisharti
== 1L) ? "an artifact"
2659 if (!u
.uconduct
.wisharti
)
2660 enl_msg(You_
, "have not wished", "did not wish",
2661 " for any artifacts", "");
2664 /* Pop up the window and wait for a key */
2665 display_nhwindow(en_win
, TRUE
);
2666 destroy_nhwindow(en_win
);
2672 #define M(c) (0x80 | (c))
2674 #define M(c) ((c) -128)
2678 #define C(c) (0x1f & (c))
2681 static const struct func_tab cmdlist
[] = {
2682 { C('d'), FALSE
, dokick
}, /* "D" is for door!...? Msg is in dokick.c */
2683 { C('e'), TRUE
, wiz_detect
},
2684 { C('f'), TRUE
, wiz_map
},
2685 { C('g'), TRUE
, wiz_genesis
},
2686 { C('i'), TRUE
, wiz_identify
},
2687 { C('l'), TRUE
, doredraw
}, /* if number_pad is set */
2688 { C('n'), TRUE
, donamelevel
}, /* if number_pad is set */
2689 { C('o'), TRUE
, dooverview_or_wiz_where
}, /* depends on wizard status */
2690 { C('p'), TRUE
, doprev_message
},
2691 { C('r'), TRUE
, doredraw
},
2692 { C('t'), TRUE
, dotele
},
2693 { C('v'), TRUE
, wiz_level_tele
},
2694 { C('w'), TRUE
, wiz_wish
},
2695 { C('x'), TRUE
, doattributes
},
2696 { C('z'), TRUE
, dosuspend_core
},
2697 { 'a', FALSE
, doapply
},
2698 { 'A', FALSE
, doddoremarm
},
2699 { M('a'), TRUE
, doorganize
},
2700 { M('A'), TRUE
, donamelevel
}, /* #annotate */
2701 /* 'b', 'B' : go sw */
2702 { 'c', FALSE
, doclose
},
2703 { 'C', TRUE
, docallcmd
},
2704 { M('c'), TRUE
, dotalk
},
2705 { M('C'), TRUE
, doconduct
}, /* #conduct */
2706 { 'd', FALSE
, dodrop
},
2707 { 'D', FALSE
, doddrop
},
2708 { M('d'), FALSE
, dodip
},
2709 { 'e', FALSE
, doeat
},
2710 { 'E', FALSE
, doengrave
},
2711 { M('e'), TRUE
, enhance_weapon_skill
},
2712 { 'f', FALSE
, dofire
},
2713 /* 'F' : fight (one time) */
2714 { M('f'), FALSE
, doforce
},
2715 /* 'g', 'G' : multiple go */
2716 /* 'h', 'H' : go west */
2717 { 'h', TRUE
, dohelp
}, /* if number_pad is set */
2718 { 'i', TRUE
, ddoinv
},
2719 { 'I', TRUE
, dotypeinv
}, /* Robert Viduya */
2720 { M('i'), TRUE
, doinvoke
},
2721 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */
2722 { 'j', FALSE
, dojump
}, /* if number_pad is on */
2723 { M('j'), FALSE
, dojump
},
2724 { 'k', FALSE
, dokick
}, /* if number_pad is on */
2725 { 'l', FALSE
, doloot
}, /* if number_pad is on */
2726 { M('l'), FALSE
, doloot
},
2727 /* 'n' prefixes a count if number_pad is on */
2728 { M('m'), TRUE
, domonability
},
2729 { 'N', TRUE
, docallcmd
}, /* if number_pad is on */
2730 { M('n'), TRUE
, docallcmd
},
2731 { M('N'), TRUE
, docallcmd
},
2732 { 'o', FALSE
, doopen
},
2733 { 'O', TRUE
, doset
},
2734 { M('o'), FALSE
, dosacrifice
},
2735 { M('O'), TRUE
, dooverview
}, /* #overview */
2736 { 'p', FALSE
, dopay
},
2737 { 'P', FALSE
, doputon
},
2738 { M('p'), TRUE
, dopray
},
2739 { 'q', FALSE
, dodrink
},
2740 { 'Q', FALSE
, dowieldquiver
},
2741 { M('q'), TRUE
, done2
},
2742 { 'r', FALSE
, doread
},
2743 { 'R', FALSE
, doremring
},
2744 { M('r'), FALSE
, dorub
},
2745 { M('R'), FALSE
, doride
}, /* #ride */
2746 { 's', TRUE
, dosearch
, "searching" },
2747 { 'S', TRUE
, dosave
},
2748 { M('s'), FALSE
, dosit
},
2749 { 't', FALSE
, dothrow
},
2750 { 'T', FALSE
, dotakeoff
},
2751 { M('t'), TRUE
, doturn
},
2752 { M('T'), FALSE
, dotip
}, /* #tip */
2753 /* 'u', 'U' : go ne */
2754 { 'u', FALSE
, dountrap
}, /* if number_pad is on */
2755 { M('u'), FALSE
, dountrap
},
2756 { 'v', TRUE
, doversion
},
2757 { 'V', TRUE
, dohistory
},
2758 { M('v'), TRUE
, doextversion
},
2759 { 'w', FALSE
, dowield
},
2760 { 'W', FALSE
, dowear
},
2761 { M('w'), FALSE
, dowipe
},
2762 { 'x', FALSE
, doswapweapon
},
2763 { 'X', FALSE
, dotwoweapon
},
2764 /* 'y', 'Y' : go nw */
2765 { 'z', FALSE
, dozap
},
2766 { 'Z', TRUE
, docast
},
2767 { '<', FALSE
, doup
},
2768 { '>', FALSE
, dodown
},
2769 { '/', TRUE
, dowhatis
},
2770 { '&', TRUE
, dowhatdoes
},
2771 { '?', TRUE
, dohelp
},
2772 { M('?'), TRUE
, doextlist
},
2774 { '!', TRUE
, dosh
},
2776 { '.', TRUE
, donull
, "waiting" },
2777 { ' ', TRUE
, donull
, "waiting" },
2778 { ',', FALSE
, dopickup
},
2779 { ':', TRUE
, dolook
},
2780 { ';', TRUE
, doquickwhatis
},
2781 { '^', TRUE
, doidtrap
},
2782 { '\\', TRUE
, dodiscovered
}, /* Robert Viduya */
2783 { '`', TRUE
, doclassdisco
},
2784 { '@', TRUE
, dotogglepickup
},
2785 { M('2'), FALSE
, dotwoweapon
},
2786 { WEAPON_SYM
, TRUE
, doprwep
},
2787 { ARMOR_SYM
, TRUE
, doprarm
},
2788 { RING_SYM
, TRUE
, doprring
},
2789 { AMULET_SYM
, TRUE
, dopramulet
},
2790 { TOOL_SYM
, TRUE
, doprtool
},
2791 { '*', TRUE
, doprinuse
}, /* inventory of all equipment in use */
2792 { GOLD_SYM
, TRUE
, doprgold
},
2793 { SPBOOK_SYM
, TRUE
, dovspell
}, /* Mike Stephenson */
2794 { '#', TRUE
, doextcmd
},
2795 { '_', TRUE
, dotravel
},
2799 struct ext_func_tab extcmdlist
[] = {
2800 { "adjust", "adjust inventory letters", doorganize
, TRUE
},
2801 { "annotate", "name current level", donamelevel
, TRUE
},
2802 { "chat", "talk to someone", dotalk
, TRUE
}, /* converse? */
2803 { "conduct", "list voluntary challenges you have maintained", doconduct
,
2805 { "dip", "dip an object into something", dodip
, FALSE
},
2806 { "enhance", "advance or check weapon and spell skills",
2807 enhance_weapon_skill
, TRUE
},
2808 { "exploremode", "enter explore mode", enter_explore_mode
, TRUE
},
2809 { "force", "force a lock", doforce
, FALSE
},
2810 { "invoke", "invoke an object's powers", doinvoke
, TRUE
},
2811 { "jump", "jump to a location", dojump
, FALSE
},
2812 { "kick", "kick something", dokick
, FALSE
},
2813 { "loot", "loot a box on the floor", doloot
, FALSE
},
2814 { "monster", "use a monster's special ability", domonability
, TRUE
},
2815 { "name", "name a monster or an object", docallcmd
, TRUE
},
2816 { "offer", "offer a sacrifice to the gods", dosacrifice
, FALSE
},
2817 { "overview", "show an overview of the dungeon", dooverview
, TRUE
},
2818 { "pray", "pray to the gods for help", dopray
, TRUE
},
2819 { "quit", "exit without saving current game", done2
, TRUE
},
2820 { "ride", "ride (or stop riding) a monster", doride
, FALSE
},
2821 { "rub", "rub a lamp or a stone", dorub
, FALSE
},
2822 { "sit", "sit down", dosit
, FALSE
},
2823 { "terrain", "show map without obstructions", doterrain
, TRUE
},
2824 { "tip", "empty a container", dotip
, FALSE
},
2825 { "turn", "turn undead", doturn
, TRUE
},
2826 { "twoweapon", "toggle two-weapon combat", dotwoweapon
, FALSE
},
2827 { "untrap", "untrap something", dountrap
, FALSE
},
2828 { "version", "list compile time options for this version of NetHack",
2829 doextversion
, TRUE
},
2830 { "wipe", "wipe off your face", dowipe
, FALSE
},
2831 { "?", "get this list of extended commands", doextlist
, TRUE
},
2833 * There must be a blank entry here for every entry in the table
2836 { (char *) 0, (char *) 0, donull
, TRUE
}, /* levelchange */
2837 { (char *) 0, (char *) 0, donull
, TRUE
}, /* lightsources */
2838 #ifdef DEBUG_MIGRATING_MONS
2839 { (char *) 0, (char *) 0, donull
, TRUE
}, /* migratemons */
2841 { (char *) 0, (char *) 0, donull
, TRUE
}, /* monpolycontrol */
2842 { (char *) 0, (char *) 0, donull
, TRUE
}, /* panic */
2843 { (char *) 0, (char *) 0, donull
, TRUE
}, /* polyself */
2845 { (char *) 0, (char *) 0, donull
, TRUE
}, /* portdebug */
2847 { (char *) 0, (char *) 0, donull
, TRUE
}, /* seenv */
2848 { (char *) 0, (char *) 0, donull
, TRUE
}, /* stats */
2849 { (char *) 0, (char *) 0, donull
, TRUE
}, /* timeout */
2850 { (char *) 0, (char *) 0, donull
, TRUE
}, /* vanquished */
2851 { (char *) 0, (char *) 0, donull
, TRUE
}, /* vision */
2852 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizsmell */
2853 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizintrinsic */
2855 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizdebug_traveldisplay */
2856 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizdebug_bury */
2858 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wizrumorcheck */
2859 { (char *) 0, (char *) 0, donull
, TRUE
}, /* wmode */
2860 { (char *) 0, (char *) 0, donull
, TRUE
} /* sentinel */
2863 /* there must be a placeholder in the table above for every entry here */
2864 static const struct ext_func_tab debug_extcmdlist
[] = {
2865 { "levelchange", "change experience level", wiz_level_change
, TRUE
},
2866 { "lightsources", "show mobile light sources", wiz_light_sources
, TRUE
},
2867 #ifdef DEBUG_MIGRATING_MONS
2868 { "migratemons", "migrate n random monsters", wiz_migrate_mons
, TRUE
},
2870 { "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol
,
2872 { "panic", "test panic routine (fatal to game)", wiz_panic
, TRUE
},
2873 { "polyself", "polymorph self", wiz_polyself
, TRUE
},
2875 { "portdebug", "wizard port debug command", wiz_port_debug
, TRUE
},
2877 { "seenv", "show seen vectors", wiz_show_seenv
, TRUE
},
2878 { "stats", "show memory statistics", wiz_show_stats
, TRUE
},
2879 { "timeout", "look at timeout queue", wiz_timeout_queue
, TRUE
},
2880 { "vanquished", "list vanquished monsters", dovanquished
, TRUE
},
2881 { "vision", "show vision array", wiz_show_vision
, TRUE
},
2882 { "wizsmell", "smell monster", wiz_smell
, TRUE
},
2883 { "wizintrinsic", "set intrinsic", wiz_intrinsic
, TRUE
},
2885 { "wizdebug_traveldisplay", "wizard debug: toggle travel display",
2886 wiz_debug_cmd_traveldisplay
, TRUE
},
2887 { "wizdebug_bury", "wizard debug: bury objs under and around you",
2888 wiz_debug_cmd_bury
, TRUE
},
2890 { "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check
, TRUE
},
2891 { "wmode", "show wall modes", wiz_show_wmodes
, TRUE
},
2892 { (char *) 0, (char *) 0, donull
, TRUE
}
2896 * Insert debug commands into the extended command list. This function
2897 * assumes that the last entry will be the help entry.
2899 * You must add entries in ext_func_tab every time you add one to the
2900 * debug_extcmdlist().
2903 add_debug_extended_commands()
2907 /* count the # of help entries */
2908 for (n
= 0; extcmdlist
[n
].ef_txt
[0] != '?'; n
++)
2911 for (i
= 0; debug_extcmdlist
[i
].ef_txt
; i
++) {
2912 /* need enough room for "?" entry plus terminator */
2913 if (n
+ 2 >= SIZE(extcmdlist
))
2914 panic("Too many debugging commands!");
2915 for (j
= 0; j
< n
; j
++)
2916 if (strcmp(debug_extcmdlist
[i
].ef_txt
, extcmdlist
[j
].ef_txt
) < 0)
2919 /* insert i'th debug entry into extcmdlist[j], pushing down */
2920 for (k
= n
; k
>= j
; --k
)
2921 extcmdlist
[k
+ 1] = extcmdlist
[k
];
2922 extcmdlist
[j
] = debug_extcmdlist
[i
];
2923 n
++; /* now an extra entry */
2932 for (i
= 0; i
< SIZE(cmdlist
); ++i
)
2933 if (cmdlist
[i
].f_funct
== fn
)
2934 return cmdlist
[i
].f_char
;
2938 static const char template[] = "%-27s %4ld %6ld";
2939 static const char stats_hdr
[] = " count bytes";
2940 static const char stats_sep
[] = "--------------------------- ----- -------";
2946 int sz
= (int) sizeof(struct obj
);
2949 sz
+= (int) sizeof(struct oextra
);
2951 sz
+= (int) strlen(ONAME(otmp
)) + 1;
2953 sz
+= (int) sizeof(struct monst
);
2955 sz
+= (int) sizeof(unsigned);
2957 sz
+= (int) sizeof(long);
2959 sz
+= (int) strlen(OMAILCMD(otmp
)) + 1;
2965 count_obj(chain
, total_count
, total_size
, top
, recurse
)
2975 for (count
= size
= 0, obj
= chain
; obj
; obj
= obj
->nobj
) {
2978 size
+= size_obj(obj
);
2980 if (recurse
&& obj
->cobj
)
2981 count_obj(obj
->cobj
, total_count
, total_size
, TRUE
, TRUE
);
2983 *total_count
+= count
;
2984 *total_size
+= size
;
2988 obj_chain(win
, src
, chain
, force
, total_count
, total_size
)
2997 long count
= 0L, size
= 0L;
2999 count_obj(chain
, &count
, &size
, TRUE
, FALSE
);
3001 if (count
|| size
|| force
) {
3002 *total_count
+= count
;
3003 *total_size
+= size
;
3004 Sprintf(buf
, template, src
, count
, size
);
3005 putstr(win
, 0, buf
);
3010 mon_invent_chain(win
, src
, chain
, total_count
, total_size
)
3013 struct monst
*chain
;
3018 long count
= 0, size
= 0;
3021 for (mon
= chain
; mon
; mon
= mon
->nmon
)
3022 count_obj(mon
->minvent
, &count
, &size
, TRUE
, FALSE
);
3024 if (count
|| size
) {
3025 *total_count
+= count
;
3026 *total_size
+= size
;
3027 Sprintf(buf
, template, src
, count
, size
);
3028 putstr(win
, 0, buf
);
3033 contained_stats(win
, src
, total_count
, total_size
)
3040 long count
= 0, size
= 0;
3043 count_obj(invent
, &count
, &size
, FALSE
, TRUE
);
3044 count_obj(fobj
, &count
, &size
, FALSE
, TRUE
);
3045 count_obj(level
.buriedobjlist
, &count
, &size
, FALSE
, TRUE
);
3046 count_obj(migrating_objs
, &count
, &size
, FALSE
, TRUE
);
3047 /* DEADMONSTER check not required in this loop since they have no
3049 for (mon
= fmon
; mon
; mon
= mon
->nmon
)
3050 count_obj(mon
->minvent
, &count
, &size
, FALSE
, TRUE
);
3051 for (mon
= migrating_mons
; mon
; mon
= mon
->nmon
)
3052 count_obj(mon
->minvent
, &count
, &size
, FALSE
, TRUE
);
3054 if (count
|| size
) {
3055 *total_count
+= count
;
3056 *total_size
+= size
;
3057 Sprintf(buf
, template, src
, count
, size
);
3058 putstr(win
, 0, buf
);
3063 size_monst(mtmp
, incl_wsegs
)
3067 int sz
= (int) sizeof (struct monst
);
3069 if (mtmp
->wormno
&& incl_wsegs
)
3070 sz
+= size_wseg(mtmp
);
3073 sz
+= (int) sizeof (struct mextra
);
3075 sz
+= (int) strlen(MNAME(mtmp
)) + 1;
3077 sz
+= (int) sizeof (struct egd
);
3079 sz
+= (int) sizeof (struct epri
);
3081 sz
+= (int) sizeof (struct eshk
);
3083 sz
+= (int) sizeof (struct emin
);
3085 sz
+= (int) sizeof (struct edog
);
3086 /* mextra->mcorpsenm doesn't point to more memory */
3092 mon_chain(win
, src
, chain
, force
, total_count
, total_size
)
3095 struct monst
*chain
;
3103 /* mon->wormno means something different for migrating_mons and mydogs */
3104 boolean incl_wsegs
= !strcmpi(src
, "fmon");
3107 for (mon
= chain
; mon
; mon
= mon
->nmon
) {
3109 size
+= size_monst(mon
, incl_wsegs
);
3111 if (count
|| size
|| force
) {
3112 *total_count
+= count
;
3113 *total_size
+= size
;
3114 Sprintf(buf
, template, src
, count
, size
);
3115 putstr(win
, 0, buf
);
3120 misc_stats(win
, total_count
, total_size
)
3125 char buf
[BUFSZ
], hdrbuf
[QBUFSZ
];
3129 struct damage
*sd
; /* shop damage */
3130 struct cemetery
*bi
; /* bones info */
3132 /* traps and engravings are output unconditionally;
3133 * others only if nonzero
3136 for (tt
= ftrap
; tt
; tt
= tt
->ntrap
) {
3138 size
+= (long) sizeof *tt
;
3140 *total_count
+= count
;
3141 *total_size
+= size
;
3142 Sprintf(hdrbuf
, "traps, size %ld", (long) sizeof (struct trap
));
3143 Sprintf(buf
, template, hdrbuf
, count
, size
);
3144 putstr(win
, 0, buf
);
3147 engr_stats("engravings, size %ld+text", hdrbuf
, &count
, &size
);
3148 *total_count
+= count
;
3149 *total_size
+= size
;
3150 Sprintf(buf
, template, hdrbuf
, count
, size
);
3151 putstr(win
, 0, buf
);
3154 light_stats("light sources, size %ld", hdrbuf
, &count
, &size
);
3155 if (count
|| size
) {
3156 *total_count
+= count
;
3157 *total_size
+= size
;
3158 Sprintf(buf
, template, hdrbuf
, count
, size
);
3159 putstr(win
, 0, buf
);
3163 timer_stats("timers, size %ld", hdrbuf
, &count
, &size
);
3164 if (count
|| size
) {
3165 *total_count
+= count
;
3166 *total_size
+= size
;
3167 Sprintf(buf
, template, hdrbuf
, count
, size
);
3168 putstr(win
, 0, buf
);
3172 for (sd
= level
.damagelist
; sd
; sd
= sd
->next
) {
3174 size
+= (long) sizeof *sd
;
3176 if (count
|| size
) {
3177 *total_count
+= count
;
3178 *total_size
+= size
;
3179 Sprintf(hdrbuf
, "shop damage, size %ld",
3180 (long) sizeof (struct damage
));
3181 Sprintf(buf
, template, hdrbuf
, count
, size
);
3182 putstr(win
, 0, buf
);
3186 region_stats("regions, size %ld+%ld*rect+N", hdrbuf
, &count
, &size
);
3187 if (count
|| size
) {
3188 *total_count
+= count
;
3189 *total_size
+= size
;
3190 Sprintf(buf
, template, hdrbuf
, count
, size
);
3191 putstr(win
, 0, buf
);
3195 for (bi
= level
.bonesinfo
; bi
; bi
= bi
->next
) {
3197 size
+= (long) sizeof *bi
;
3199 if (count
|| size
) {
3200 *total_count
+= count
;
3201 *total_size
+= size
;
3202 Sprintf(hdrbuf
, "bones history, size %ld",
3203 (long) sizeof (struct cemetery
));
3204 Sprintf(buf
, template, hdrbuf
, count
, size
);
3205 putstr(win
, 0, buf
);
3209 for (idx
= 0; idx
< NUM_OBJECTS
; ++idx
)
3210 if (objects
[idx
].oc_uname
) {
3212 size
+= (long) (strlen(objects
[idx
].oc_uname
) + 1);
3214 if (count
|| size
) {
3215 *total_count
+= count
;
3216 *total_size
+= size
;
3217 Strcpy(hdrbuf
, "object type names, text");
3218 Sprintf(buf
, template, hdrbuf
, count
, size
);
3219 putstr(win
, 0, buf
);
3224 * Display memory usage of all monsters and objects on the level.
3231 long total_obj_size
, total_obj_count
,
3232 total_mon_size
, total_mon_count
,
3233 total_ovr_size
, total_ovr_count
,
3234 total_misc_size
, total_misc_count
;
3236 win
= create_nhwindow(NHW_TEXT
);
3237 putstr(win
, 0, "Current memory statistics:");
3239 total_obj_count
= total_obj_size
= 0L;
3240 putstr(win
, 0, stats_hdr
);
3241 Sprintf(buf
, " Objects, base size %ld", (long) sizeof (struct obj
));
3242 putstr(win
, 0, buf
);
3243 obj_chain(win
, "invent", invent
, TRUE
, &total_obj_count
, &total_obj_size
);
3244 obj_chain(win
, "fobj", fobj
, TRUE
, &total_obj_count
, &total_obj_size
);
3245 obj_chain(win
, "buried", level
.buriedobjlist
, FALSE
,
3246 &total_obj_count
, &total_obj_size
);
3247 obj_chain(win
, "migrating obj", migrating_objs
, FALSE
,
3248 &total_obj_count
, &total_obj_size
);
3249 obj_chain(win
, "billobjs", billobjs
, FALSE
,
3250 &total_obj_count
, &total_obj_size
);
3251 mon_invent_chain(win
, "minvent", fmon
, &total_obj_count
, &total_obj_size
);
3252 mon_invent_chain(win
, "migrating minvent", migrating_mons
,
3253 &total_obj_count
, &total_obj_size
);
3254 contained_stats(win
, "contained", &total_obj_count
, &total_obj_size
);
3255 putstr(win
, 0, stats_sep
);
3256 Sprintf(buf
, template, " Obj total", total_obj_count
, total_obj_size
);
3257 putstr(win
, 0, buf
);
3259 total_mon_count
= total_mon_size
= 0L;
3261 Sprintf(buf
, " Monsters, base size %ld", (long) sizeof (struct monst
));
3262 putstr(win
, 0, buf
);
3263 mon_chain(win
, "fmon", fmon
, TRUE
, &total_mon_count
, &total_mon_size
);
3264 mon_chain(win
, "migrating", migrating_mons
, FALSE
,
3265 &total_mon_count
, &total_mon_size
);
3266 /* 'mydogs' is only valid during level change or end of game disclosure,
3267 but conceivably we've been called from within debugger at such time */
3268 if (mydogs
) /* monsters accompanying hero */
3269 mon_chain(win
, "mydogs", mydogs
, FALSE
,
3270 &total_mon_count
, &total_mon_size
);
3271 putstr(win
, 0, stats_sep
);
3272 Sprintf(buf
, template, " Mon total", total_mon_count
, total_mon_size
);
3273 putstr(win
, 0, buf
);
3275 total_ovr_count
= total_ovr_size
= 0L;
3277 putstr(win
, 0, " Overview");
3278 overview_stats(win
, template, &total_ovr_count
, &total_ovr_size
);
3279 putstr(win
, 0, stats_sep
);
3280 Sprintf(buf
, template, " Over total", total_ovr_count
, total_ovr_size
);
3281 putstr(win
, 0, buf
);
3283 total_misc_count
= total_misc_size
= 0L;
3285 putstr(win
, 0, " Miscellaneous");
3286 misc_stats(win
, &total_misc_count
, &total_misc_size
);
3287 putstr(win
, 0, stats_sep
);
3288 Sprintf(buf
, template, " Misc total", total_misc_count
, total_misc_size
);
3289 putstr(win
, 0, buf
);
3292 putstr(win
, 0, stats_sep
);
3293 Sprintf(buf
, template, " Grand total",
3294 (total_obj_count
+ total_mon_count
3295 + total_ovr_count
+ total_misc_count
),
3296 (total_obj_size
+ total_mon_size
3297 + total_ovr_size
+ total_misc_size
));
3298 putstr(win
, 0, buf
);
3300 #if defined(__BORLANDC__) && !defined(_WIN32)
3301 show_borlandc_stats(win
);
3304 display_nhwindow(win
, FALSE
);
3305 destroy_nhwindow(win
);
3313 timer_sanity_check();
3315 light_sources_sanity_check();
3318 #ifdef DEBUG_MIGRATING_MONS
3324 struct permonst
*ptr
;
3328 getlin("How many random monsters to migrate? [0]", inbuf
);
3329 if (*inbuf
== '\033')
3331 mcount
= atoi(inbuf
);
3332 if (mcount
< 0 || mcount
> (COLNO
* ROWNO
) || Is_botlevel(&u
.uz
))
3334 while (mcount
> 0) {
3335 if (Is_stronghold(&u
.uz
))
3336 assign_level(&tolevel
, &valley_level
);
3338 get_level(&tolevel
, depth(&u
.uz
) + 1);
3340 mtmp
= makemon(ptr
, 0, 0, NO_MM_FLAGS
);
3342 migrate_to_level(mtmp
, ledger_no(&tolevel
), MIGR_RANDOM
,
3350 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
3351 #define unmeta(c) (0x7f & (c))
3353 /* called at startup and after number_pad is twiddled */
3355 reset_commands(initial
)
3358 static const char sdir
[] = "hykulnjb><",
3359 sdir_swap_yz
[] = "hzkulnjb><",
3360 ndir
[] = "47896321><",
3361 ndir_phone_layout
[] = "41236987><";
3362 static const int ylist
[] = {
3363 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
3365 const struct func_tab
*cmdtmp
;
3367 int c
, i
, updated
= 0;
3371 for (i
= 0; i
< SIZE(cmdlist
); i
++) {
3372 c
= cmdlist
[i
].f_char
& 0xff;
3373 Cmd
.commands
[c
] = &cmdlist
[i
];
3375 Cmd
.num_pad
= FALSE
;
3376 Cmd
.pcHack_compat
= Cmd
.phone_layout
= Cmd
.swap_yz
= FALSE
;
3379 flagtemp
= iflags
.num_pad
;
3380 if (flagtemp
!= Cmd
.num_pad
) {
3381 Cmd
.num_pad
= flagtemp
;
3384 /* swap_yz mode (only applicable for !num_pad); intended for
3385 QWERTZ keyboard used in Central Europe, particularly Germany */
3386 flagtemp
= (iflags
.num_pad_mode
& 1) ? !Cmd
.num_pad
: FALSE
;
3387 if (flagtemp
!= Cmd
.swap_yz
) {
3388 Cmd
.swap_yz
= flagtemp
;
3390 /* Cmd.swap_yz has been toggled;
3391 perform the swap (or reverse previous one) */
3392 for (i
= 0; i
< SIZE(ylist
); i
++) {
3393 c
= ylist
[i
] & 0xff;
3394 cmdtmp
= Cmd
.commands
[c
]; /* tmp = [y] */
3395 Cmd
.commands
[c
] = Cmd
.commands
[c
+ 1]; /* [y] = [z] */
3396 Cmd
.commands
[c
+ 1] = cmdtmp
; /* [z] = tmp */
3399 /* MSDOS compatibility mode (only applicable for num_pad) */
3400 flagtemp
= (iflags
.num_pad_mode
& 1) ? Cmd
.num_pad
: FALSE
;
3401 if (flagtemp
!= Cmd
.pcHack_compat
) {
3402 Cmd
.pcHack_compat
= flagtemp
;
3404 /* pcHack_compat has been toggled */
3406 cmdtmp
= Cmd
.commands
['5'];
3407 Cmd
.commands
['5'] = Cmd
.commands
[c
];
3408 Cmd
.commands
[c
] = cmdtmp
;
3410 Cmd
.commands
[c
] = Cmd
.pcHack_compat
? Cmd
.commands
['I'] : 0;
3412 /* phone keypad layout (only applicable for num_pad) */
3413 flagtemp
= (iflags
.num_pad_mode
& 2) ? Cmd
.num_pad
: FALSE
;
3414 if (flagtemp
!= Cmd
.phone_layout
) {
3415 Cmd
.phone_layout
= flagtemp
;
3417 /* phone_layout has been toggled */
3418 for (i
= 0; i
< 3; i
++) {
3419 c
= '1' + i
; /* 1,2,3 <-> 7,8,9 */
3420 cmdtmp
= Cmd
.commands
[c
]; /* tmp = [1] */
3421 Cmd
.commands
[c
] = Cmd
.commands
[c
+ 6]; /* [1] = [7] */
3422 Cmd
.commands
[c
+ 6] = cmdtmp
; /* [7] = tmp */
3423 c
= (M('1') & 0xff) + i
; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
3424 cmdtmp
= Cmd
.commands
[c
]; /* tmp = [M-1] */
3425 Cmd
.commands
[c
] = Cmd
.commands
[c
+ 6]; /* [M-1] = [M-7] */
3426 Cmd
.commands
[c
+ 6] = cmdtmp
; /* [M-7] = tmp */
3433 Cmd
.dirchars
= !Cmd
.num_pad
3434 ? (!Cmd
.swap_yz
? sdir
: sdir_swap_yz
)
3435 : (!Cmd
.phone_layout
? ndir
: ndir_phone_layout
);
3436 Cmd
.alphadirchars
= !Cmd
.num_pad
? Cmd
.dirchars
: sdir
;
3438 Cmd
.move_W
= Cmd
.dirchars
[0];
3439 Cmd
.move_NW
= Cmd
.dirchars
[1];
3440 Cmd
.move_N
= Cmd
.dirchars
[2];
3441 Cmd
.move_NE
= Cmd
.dirchars
[3];
3442 Cmd
.move_E
= Cmd
.dirchars
[4];
3443 Cmd
.move_SE
= Cmd
.dirchars
[5];
3444 Cmd
.move_S
= Cmd
.dirchars
[6];
3445 Cmd
.move_SW
= Cmd
.dirchars
[7];
3448 /* non-movement commands which accept 'm' prefix to request menu operation */
3450 accept_menu_prefix(cmd_func
)
3451 int NDECL((*cmd_func
));
3453 if (cmd_func
== dopickup
|| cmd_func
== dotip
3454 /* eat, #offer, and apply tinning-kit all use floorfood() to pick
3455 an item on floor or in invent; 'm' skips picking from floor
3456 (ie, inventory only) rather than request use of menu operation */
3457 || cmd_func
== doeat
|| cmd_func
== dosacrifice
|| cmd_func
== doapply
3458 /* 'm' for removing saddle from adjacent monster without checking
3459 for containers at <u.ux,u.uy> */
3460 || cmd_func
== doloot
3461 /* 'm' prefix allowed for some extended commands */
3462 || cmd_func
== doextcmd
|| cmd_func
== doextlist
)
3471 boolean do_walk
, do_rush
, prefix_seen
, bad_command
,
3472 firsttime
= (cmd
== 0);
3474 iflags
.menu_requested
= FALSE
;
3476 if (program_state
.done_hup
)
3483 if (*cmd
== '\033') {
3484 context
.move
= FALSE
;
3487 if (*cmd
== DOAGAIN
&& !in_doagain
&& saveq
[0]) {
3490 rhack((char *) 0); /* read and execute command */
3494 /* Special case of *cmd == ' ' handled better below */
3495 if (!*cmd
|| *cmd
== (char) 0377) {
3497 context
.move
= FALSE
;
3498 return; /* probably we just had an interrupt */
3501 /* handle most movement commands */
3502 do_walk
= do_rush
= prefix_seen
= FALSE
;
3503 context
.travel
= context
.travel1
= 0;
3506 if (movecmd(cmd
[1])) {
3514 break; /* else FALLTHRU */
3516 if (movecmd(lowc(cmd
[1]))) {
3524 break; /* else FALLTHRU */
3525 /* Effects of movement commands and invisible monsters:
3526 * m: always move onto space (even if 'I' remembered)
3527 * F: always attack space (even if 'I' not remembered)
3528 * normal movement: attack if 'I', move otherwise.
3531 if (movecmd(cmd
[1])) {
3532 context
.forcefight
= 1;
3538 if (movecmd(cmd
[1]) || u
.dz
) {
3544 cmd
[0] = cmd
[1]; /* "m<" or "m>" */
3549 if (movecmd(lowc(cmd
[1]))) {
3559 (void) ddoinv(); /* a convenience borrowed from the PC */
3560 context
.move
= FALSE
;
3564 if (iflags
.clicklook
) {
3565 context
.move
= FALSE
;
3566 do_look(2, &clicklook_cc
);
3570 if (flags
.travelcmd
) {
3572 context
.travel1
= 1;
3580 if (movecmd(*cmd
)) { /* ordinary movement */
3581 context
.run
= 0; /* only matters here if it was 8 */
3583 } else if (movecmd(Cmd
.num_pad
? unmeta(*cmd
) : lowc(*cmd
))) {
3586 } else if (movecmd(unctrl(*cmd
))) {
3593 /* some special prefix handling */
3594 /* overload 'm' prefix to mean "request a menu" */
3595 if (prefix_seen
&& cmd
[0] == 'm') {
3596 /* (for func_tab cast, see below) */
3597 const struct func_tab
*ft
= Cmd
.commands
[cmd
[1] & 0xff];
3598 int NDECL((*func
)) = ft
? ((struct func_tab
*) ft
)->f_funct
: 0;
3600 if (func
&& accept_menu_prefix(func
)) {
3601 iflags
.menu_requested
= TRUE
;
3606 if ((do_walk
|| do_rush
) && !context
.travel
&& !dxdy_moveok()) {
3607 /* trying to move diagonally as a grid bug;
3608 this used to be treated by movecmd() as not being
3609 a movement attempt, but that didn't provide for any
3610 feedback and led to strangeness if the key pressed
3611 ('u' in particular) was overloaded for num_pad use */
3612 You_cant("get there from here...");
3614 context
.nopick
= context
.forcefight
= FALSE
;
3615 context
.move
= context
.mv
= FALSE
;
3624 context
.forcefight
= 0;
3626 } else if (do_rush
) {
3629 multi
= max(COLNO
, ROWNO
);
3630 u
.last_str_turn
= 0;
3635 } else if (prefix_seen
&& cmd
[1] == '\033') { /* <prefix><escape> */
3636 /* don't report "unknown command" for change of heart... */
3637 bad_command
= FALSE
;
3638 } else if (*cmd
== ' ' && !flags
.rest_on_space
) {
3639 bad_command
= TRUE
; /* skip cmdlist[] loop */
3641 /* handle all other commands */
3643 register const struct func_tab
*tlist
;
3644 int res
, NDECL((*func
));
3646 /* current - use *cmd to directly index cmdlist array */
3647 if ((tlist
= Cmd
.commands
[*cmd
& 0xff]) != 0) {
3648 if (u
.uburied
&& !tlist
->can_if_buried
) {
3649 You_cant("do that while you are buried!");
3652 /* we discard 'const' because some compilers seem to have
3653 trouble with the pointer passed to set_occupation() */
3654 func
= ((struct func_tab
*) tlist
)->f_funct
;
3655 if (tlist
->f_text
&& !occupation
&& multi
)
3656 set_occupation(func
, tlist
->f_text
, multi
);
3657 res
= (*func
)(); /* perform the command */
3660 context
.move
= FALSE
;
3665 /* if we reach here, cmd wasn't found in cmdlist[] */
3670 char expcmd
[20]; /* we expect 'cmd' to point to 1 or 2 chars */
3674 while ((c
= *cmd
++) != '\0')
3675 Strcat(expcmd
, visctrl(c
)); /* add 1..4 chars plus terminator */
3677 if (!prefix_seen
|| !iflags
.cmdassist
3678 || !help_dir(0, "Invalid direction key!"))
3679 Norep("Unknown command '%s'.", expcmd
);
3682 context
.move
= FALSE
;
3687 /* convert an x,y pair into a direction code */
3694 for (dd
= 0; dd
< 8; dd
++)
3695 if (x
== xdir
[dd
] && y
== ydir
[dd
])
3700 /* convert a direction code into an x,y pair */
3711 /* also sets u.dz, but returns false for <> */
3716 register const char *dp
= index(Cmd
.dirchars
, sym
);
3721 u
.dx
= xdir
[dp
- Cmd
.dirchars
];
3722 u
.dy
= ydir
[dp
- Cmd
.dirchars
];
3723 u
.dz
= zdir
[dp
- Cmd
.dirchars
];
3724 #if 0 /* now handled elsewhere */
3725 if (u
.dx
&& u
.dy
&& NODIAG(u
.umonnum
)) {
3733 /* grid bug handling which used to be in movecmd() */
3737 if (u
.dx
&& u
.dy
&& NODIAG(u
.umonnum
))
3739 return u
.dx
|| u
.dy
;
3742 /* decide whether a character (user input keystroke) requests screen repaint */
3747 return (boolean
) (c
== C('r') || (Cmd
.num_pad
&& c
== C('l')));
3754 return (boolean
) (c
== 'g' || c
== 'G'
3755 || c
== 'm' || c
== 'M'
3757 || (Cmd
.num_pad
&& (c
== '5' || c
== '-')));
3761 * uses getdir() but unlike getdir() it specifically
3762 * produces coordinates using the direction from getdir()
3763 * and verifies that those coordinates are ok.
3765 * If the call to getdir() returns 0, Never_mind is displayed.
3766 * If the resulting coordinates are not okay, emsg is displayed.
3768 * Returns non-zero if coordinates in cc are valid.
3771 get_adjacent_loc(prompt
, emsg
, x
, y
, cc
)
3772 const char *prompt
, *emsg
;
3777 if (!getdir(prompt
)) {
3783 if (cc
&& isok(new_x
, new_y
)) {
3802 if (in_doagain
|| *readchar_queue
)
3803 dirsym
= readchar();
3805 dirsym
= yn_function((s
&& *s
!= '^') ? s
: "In what direction?",
3807 /* remove the prompt string so caller won't have to */
3808 clear_nhwindow(WIN_MESSAGE
);
3810 if (redraw_cmd(dirsym
)) { /* ^R */
3811 docrt(); /* redraw */
3816 if (dirsym
== '.' || dirsym
== 's') {
3817 u
.dx
= u
.dy
= u
.dz
= 0;
3818 } else if (!(is_mov
= movecmd(dirsym
)) && !u
.dz
) {
3819 boolean did_help
= FALSE
, help_requested
;
3821 if (!index(quitchars
, dirsym
)) {
3822 help_requested
= (dirsym
== '?');
3823 if (help_requested
|| iflags
.cmdassist
) {
3824 did_help
= help_dir((s
&& *s
== '^') ? dirsym
: 0,
3825 help_requested
? (const char *) 0
3826 : "Invalid direction key!");
3831 pline("What a strange direction!");
3834 } else if (is_mov
&& !dxdy_moveok()) {
3835 You_cant("orient yourself that direction.");
3838 if (!u
.dz
&& (Stunned
|| (Confusion
&& !rn2(5))))
3844 show_direction_keys(win
, nodiag
)
3851 Sprintf(buf
, " %c ", Cmd
.move_N
);
3852 putstr(win
, 0, buf
);
3853 putstr(win
, 0, " | ");
3854 Sprintf(buf
, " %c- . -%c", Cmd
.move_W
, Cmd
.move_E
);
3855 putstr(win
, 0, buf
);
3856 putstr(win
, 0, " | ");
3857 Sprintf(buf
, " %c ", Cmd
.move_S
);
3858 putstr(win
, 0, buf
);
3860 Sprintf(buf
, " %c %c %c", Cmd
.move_NW
, Cmd
.move_N
,
3862 putstr(win
, 0, buf
);
3863 putstr(win
, 0, " \\ | / ");
3864 Sprintf(buf
, " %c- . -%c", Cmd
.move_W
, Cmd
.move_E
);
3865 putstr(win
, 0, buf
);
3866 putstr(win
, 0, " / | \\ ");
3867 Sprintf(buf
, " %c %c %c", Cmd
.move_SW
, Cmd
.move_S
,
3869 putstr(win
, 0, buf
);
3879 static const char wiz_only_list
[] = "EFGIVW";
3882 char buf
[BUFSZ
], buf2
[BUFSZ
], *explain
;
3884 win
= create_nhwindow(NHW_TEXT
);
3888 Sprintf(buf
, "cmdassist: %s", msg
);
3889 putstr(win
, 0, buf
);
3892 if (letter(sym
) || sym
== '[') { /* 'dat/cmdhelp' shows ESC as ^[ */
3893 sym
= highc(sym
); /* @A-Z[ (note: letter() accepts '@') */
3894 ctrl
= (sym
- 'A') + 1; /* 0-27 (note: 28-31 aren't applicable) */
3895 if ((explain
= dowhatdoes_core(ctrl
, buf2
)) != 0
3896 && (!index(wiz_only_list
, sym
) || wizard
)) {
3897 Sprintf(buf
, "Are you trying to use ^%c%s?", sym
,
3898 index(wiz_only_list
, sym
)
3900 : " as specified in the Guidebook");
3901 putstr(win
, 0, buf
);
3903 putstr(win
, 0, explain
);
3906 "To use that command, hold down the <Ctrl> key as a shift");
3907 Sprintf(buf
, "and press the <%c> key.", sym
);
3908 putstr(win
, 0, buf
);
3913 Sprintf(buf
, "Valid direction keys %sare:",
3914 NODIAG(u
.umonnum
) ? "in your current form " : "");
3915 putstr(win
, 0, buf
);
3916 show_direction_keys(win
, NODIAG(u
.umonnum
));
3919 putstr(win
, 0, " < up");
3920 putstr(win
, 0, " > down");
3921 putstr(win
, 0, " . direct at yourself");
3923 /* non-null msg means that this wasn't an explicit user request */
3926 "(Suppress this message with !cmdassist in config file.)");
3928 display_nhwindow(win
, FALSE
);
3929 destroy_nhwindow(win
);
3936 register int x
= NODIAG(u
.umonnum
) ? 2 * rn2(4) : rn2(8);
3947 static NEARDATA
const char *const dirnames
[] = {
3948 "west", "northwest", "north", "northeast", "east",
3949 "southeast", "south", "southwest", "down", "up",
3952 if (dir
< 0 || dir
>= SIZE(dirnames
))
3954 return dirnames
[dir
];
3961 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
3962 return x
>= 1 && x
<= COLNO
- 1 && y
>= 0 && y
<= ROWNO
- 1;
3965 static NEARDATA
int last_multi
;
3968 * convert a MAP window position into a movecmd
3971 click_to_cmd(x
, y
, mod
)
3978 if (iflags
.clicklook
&& mod
== CLICK_2
) {
3981 cmd
[0] = CMD_CLICKLOOK
;
3988 if (flags
.travelcmd
) {
3989 if (abs(x
) <= 1 && abs(y
) <= 1) {
3990 x
= sgn(x
), y
= sgn(y
);
3994 cmd
[0] = CMD_TRAVEL
;
3998 if (x
== 0 && y
== 0) {
4000 if (IS_FOUNTAIN(levl
[u
.ux
][u
.uy
].typ
)
4001 || IS_SINK(levl
[u
.ux
][u
.uy
].typ
)) {
4002 cmd
[0] = mod
== CLICK_1
? 'q' : M('d');
4004 } else if (IS_THRONE(levl
[u
.ux
][u
.uy
].typ
)) {
4007 } else if ((u
.ux
== xupstair
&& u
.uy
== yupstair
)
4008 || (u
.ux
== sstairs
.sx
&& u
.uy
== sstairs
.sy
4010 || (u
.ux
== xupladder
&& u
.uy
== yupladder
)) {
4012 } else if ((u
.ux
== xdnstair
&& u
.uy
== ydnstair
)
4013 || (u
.ux
== sstairs
.sx
&& u
.uy
== sstairs
.sy
4015 || (u
.ux
== xdnladder
&& u
.uy
== ydnladder
)) {
4017 } else if (OBJ_AT(u
.ux
, u
.uy
)) {
4019 Is_container(level
.objects
[u
.ux
][u
.uy
]) ? M('l') : ',';
4022 return "."; /* just rest */
4026 /* directional commands */
4030 if (!m_at(u
.ux
+ x
, u
.uy
+ y
)
4031 && !test_move(u
.ux
, u
.uy
, x
, y
, TEST_MOVE
)) {
4032 cmd
[1] = Cmd
.dirchars
[dir
];
4034 if (IS_DOOR(levl
[u
.ux
+ x
][u
.uy
+ y
].typ
)) {
4035 /* slight assistance to the player: choose kick/open for them
4037 if (levl
[u
.ux
+ x
][u
.uy
+ y
].doormask
& D_LOCKED
) {
4041 if (levl
[u
.ux
+ x
][u
.uy
+ y
].doormask
& D_CLOSED
) {
4046 if (levl
[u
.ux
+ x
][u
.uy
+ y
].typ
<= SCORR
) {
4053 /* convert without using floating point, allowing sloppy clicking */
4056 else if (y
> 2 * abs(x
))
4058 else if (x
< -2 * abs(y
))
4060 else if (y
< -2 * abs(x
))
4063 x
= sgn(x
), y
= sgn(y
);
4065 if (x
== 0 && y
== 0) /* map click on player to "rest" command */
4071 /* move, attack, etc. */
4073 if (mod
== CLICK_1
) {
4074 cmd
[0] = Cmd
.dirchars
[dir
];
4076 cmd
[0] = (Cmd
.num_pad
4077 ? M(Cmd
.dirchars
[dir
])
4078 : (Cmd
.dirchars
[dir
] - 'a' + 'A')); /* run command */
4085 get_count(allowchars
, inkey
, maxcount
, count
)
4094 boolean backspaced
= FALSE
;
4095 /* this should be done in port code so that we have erase_char
4096 and kill_char available; we can at least fake erase_char */
4097 #define STANDBY_erase_char '\177'
4107 cnt
= 10L * cnt
+ (long) (key
- '0');
4110 else if (maxcount
> 0 && cnt
> maxcount
)
4112 } else if (cnt
&& (key
== '\b' || key
== STANDBY_erase_char
)) {
4115 } else if (key
== '\033') {
4117 } else if (!allowchars
|| index(allowchars
, key
)) {
4122 if (cnt
> 9 || backspaced
) {
4123 clear_nhwindow(WIN_MESSAGE
);
4124 if (backspaced
&& !cnt
) {
4125 Sprintf(qbuf
, "Count: ");
4127 Sprintf(qbuf
, "Count: %ld", cnt
);
4141 #ifdef LINT /* static char in_line[COLNO]; */
4142 char in_line
[COLNO
];
4144 static char in_line
[COLNO
];
4147 boolean prezero
= FALSE
;
4151 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
4154 alt_esc
= iflags
.altmeta
; /* readchar() hack */
4156 if (!Cmd
.num_pad
|| (foo
= readchar()) == 'n') {
4157 long tmpmulti
= multi
;
4159 foo
= get_count(NULL
, '\0', LARGEST_INT
, &tmpmulti
);
4160 last_multi
= multi
= tmpmulti
;
4163 alt_esc
= FALSE
; /* readchar() reset */
4166 if (foo
== '\033') { /* esc cancels count (TH) */
4167 clear_nhwindow(WIN_MESSAGE
);
4168 multi
= last_multi
= 0;
4169 } else if (foo
== DOAGAIN
|| in_doagain
) {
4173 savech(0); /* reset input queue */
4181 save_cm
= (char *) 0;
4183 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
4184 if (Cmd
.pcHack_compat
) {
4185 /* This handles very old inconsistent DOS/Windows behaviour
4186 in a different way: earlier, the keyboard handler mapped
4187 these, which caused counts to be strange when entered
4188 from the number pad. Now do not map them until here. */
4206 if (prefix_cmd(foo
)) {
4212 clear_nhwindow(WIN_MESSAGE
);
4214 in_line
[0] = '\033';
4218 #ifdef HANGUPHANDLING
4219 /* some very old systems, or descendents of such systems, expect signal
4220 handlers to have return type `int', but they don't actually inspect
4221 the return value so we should be safe using `void' unconditionally */
4224 hangup(sig_unused
) /* called as signal() handler, so sent at least one arg */
4225 int sig_unused UNUSED
;
4227 if (program_state
.exiting
)
4228 program_state
.in_moveloop
= 0;
4231 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
4232 and a couple of other places; actual hangup handling occurs then.
4233 This is 'safer' because it disallows certain cheats and also
4234 protects against losing objects in the process of being thrown,
4235 but also potentially riskier because the disconnected program
4236 must continue running longer before attempting a hangup save. */
4237 program_state
.done_hup
++;
4238 /* defer hangup iff game appears to be in progress */
4239 if (program_state
.in_moveloop
&& program_state
.something_worth_saving
)
4241 #endif /* SAFERHANGUP */
4248 #ifdef NOSAVEONHANGUP
4250 if (flags
.ins_chkpt
&& program_state
.something_worth_saving
)
4251 program_statue
.preserve_locks
= 1; /* keep files for recovery */
4253 program_state
.something_worth_saving
= 0; /* don't save */
4257 if (!program_state
.done_hup
++)
4259 if (program_state
.something_worth_saving
)
4261 if (iflags
.window_inited
)
4262 exit_nhwindows((char *) 0);
4264 terminate(EXIT_SUCCESS
);
4265 /*NOTREACHED*/ /* not necessarily true for vms... */
4268 #endif /* HANGUPHANDLING */
4274 int x
= u
.ux
, y
= u
.uy
, mod
= 0;
4276 if (*readchar_queue
)
4277 sym
= *readchar_queue
++;
4279 sym
= in_doagain
? pgetchar() : nh_poskey(&x
, &y
, &mod
);
4283 register int cnt
= NR_OF_EOFS
;
4285 * Some SYSV systems seem to return EOFs for various reasons
4286 * (?like when one hits break or for interrupted systemcalls?),
4287 * and we must see several before we quit.
4290 clearerr(stdin
); /* omit if clearerr is undefined */
4292 } while (--cnt
&& sym
== EOF
);
4294 #endif /* NR_OF_EOFS */
4297 #ifdef HANGUPHANDLING
4298 hangup(0); /* call end_of_input() or set program_state.done_hup */
4302 } else if (sym
== '\033' && alt_esc
) {
4303 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
4304 sym
= *readchar_queue
? *readchar_queue
++ : pgetchar();
4305 if (sym
== EOF
|| sym
== 0)
4307 else if (sym
!= '\033')
4308 sym
|= 0200; /* force 8th bit on */
4310 } else if (sym
== 0) {
4312 readchar_queue
= click_to_cmd(x
, y
, mod
);
4313 sym
= *readchar_queue
++;
4321 /* Keyboard travel command */
4325 if (!flags
.travelcmd
)
4328 cc
.x
= iflags
.travelcc
.x
;
4329 cc
.y
= iflags
.travelcc
.y
;
4330 if (cc
.x
== -1 && cc
.y
== -1) {
4331 /* No cached destination, start attempt from current position */
4335 iflags
.getloc_travelmode
= TRUE
;
4336 pline("Where do you want to travel to?");
4337 if (getpos(&cc
, TRUE
, "the desired destination") < 0) {
4338 /* user pressed ESC */
4339 iflags
.getloc_travelmode
= FALSE
;
4342 iflags
.getloc_travelmode
= FALSE
;
4343 iflags
.travelcc
.x
= u
.tx
= cc
.x
;
4344 iflags
.travelcc
.y
= u
.ty
= cc
.y
;
4345 cmd
[0] = CMD_TRAVEL
;
4346 readchar_queue
= cmd
;
4351 extern void NDECL(win32con_debug_keystrokes
);
4352 extern void NDECL(win32con_handler_info
);
4361 int num_menu_selections
;
4362 struct menu_selection_struct
{
4365 } menu_selections
[] = {
4367 { "test win32 keystrokes (tty only)", win32con_debug_keystrokes
},
4368 { "show keystroke handler information (tty only)",
4369 win32con_handler_info
},
4371 { (char *) 0, (void NDECL((*) )) 0 } /* array terminator */
4374 num_menu_selections
= SIZE(menu_selections
) - 1;
4375 if (num_menu_selections
> 0) {
4376 menu_item
*pick_list
;
4377 win
= create_nhwindow(NHW_MENU
);
4379 for (k
= 0; k
< num_menu_selections
; ++k
) {
4381 add_menu(win
, NO_GLYPH
, &any
, item
++, 0, ATR_NONE
,
4382 menu_selections
[k
].menutext
, MENU_UNSELECTED
);
4384 end_menu(win
, "Which port debugging feature?");
4385 n
= select_menu(win
, PICK_ONE
, &pick_list
);
4386 destroy_nhwindow(win
);
4388 n
= pick_list
[0].item
.a_int
- 1;
4389 free((genericptr_t
) pick_list
);
4390 /* execute the function */
4391 (*menu_selections
[n
].fn
)();
4394 pline("No port-specific debug capability defined.");
4397 #endif /*PORT_DEBUG*/
4400 * Parameter validator for generic yes/no function to prevent
4401 * the core from sending too long a prompt string to the
4402 * window port causing a buffer overflow there.
4405 yn_function(query
, resp
, def
)
4406 const char *query
, *resp
;
4411 iflags
.last_msg
= PLNMSG_UNKNOWN
; /* most recent pline is clobbered */
4413 /* maximum acceptable length is QBUFSZ-1 */
4414 if (strlen(query
) >= QBUFSZ
) {
4415 /* caller shouldn't have passed anything this long */
4416 paniclog("Query truncated: ", query
);
4417 (void) strncpy(qbuf
, query
, QBUFSZ
- 1 - 3);
4418 Strcpy(&qbuf
[QBUFSZ
- 1 - 3], "...");
4421 return (*windowprocs
.win_yn_function
)(query
, resp
, def
);
4424 /* for paranoid_confirm:quit,die,attack prompting */
4426 paranoid_query(be_paranoid
, prompt
)
4427 boolean be_paranoid
;
4430 boolean confirmed_ok
;
4432 /* when paranoid, player must respond with "yes" rather than just 'y'
4433 to give the go-ahead for this query; default is "no" unless the
4434 ParanoidConfirm flag is set in which case there's no default */
4436 char qbuf
[QBUFSZ
], ans
[BUFSZ
];
4437 const char *promptprefix
= "", *responsetype
= ParanoidConfirm
4440 int trylimit
= 6; /* 1 normal, 5 more with "Yes or No:" prefix */
4442 /* in addition to being paranoid about this particular
4443 query, we might be even more paranoid about all paranoia
4444 responses (ie, ParanoidConfirm is set) in which case we
4445 require "no" to reject in addition to "yes" to confirm
4446 (except we won't loop if response is ESC; it means no) */
4448 Sprintf(qbuf
, "%s%s %s", promptprefix
, prompt
, responsetype
);
4450 (void) mungspaces(ans
);
4451 confirmed_ok
= !strcmpi(ans
, "yes");
4452 if (confirmed_ok
|| *ans
== '\033')
4454 promptprefix
= "\"Yes\" or \"No\": ";
4455 } while (ParanoidConfirm
&& strcmpi(ans
, "no") && --trylimit
);
4457 confirmed_ok
= (yn(prompt
) == 'y');
4459 return confirmed_ok
;
4466 /* Does current window system support suspend? */
4467 if ((*windowprocs
.win_can_suspend
)()) {
4468 /* NB: SYSCF SHELLERS handled in port code. */
4472 Norep("Suspend command not available.");