1/*	SCCS Id: @(#)cmd.c	3.4	2003/02/06	*/
2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3/* NetHack may be freely redistributed.  See license for details. */
4
5#include "hack.h"
6#include "func_tab.h"
7/* #define DEBUG */	/* uncomment for debugging */
8
9/*
10 * Some systems may have getchar() return EOF for various reasons, and
11 * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
12 */
13#if defined(SYSV) || defined(DGUX) || defined(HPUX)
14#define NR_OF_EOFS	20
15#endif
16
17#define CMD_TRAVEL (char)0x90
18
19#ifdef NETHACK_DEBUG
20/*
21 * only one "wiz_debug_cmd" routine should be available (in whatever
22 * module you are trying to debug) or things are going to get rather
23 * hard to link :-)
24 */
25extern int NDECL(wiz_debug_cmd);
26#endif
27
28#ifdef DUMB	/* stuff commented out in extern.h, but needed here */
29extern int NDECL(doapply); /**/
30extern int NDECL(dorub); /**/
31extern int NDECL(dojump); /**/
32extern int NDECL(doextlist); /**/
33extern int NDECL(dodrop); /**/
34extern int NDECL(doddrop); /**/
35extern int NDECL(dodown); /**/
36extern int NDECL(doup); /**/
37extern int NDECL(donull); /**/
38extern int NDECL(dowipe); /**/
39extern int NDECL(do_mname); /**/
40extern int NDECL(ddocall); /**/
41extern int NDECL(dotakeoff); /**/
42extern int NDECL(doremring); /**/
43extern int NDECL(dowear); /**/
44extern int NDECL(doputon); /**/
45extern int NDECL(doddoremarm); /**/
46extern int NDECL(dokick); /**/
47extern int NDECL(dofire); /**/
48extern int NDECL(dothrow); /**/
49extern int NDECL(doeat); /**/
50extern int NDECL(done2); /**/
51extern int NDECL(doengrave); /**/
52extern int NDECL(dopickup); /**/
53extern int NDECL(ddoinv); /**/
54extern int NDECL(dotypeinv); /**/
55extern int NDECL(dolook); /**/
56extern int NDECL(doprgold); /**/
57extern int NDECL(doprwep); /**/
58extern int NDECL(doprarm); /**/
59extern int NDECL(doprring); /**/
60extern int NDECL(dopramulet); /**/
61extern int NDECL(doprtool); /**/
62extern int NDECL(dosuspend); /**/
63extern int NDECL(doforce); /**/
64extern int NDECL(doopen); /**/
65extern int NDECL(doclose); /**/
66extern int NDECL(dosh); /**/
67extern int NDECL(dodiscovered); /**/
68extern int NDECL(doset); /**/
69extern int NDECL(dotogglepickup); /**/
70extern int NDECL(dowhatis); /**/
71extern int NDECL(doquickwhatis); /**/
72extern int NDECL(dowhatdoes); /**/
73extern int NDECL(dohelp); /**/
74extern int NDECL(dohistory); /**/
75extern int NDECL(doloot); /**/
76extern int NDECL(dodrink); /**/
77extern int NDECL(dodip); /**/
78extern int NDECL(dosacrifice); /**/
79extern int NDECL(dopray); /**/
80extern int NDECL(doturn); /**/
81extern int NDECL(doredraw); /**/
82extern int NDECL(doread); /**/
83extern int NDECL(dosave); /**/
84extern int NDECL(dosearch); /**/
85extern int NDECL(doidtrap); /**/
86extern int NDECL(dopay); /**/
87extern int NDECL(dosit); /**/
88extern int NDECL(dotalk); /**/
89extern int NDECL(docast); /**/
90extern int NDECL(dovspell); /**/
91extern int NDECL(dotele); /**/
92extern int NDECL(dountrap); /**/
93extern int NDECL(doversion); /**/
94extern int NDECL(doextversion); /**/
95extern int NDECL(doswapweapon); /**/
96extern int NDECL(dowield); /**/
97extern int NDECL(dowieldquiver); /**/
98extern int NDECL(dozap); /**/
99extern int NDECL(doorganize); /**/
100#endif /* DUMB */
101
102#ifdef OVL1
103static int NDECL((*timed_occ_fn));
104#endif /* OVL1 */
105
106STATIC_PTR int NDECL(doprev_message);
107STATIC_PTR int NDECL(timed_occupation);
108STATIC_PTR int NDECL(doextcmd);
109STATIC_PTR int NDECL(domonability);
110STATIC_PTR int NDECL(dotravel);
111# ifdef WIZARD
112STATIC_PTR int NDECL(wiz_wish);
113STATIC_PTR int NDECL(wiz_identify);
114STATIC_PTR int NDECL(wiz_map);
115STATIC_PTR int NDECL(wiz_genesis);
116STATIC_PTR int NDECL(wiz_where);
117STATIC_PTR int NDECL(wiz_detect);
118STATIC_PTR int NDECL(wiz_panic);
119STATIC_PTR int NDECL(wiz_polyself);
120STATIC_PTR int NDECL(wiz_level_tele);
121STATIC_PTR int NDECL(wiz_level_change);
122STATIC_PTR int NDECL(wiz_show_seenv);
123STATIC_PTR int NDECL(wiz_show_vision);
124STATIC_PTR int NDECL(wiz_mon_polycontrol);
125STATIC_PTR int NDECL(wiz_show_wmodes);
126#if defined(__BORLANDC__) && !defined(_WIN32)
127extern void FDECL(show_borlandc_stats, (winid));
128#endif
129#ifdef NETHACK_DEBUG_MIGRATING_MONS
130STATIC_PTR int NDECL(wiz_migrate_mons);
131#endif
132STATIC_DCL void FDECL(count_obj, (struct obj *, long *, long *, BOOLEAN_P, BOOLEAN_P));
133STATIC_DCL void FDECL(obj_chain, (winid, const char *, struct obj *, long *, long *));
134STATIC_DCL void FDECL(mon_invent_chain, (winid, const char *, struct monst *, long *, long *));
135STATIC_DCL void FDECL(mon_chain, (winid, const char *, struct monst *, long *, long *));
136STATIC_DCL void FDECL(contained, (winid, const char *, long *, long *));
137STATIC_PTR int NDECL(wiz_show_stats);
138#  ifdef PORT_DEBUG
139STATIC_DCL int NDECL(wiz_port_debug);
140#  endif
141# endif
142STATIC_PTR int NDECL(enter_explore_mode);
143STATIC_PTR int NDECL(doattributes);
144STATIC_PTR int NDECL(doconduct); /**/
145STATIC_PTR boolean NDECL(minimal_enlightenment);
146
147#ifdef OVLB
148STATIC_DCL void FDECL(enlght_line, (const char *,const char *,const char *));
149STATIC_DCL char *FDECL(enlght_combatinc, (const char *,int,int,char *));
150#ifdef UNIX
151static void NDECL(end_of_input);
152#endif
153#endif /* OVLB */
154
155static const char* readchar_queue="";
156
157STATIC_DCL char *NDECL(parse);
158STATIC_DCL boolean FDECL(help_dir, (CHAR_P,const char *));
159
160#ifdef OVL1
161
162STATIC_PTR int
163doprev_message()
164{
165    return nh_doprev_message();
166}
167
168/* Count down by decrementing multi */
169STATIC_PTR int
170timed_occupation()
171{
172	(*timed_occ_fn)();
173	if (multi > 0)
174		multi--;
175	return multi > 0;
176}
177
178/* If you have moved since initially setting some occupations, they
179 * now shouldn't be able to restart.
180 *
181 * The basic rule is that if you are carrying it, you can continue
182 * since it is with you.  If you are acting on something at a distance,
183 * your orientation to it must have changed when you moved.
184 *
185 * The exception to this is taking off items, since they can be taken
186 * off in a number of ways in the intervening time, screwing up ordering.
187 *
188 *	Currently:	Take off all armor.
189 *			Picking Locks / Forcing Chests.
190 *			Setting traps.
191 */
192void
193reset_occupations()
194{
195	reset_remarm();
196	reset_pick();
197	reset_trapset();
198}
199
200/* If a time is given, use it to timeout this function, otherwise the
201 * function times out by its own means.
202 */
203void
204set_occupation(fn, txt, xtime)
205int NDECL((*fn));
206const char *txt;
207int xtime;
208{
209	if (xtime) {
210		occupation = timed_occupation;
211		timed_occ_fn = fn;
212	} else
213		occupation = fn;
214	occtxt = txt;
215	occtime = 0;
216	return;
217}
218
219#ifdef REDO
220
221static char NDECL(popch);
222
223/* Provide a means to redo the last command.  The flag `in_doagain' is set
224 * to true while redoing the command.  This flag is tested in commands that
225 * require additional input (like `throw' which requires a thing and a
226 * direction), and the input prompt is not shown.  Also, while in_doagain is
227 * TRUE, no keystrokes can be saved into the saveq.
228 */
229#define BSIZE 20
230static char pushq[BSIZE], saveq[BSIZE];
231static NEARDATA int phead, ptail, shead, stail;
232
233static char
234popch() {
235	/* If occupied, return '\0', letting tgetch know a character should
236	 * be read from the keyboard.  If the character read is not the
237	 * ABORT character (as checked in pcmain.c), that character will be
238	 * pushed back on the pushq.
239	 */
240	if (occupation) return '\0';
241	if (in_doagain) return(char)((shead != stail) ? saveq[stail++] : '\0');
242	else		return(char)((phead != ptail) ? pushq[ptail++] : '\0');
243}
244
245char
246pgetchar() {		/* curtesy of aeb@cwi.nl */
247	register int ch;
248
249	if(!(ch = popch()))
250		ch = nhgetch();
251	return((char)ch);
252}
253
254/* A ch == 0 resets the pushq */
255void
256pushch(ch)
257char ch;
258{
259	if (!ch)
260		phead = ptail = 0;
261	if (phead < BSIZE)
262		pushq[phead++] = ch;
263	return;
264}
265
266/* A ch == 0 resets the saveq.	Only save keystrokes when not
267 * replaying a previous command.
268 */
269void
270savech(ch)
271char ch;
272{
273	if (!in_doagain) {
274		if (!ch)
275			phead = ptail = shead = stail = 0;
276		else if (shead < BSIZE)
277			saveq[shead++] = ch;
278	}
279	return;
280}
281#endif /* REDO */
282
283#endif /* OVL1 */
284#ifdef OVLB
285
286STATIC_PTR int
287doextcmd()	/* here after # - now read a full-word command */
288{
289	int idx, retval;
290
291	/* keep repeating until we don't run help or quit */
292	do {
293	    idx = get_ext_cmd();
294	    if (idx < 0) return 0;	/* quit */
295
296	    retval = (*extcmdlist[idx].ef_funct)();
297	} while (extcmdlist[idx].ef_funct == doextlist);
298
299	return retval;
300}
301
302int
303doextlist()	/* here after #? - now list all full-word commands */
304{
305	register const struct ext_func_tab *efp;
306	char	 buf[BUFSZ];
307	winid datawin;
308
309	datawin = create_nhwindow(NHW_TEXT);
310	putstr(datawin, 0, "");
311	putstr(datawin, 0, "            Extended Commands List");
312	putstr(datawin, 0, "");
313	putstr(datawin, 0, "    Press '#', then type:");
314	putstr(datawin, 0, "");
315
316	for(efp = extcmdlist; efp->ef_txt; efp++) {
317		Sprintf(buf, "    %-15s - %s.", efp->ef_txt, efp->ef_desc);
318		putstr(datawin, 0, buf);
319	}
320	display_nhwindow(datawin, FALSE);
321	destroy_nhwindow(datawin);
322	return 0;
323}
324
325#ifdef TTY_GRAPHICS
326#define MAX_EXT_CMD 40		/* Change if we ever have > 40 ext cmds */
327/*
328 * This is currently used only by the tty port and is
329 * controlled via runtime option 'extmenu'
330 */
331int
332extcmd_via_menu()	/* here after # - now show pick-list of possible commands */
333{
334    const struct ext_func_tab *efp;
335    menu_item *pick_list = (menu_item *)0;
336    winid win;
337    anything any;
338    const struct ext_func_tab *choices[MAX_EXT_CMD];
339    char buf[BUFSZ];
340    char cbuf[QBUFSZ], prompt[QBUFSZ], fmtstr[20];
341    int i, n, nchoices, acount;
342    int ret,  biggest;
343    int accelerator, prevaccelerator;
344    int  matchlevel = 0;
345
346    ret = 0;
347    cbuf[0] = '\0';
348    biggest = 0;
349    while (!ret) {
350	    i = n = 0;
351	    accelerator = 0;
352	    any.a_void = 0;
353	    /* populate choices */
354	    for(efp = extcmdlist; efp->ef_txt; efp++) {
355		if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) {
356			choices[i++] = efp;
357			if ((int)strlen(efp->ef_desc) > biggest) {
358				biggest = strlen(efp->ef_desc);
359				Sprintf(fmtstr,"%%-%ds", biggest + 15);
360			}
361#ifdef NETHACK_DEBUG
362			if (i >= MAX_EXT_CMD - 2) {
363			    impossible("Exceeded %d extended commands in doextcmd() menu",
364					MAX_EXT_CMD - 2);
365			    return 0;
366			}
367#endif
368		}
369	    }
370	    choices[i] = (struct ext_func_tab *)0;
371	    nchoices = i;
372	    /* if we're down to one, we have our selection so get out of here */
373	    if (nchoices == 1) {
374		for (i = 0; extcmdlist[i].ef_txt != (char *)0; i++)
375			if (!strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) {
376				ret = i;
377				break;
378			}
379		break;
380	    }
381
382	    /* otherwise... */
383	    win = create_nhwindow(NHW_MENU);
384	    start_menu(win);
385	    prevaccelerator = 0;
386	    acount = 0;
387	    for(i = 0; choices[i]; ++i) {
388		accelerator = choices[i]->ef_txt[matchlevel];
389		if (accelerator != prevaccelerator || nchoices < (ROWNO - 3)) {
390		    if (acount) {
391 			/* flush the extended commands for that letter already in buf */
392			Sprintf(buf, fmtstr, prompt);
393			any.a_char = prevaccelerator;
394			add_menu(win, NO_GLYPH, &any, any.a_char, 0,
395					ATR_NONE, buf, FALSE);
396			acount = 0;
397		    }
398		}
399		prevaccelerator = accelerator;
400		if (!acount || nchoices < (ROWNO - 3)) {
401		    Sprintf(prompt, "%s [%s]", choices[i]->ef_txt,
402				choices[i]->ef_desc);
403		} else if (acount == 1) {
404		    Sprintf(prompt, "%s or %s", choices[i-1]->ef_txt,
405				choices[i]->ef_txt);
406		} else {
407		    Strcat(prompt," or ");
408		    Strcat(prompt, choices[i]->ef_txt);
409		}
410		++acount;
411	    }
412	    if (acount) {
413		/* flush buf */
414		Sprintf(buf, fmtstr, prompt);
415		any.a_char = prevaccelerator;
416		add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE, buf, FALSE);
417	    }
418	    Sprintf(prompt, "Extended Command: %s", cbuf);
419	    end_menu(win, prompt);
420	    n = select_menu(win, PICK_ONE, &pick_list);
421	    destroy_nhwindow(win);
422	    if (n==1) {
423		if (matchlevel > (QBUFSZ - 2)) {
424			free((genericptr_t)pick_list);
425#ifdef NETHACK_DEBUG
426			impossible("Too many characters (%d) entered in extcmd_via_menu()",
427				matchlevel);
428#endif
429			ret = -1;
430		} else {
431			cbuf[matchlevel++] = pick_list[0].item.a_char;
432			cbuf[matchlevel] = '\0';
433			free((genericptr_t)pick_list);
434		}
435	    } else {
436		if (matchlevel) {
437			ret = 0;
438			matchlevel = 0;
439		} else
440			ret = -1;
441	    }
442    }
443    return ret;
444}
445#endif
446
447/* #monster command - use special monster ability while polymorphed */
448STATIC_PTR int
449domonability()
450{
451	if (can_breathe(youmonst.data)) return dobreathe();
452	else if (attacktype(youmonst.data, AT_SPIT)) return dospit();
453	else if (youmonst.data->mlet == S_NYMPH) return doremove();
454	else if (attacktype(youmonst.data, AT_GAZE)) return dogaze();
455	else if (is_were(youmonst.data)) return dosummon();
456	else if (webmaker(youmonst.data)) return dospinweb();
457	else if (is_hider(youmonst.data)) return dohide();
458	else if (is_mind_flayer(youmonst.data)) return domindblast();
459	else if (u.umonnum == PM_GREMLIN) {
460	    if(IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
461		if (split_mon(&youmonst, (struct monst *)0))
462		    dryup(u.ux, u.uy, TRUE);
463	    } else There("is no fountain here.");
464	} else if (is_unicorn(youmonst.data)) {
465	    use_unicorn_horn((struct obj *)0);
466	    return 1;
467	} else if (youmonst.data->msound == MS_SHRIEK) {
468	    You("shriek.");
469	    if(u.uburied)
470		pline("Unfortunately sound does not carry well through rock.");
471	    else aggravate();
472	} else if (Upolyd)
473		pline("Any special ability you may have is purely reflexive.");
474	else You("don't have a special ability in your normal form!");
475	return 0;
476}
477
478STATIC_PTR int
479enter_explore_mode()
480{
481	if(!discover && !wizard) {
482		pline("Beware!  From explore mode there will be no return to normal game.");
483		if (yn("Do you want to enter explore mode?") == 'y') {
484			clear_nhwindow(WIN_MESSAGE);
485			You("are now in non-scoring explore mode.");
486			discover = TRUE;
487		}
488		else {
489			clear_nhwindow(WIN_MESSAGE);
490			pline("Resuming normal game.");
491		}
492	}
493	return 0;
494}
495
496#ifdef WIZARD
497
498/* ^W command - wish for something */
499STATIC_PTR int
500wiz_wish()	/* Unlimited wishes for debug mode by Paul Polderman */
501{
502	if (wizard) {
503	    boolean save_verbose = flags.verbose;
504
505	    flags.verbose = FALSE;
506	    makewish();
507	    flags.verbose = save_verbose;
508	    (void) encumber_msg();
509	} else
510	    pline("Unavailable command '^W'.");
511	return 0;
512}
513
514/* ^I command - identify hero's inventory */
515STATIC_PTR int
516wiz_identify()
517{
518	if (wizard)	identify_pack(0);
519	else		pline("Unavailable command '^I'.");
520	return 0;
521}
522
523/* ^F command - reveal the level map and any traps on it */
524STATIC_PTR int
525wiz_map()
526{
527	if (wizard) {
528	    struct trap *t;
529	    long save_Hconf = HConfusion,
530		 save_Hhallu = HHallucination;
531
532	    HConfusion = HHallucination = 0L;
533	    for (t = ftrap; t != 0; t = t->ntrap) {
534		t->tseen = 1;
535		map_trap(t, TRUE);
536	    }
537	    do_mapping();
538	    HConfusion = save_Hconf;
539	    HHallucination = save_Hhallu;
540	} else
541	    pline("Unavailable command '^F'.");
542	return 0;
543}
544
545/* ^G command - generate monster(s); a count prefix will be honored */
546STATIC_PTR int
547wiz_genesis()
548{
549	if (wizard)	(void) create_particular();
550	else		pline("Unavailable command '^G'.");
551	return 0;
552}
553
554/* ^O command - display dungeon layout */
555STATIC_PTR int
556wiz_where()
557{
558	if (wizard) (void) print_dungeon(FALSE, (schar *)0, (xchar *)0);
559	else	    pline("Unavailable command '^O'.");
560	return 0;
561}
562
563/* ^E command - detect unseen (secret doors, traps, hidden monsters) */
564STATIC_PTR int
565wiz_detect()
566{
567	if(wizard)  (void) findit();
568	else	    pline("Unavailable command '^E'.");
569	return 0;
570}
571
572/* ^V command - level teleport */
573STATIC_PTR int
574wiz_level_tele()
575{
576	if (wizard)	level_tele();
577	else		pline("Unavailable command '^V'.");
578	return 0;
579}
580
581/* #monpolycontrol command - choose new form for shapechangers, polymorphees */
582STATIC_PTR int
583wiz_mon_polycontrol()
584{
585    iflags.mon_polycontrol = !iflags.mon_polycontrol;
586    pline("Monster polymorph control is %s.",
587	  iflags.mon_polycontrol ? "on" : "off");
588    return 0;
589}
590
591/* #levelchange command - adjust hero's experience level */
592STATIC_PTR int
593wiz_level_change()
594{
595    char buf[BUFSZ];
596    int newlevel;
597    int ret;
598
599    getlin("To what experience level do you want to be set?", buf);
600    (void)mungspaces(buf);
601    if (buf[0] == '\033' || buf[0] == '\0') ret = 0;
602    else ret = sscanf(buf, "%d", &newlevel);
603
604    if (ret != 1) {
605	pline(Never_mind);
606	return 0;
607    }
608    if (newlevel == u.ulevel) {
609	You("are already that experienced.");
610    } else if (newlevel < u.ulevel) {
611	if (u.ulevel == 1) {
612	    You("are already as inexperienced as you can get.");
613	    return 0;
614	}
615	if (newlevel < 1) newlevel = 1;
616	while (u.ulevel > newlevel)
617	    losexp("#levelchange");
618    } else {
619	if (u.ulevel >= MAXULEV) {
620	    You("are already as experienced as you can get.");
621	    return 0;
622	}
623	if (newlevel > MAXULEV) newlevel = MAXULEV;
624	while (u.ulevel < newlevel)
625	    pluslvl(FALSE);
626    }
627    u.ulevelmax = u.ulevel;
628    return 0;
629}
630
631/* #panic command - test program's panic handling */
632STATIC_PTR int
633wiz_panic()
634{
635	if (yn("Do you want to call panic() and end your game?") == 'y')
636		panic("crash test.");
637        return 0;
638}
639
640/* #polyself command - change hero's form */
641STATIC_PTR int
642wiz_polyself()
643{
644        polyself(TRUE);
645        return 0;
646}
647
648/* #seenv command */
649STATIC_PTR int
650wiz_show_seenv()
651{
652	winid win;
653	int x, y, v, startx, stopx, curx;
654	char row[COLNO+1];
655
656	win = create_nhwindow(NHW_TEXT);
657	/*
658	 * Each seenv description takes up 2 characters, so center
659	 * the seenv display around the hero.
660	 */
661	startx = max(1, u.ux-(COLNO/4));
662	stopx = min(startx+(COLNO/2), COLNO);
663	/* can't have a line exactly 80 chars long */
664	if (stopx - startx == COLNO/2) startx++;
665
666	for (y = 0; y < ROWNO; y++) {
667	    for (x = startx, curx = 0; x < stopx; x++, curx += 2) {
668		if (x == u.ux && y == u.uy) {
669		    row[curx] = row[curx+1] = '@';
670		} else {
671		    v = levl[x][y].seenv & 0xff;
672		    if (v == 0)
673			row[curx] = row[curx+1] = ' ';
674		    else
675			Sprintf(&row[curx], "%02x", v);
676		}
677	    }
678	    /* remove trailing spaces */
679	    for (x = curx-1; x >= 0; x--)
680		if (row[x] != ' ') break;
681	    row[x+1] = '\0';
682
683	    putstr(win, 0, row);
684	}
685	display_nhwindow(win, TRUE);
686	destroy_nhwindow(win);
687	return 0;
688}
689
690/* #vision command */
691STATIC_PTR int
692wiz_show_vision()
693{
694	winid win;
695	int x, y, v;
696	char row[COLNO+1];
697
698	win = create_nhwindow(NHW_TEXT);
699	Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
700		COULD_SEE, IN_SIGHT, TEMP_LIT);
701	putstr(win, 0, row);
702	putstr(win, 0, "");
703	for (y = 0; y < ROWNO; y++) {
704	    for (x = 1; x < COLNO; x++) {
705		if (x == u.ux && y == u.uy)
706		    row[x] = '@';
707		else {
708		    v = viz_array[y][x]; /* data access should be hidden */
709		    if (v == 0)
710			row[x] = ' ';
711		    else
712			row[x] = '0' + viz_array[y][x];
713		}
714	    }
715	    /* remove trailing spaces */
716	    for (x = COLNO-1; x >= 1; x--)
717		if (row[x] != ' ') break;
718	    row[x+1] = '\0';
719
720	    putstr(win, 0, &row[1]);
721	}
722	display_nhwindow(win, TRUE);
723	destroy_nhwindow(win);
724	return 0;
725}
726
727/* #wmode command */
728STATIC_PTR int
729wiz_show_wmodes()
730{
731	winid win;
732	int x,y;
733	char row[COLNO+1];
734	struct rm *lev;
735
736	win = create_nhwindow(NHW_TEXT);
737	for (y = 0; y < ROWNO; y++) {
738	    for (x = 0; x < COLNO; x++) {
739		lev = &levl[x][y];
740		if (x == u.ux && y == u.uy)
741		    row[x] = '@';
742		else if (IS_WALL(lev->typ) || lev->typ == SDOOR)
743		    row[x] = '0' + (lev->wall_info & WM_MASK);
744		else if (lev->typ == CORR)
745		    row[x] = '#';
746		else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ))
747		    row[x] = '.';
748		else
749		    row[x] = 'x';
750	    }
751	    row[COLNO] = '\0';
752	    putstr(win, 0, row);
753	}
754	display_nhwindow(win, TRUE);
755	destroy_nhwindow(win);
756	return 0;
757}
758
759#endif /* WIZARD */
760
761
762/* -enlightenment and conduct- */
763static winid en_win;
764static const char
765	You_[] = "You ",
766	are[]  = "are ",  were[]  = "were ",
767	have[] = "have ", had[]   = "had ",
768	can[]  = "can ",  could[] = "could ";
769static const char
770	have_been[]  = "have been ",
771	have_never[] = "have never ", never[] = "never ";
772
773#define enl_msg(prefix,present,past,suffix) \
774			enlght_line(prefix, final ? past : present, suffix)
775#define you_are(attr)	enl_msg(You_,are,were,attr)
776#define you_have(attr)	enl_msg(You_,have,had,attr)
777#define you_can(attr)	enl_msg(You_,can,could,attr)
778#define you_have_been(goodthing) enl_msg(You_,have_been,were,goodthing)
779#define you_have_never(badthing) enl_msg(You_,have_never,never,badthing)
780#define you_have_X(something)	enl_msg(You_,have,(const char *)"",something)
781
782static void
783enlght_line(start, middle, end)
784const char *start, *middle, *end;
785{
786	char buf[BUFSZ];
787
788	Sprintf(buf, "%s%s%s.", start, middle, end);
789	putstr(en_win, 0, buf);
790}
791
792/* format increased damage or chance to hit */
793static char *
794enlght_combatinc(inctyp, incamt, final, outbuf)
795const char *inctyp;
796int incamt, final;
797char *outbuf;
798{
799	char numbuf[24];
800	const char *modif, *bonus;
801
802	if (final
803#ifdef WIZARD
804		|| wizard
805#endif
806	  ) {
807	    Sprintf(numbuf, "%s%d",
808		    (incamt > 0) ? "+" : "", incamt);
809	    modif = (const char *) numbuf;
810	} else {
811	    int absamt = abs(incamt);
812
813	    if (absamt <= 3) modif = "small";
814	    else if (absamt <= 6) modif = "moderate";
815	    else if (absamt <= 12) modif = "large";
816	    else modif = "huge";
817	}
818	bonus = (incamt > 0) ? "bonus" : "penalty";
819	/* "bonus to hit" vs "damage bonus" */
820	if (!strcmp(inctyp, "damage")) {
821	    const char *ctmp = inctyp;
822	    inctyp = bonus;
823	    bonus = ctmp;
824	}
825	Sprintf(outbuf, "%s %s %s", an(modif), bonus, inctyp);
826	return outbuf;
827}
828
829void
830enlightenment(final)
831int final;	/* 0 => still in progress; 1 => over, survived; 2 => dead */
832{
833	int ltmp;
834	char buf[BUFSZ];
835
836	en_win = create_nhwindow(NHW_MENU);
837	putstr(en_win, 0, final ? "Final Attributes:" : "Current Attributes:");
838	putstr(en_win, 0, "");
839
840#ifdef ELBERETH
841	if (u.uevent.uhand_of_elbereth) {
842	    static const char * const hofe_titles[3] = {
843				"the Hand of Elbereth",
844				"the Envoy of Balance",
845				"the Glory of Arioch"
846	    };
847	    you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1]);
848	}
849#endif
850
851	/* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
852	if (u.ualign.record >= 20)	you_are("piously aligned");
853	else if (u.ualign.record > 13)	you_are("devoutly aligned");
854	else if (u.ualign.record > 8)	you_are("fervently aligned");
855	else if (u.ualign.record > 3)	you_are("stridently aligned");
856	else if (u.ualign.record == 3)	you_are("aligned");
857	else if (u.ualign.record > 0)	you_are("haltingly aligned");
858	else if (u.ualign.record == 0)	you_are("nominally aligned");
859	else if (u.ualign.record >= -3)	you_have("strayed");
860	else if (u.ualign.record >= -8)	you_have("sinned");
861	else you_have("transgressed");
862#ifdef WIZARD
863	if (wizard) {
864		Sprintf(buf, " %d", u.ualign.record);
865		enl_msg("Your alignment ", "is", "was", buf);
866	}
867#endif
868
869	/*** Resistances to troubles ***/
870	if (Fire_resistance) you_are("fire resistant");
871	if (Cold_resistance) you_are("cold resistant");
872	if (Sleep_resistance) you_are("sleep resistant");
873	if (Disint_resistance) you_are("disintegration-resistant");
874	if (Shock_resistance) you_are("shock resistant");
875	if (Poison_resistance) you_are("poison resistant");
876	if (Drain_resistance) you_are("level-drain resistant");
877	if (Sick_resistance) you_are("immune to sickness");
878	if (Antimagic) you_are("magic-protected");
879	if (Acid_resistance) you_are("acid resistant");
880	if (Stone_resistance)
881		you_are("petrification resistant");
882	if (Invulnerable) you_are("invulnerable");
883	if (u.uedibility) you_can("recognize detrimental food");
884
885	/*** Troubles ***/
886	if (Halluc_resistance)
887		enl_msg("You resist", "", "ed", " hallucinations");
888	if (final) {
889		if (Hallucination) you_are("hallucinating");
890		if (Stunned) you_are("stunned");
891		if (Confusion) you_are("confused");
892		if (Blinded) you_are("blinded");
893		if (Sick) {
894			if (u.usick_type & SICK_VOMITABLE)
895				you_are("sick from food poisoning");
896			if (u.usick_type & SICK_NONVOMITABLE)
897				you_are("sick from illness");
898		}
899	}
900	if (Stoned) you_are("turning to stone");
901	if (Slimed) you_are("turning into slime");
902	if (Strangled) you_are((u.uburied) ? "buried" : "being strangled");
903	if (Glib) {
904		Sprintf(buf, "slippery %s", makeplural(body_part(FINGER)));
905		you_have(buf);
906	}
907	if (Fumbling) enl_msg("You fumble", "", "d", "");
908	if (Wounded_legs
909#ifdef STEED
910	    && !u.usteed
911#endif
912			  ) {
913		Sprintf(buf, "wounded %s", makeplural(body_part(LEG)));
914		you_have(buf);
915	}
916#if defined(WIZARD) && defined(STEED)
917	if (Wounded_legs && u.usteed && wizard) {
918	    Strcpy(buf, x_monnam(u.usteed, ARTICLE_YOUR, (char *)0,
919		    SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION, FALSE));
920	    *buf = highc(*buf);
921	    enl_msg(buf, " has", " had", " wounded legs");
922	}
923#endif
924	if (Sleeping) enl_msg("You ", "fall", "fell", " asleep");
925	if (Hunger) enl_msg("You hunger", "", "ed", " rapidly");
926
927	/*** Vision and senses ***/
928	if (See_invisible) enl_msg(You_, "see", "saw", " invisible");
929	if (Blind_telepat) you_are("telepathic");
930	if (Warning) you_are("warned");
931	if (Warn_of_mon && flags.warntype) {
932		Sprintf(buf, "aware of the presence of %s",
933			(flags.warntype & M2_ORC) ? "orcs" :
934			(flags.warntype & M2_DEMON) ? "demons" :
935			something);
936		you_are(buf);
937	}
938	if (Undead_warning) you_are("warned of undead");
939	if (Searching) you_have("automatic searching");
940	if (Clairvoyant) you_are("clairvoyant");
941	if (Infravision) you_have("infravision");
942	if (Detect_monsters) you_are("sensing the presence of monsters");
943	if (u.umconf) you_are("going to confuse monsters");
944
945	/*** Appearance and behavior ***/
946	if (Adornment) {
947	    int adorn = 0;
948
949	    if(uleft && uleft->otyp == RIN_ADORNMENT) adorn += uleft->spe;
950	    if(uright && uright->otyp == RIN_ADORNMENT) adorn += uright->spe;
951	    if (adorn < 0)
952		you_are("poorly adorned");
953	    else
954		you_are("adorned");
955	}
956	if (Invisible) you_are("invisible");
957	else if (Invis) you_are("invisible to others");
958	/* ordinarily "visible" is redundant; this is a special case for
959	   the situation when invisibility would be an expected attribute */
960	else if ((HInvis || EInvis || pm_invisible(youmonst.data)) && BInvis)
961	    you_are("visible");
962	if (Displaced) you_are("displaced");
963	if (Stealth) you_are("stealthy");
964	if (Aggravate_monster) enl_msg("You aggravate", "", "d", " monsters");
965	if (Conflict) enl_msg("You cause", "", "d", " conflict");
966
967	/*** Transportation ***/
968	if (Jumping) you_can("jump");
969	if (Teleportation) you_can("teleport");
970	if (Teleport_control) you_have("teleport control");
971	if (Lev_at_will) you_are("levitating, at will");
972	else if (Levitation) you_are("levitating");	/* without control */
973	else if (Flying) you_can("fly");
974	if (Wwalking) you_can("walk on water");
975	if (Swimming) you_can("swim");
976	if (Breathless) you_can("survive without air");
977	else if (Amphibious) you_can("breathe water");
978	if (Passes_walls) you_can("walk through walls");
979#ifdef STEED
980	/* If you die while dismounting, u.usteed is still set.  Since several
981	 * places in the done() sequence depend on u.usteed, just detect this
982	 * special case. */
983	if (u.usteed && (final < 2 || strcmp(killer, "riding accident"))) {
984	    Sprintf(buf, "riding %s", y_monnam(u.usteed));
985	    you_are(buf);
986	}
987#endif
988	if (u.uswallow) {
989	    Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck));
990#ifdef WIZARD
991	    if (wizard) Sprintf(eos(buf), " (%u)", u.uswldtim);
992#endif
993	    you_are(buf);
994	} else if (u.ustuck) {
995	    Sprintf(buf, "%s %s",
996		    (Upolyd && sticks(youmonst.data)) ? "holding" : "held by",
997		    a_monnam(u.ustuck));
998	    you_are(buf);
999	}
1000
1001	/*** Physical attributes ***/
1002	if (u.uhitinc)
1003	    you_have(enlght_combatinc("to hit", u.uhitinc, final, buf));
1004	if (u.udaminc)
1005	    you_have(enlght_combatinc("damage", u.udaminc, final, buf));
1006	if (Slow_digestion) you_have("slower digestion");
1007	if (Regeneration) enl_msg("You regenerate", "", "d", "");
1008	if (u.uspellprot || Protection) {
1009	    int prot = 0;
1010
1011	    if(uleft && uleft->otyp == RIN_PROTECTION) prot += uleft->spe;
1012	    if(uright && uright->otyp == RIN_PROTECTION) prot += uright->spe;
1013	    if (HProtection & INTRINSIC) prot += u.ublessed;
1014	    prot += u.uspellprot;
1015
1016	    if (prot < 0)
1017		you_are("ineffectively protected");
1018	    else
1019		you_are("protected");
1020	}
1021	if (Protection_from_shape_changers)
1022		you_are("protected from shape changers");
1023	if (Polymorph) you_are("polymorphing");
1024	if (Polymorph_control) you_have("polymorph control");
1025	if (u.ulycn >= LOW_PM) {
1026		Strcpy(buf, an(mons[u.ulycn].mname));
1027		you_are(buf);
1028	}
1029	if (Upolyd) {
1030	    if (u.umonnum == u.ulycn) Strcpy(buf, "in beast form");
1031	    else Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname));
1032#ifdef WIZARD
1033	    if (wizard) Sprintf(eos(buf), " (%d)", u.mtimedone);
1034#endif
1035	    you_are(buf);
1036	}
1037	if (Unchanging) you_can("not change from your current form");
1038	if (Fast) you_are(Very_fast ? "very fast" : "fast");
1039	if (Reflecting) you_have("reflection");
1040	if (Free_action) you_have("free action");
1041	if (Fixed_abil) you_have("fixed abilities");
1042	if (Lifesaved)
1043		enl_msg("Your life ", "will be", "would have been", " saved");
1044	if (u.twoweap) you_are("wielding two weapons at once");
1045
1046	/*** Miscellany ***/
1047	if (Luck) {
1048	    ltmp = abs((int)Luck);
1049	    Sprintf(buf, "%s%slucky",
1050		    ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "",
1051		    Luck < 0 ? "un" : "");
1052#ifdef WIZARD
1053	    if (wizard) Sprintf(eos(buf), " (%d)", Luck);
1054#endif
1055	    you_are(buf);
1056	}
1057#ifdef WIZARD
1058	 else if (wizard) enl_msg("Your luck ", "is", "was", " zero");
1059#endif
1060	if (u.moreluck > 0) you_have("extra luck");
1061	else if (u.moreluck < 0) you_have("reduced luck");
1062	if (carrying(LUCKSTONE) || stone_luck(TRUE)) {
1063	    ltmp = stone_luck(FALSE);
1064	    if (ltmp <= 0)
1065		enl_msg("Bad luck ", "does", "did", " not time out for you");
1066	    if (ltmp >= 0)
1067		enl_msg("Good luck ", "does", "did", " not time out for you");
1068	}
1069
1070	if (u.ugangr) {
1071	    Sprintf(buf, " %sangry with you",
1072		    u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : "");
1073#ifdef WIZARD
1074	    if (wizard) Sprintf(eos(buf), " (%d)", u.ugangr);
1075#endif
1076	    enl_msg(u_gname(), " is", " was", buf);
1077	} else
1078	    /*
1079	     * We need to suppress this when the game is over, because death
1080	     * can change the value calculated by can_pray(), potentially
1081	     * resulting in a false claim that you could have prayed safely.
1082	     */
1083	  if (!final) {
1084#if 0
1085	    /* "can [not] safely pray" vs "could [not] have safely prayed" */
1086	    Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ",
1087		    final ? "have " : "", final ? "ed" : "");
1088#else
1089	    Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not ");
1090#endif
1091#ifdef WIZARD
1092	    if (wizard) Sprintf(eos(buf), " (%d)", u.ublesscnt);
1093#endif
1094	    you_can(buf);
1095	}
1096
1097    {
1098	const char *p;
1099
1100	buf[0] = '\0';
1101	if (final < 2) {    /* still in progress, or quit/escaped/ascended */
1102	    p = "survived after being killed ";
1103	    switch (u.umortality) {
1104	    case 0:  p = !final ? (char *)0 : "survived";  break;
1105	    case 1:  Strcpy(buf, "once");  break;
1106	    case 2:  Strcpy(buf, "twice");  break;
1107	    case 3:  Strcpy(buf, "thrice");  break;
1108	    default: Sprintf(buf, "%d times", u.umortality);
1109		     break;
1110	    }
1111	} else {		/* game ended in character's death */
1112	    p = "are dead";
1113	    switch (u.umortality) {
1114	    case 0:  impossible("dead without dying?");
1115	    case 1:  break;			/* just "are dead" */
1116	    default: Sprintf(buf, " (%d%s time!)", u.umortality,
1117			     ordin(u.umortality));
1118		     break;
1119	    }
1120	}
1121	if (p) enl_msg(You_, "have been killed ", p, buf);
1122    }
1123
1124	display_nhwindow(en_win, TRUE);
1125	destroy_nhwindow(en_win);
1126	return;
1127}
1128
1129/*
1130 * Courtesy function for non-debug, non-explorer mode players
1131 * to help refresh them about who/what they are.
1132 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
1133 */
1134STATIC_OVL boolean
1135minimal_enlightenment()
1136{
1137	winid tmpwin;
1138	menu_item *selected;
1139	anything any;
1140	int genidx, n;
1141	char buf[BUFSZ], buf2[BUFSZ];
1142	static const char untabbed_fmtstr[] = "%-15s: %-12s";
1143	static const char untabbed_deity_fmtstr[] = "%-17s%s";
1144	static const char tabbed_fmtstr[] = "%s:\t%-12s";
1145	static const char tabbed_deity_fmtstr[] = "%s\t%s";
1146	static const char *fmtstr;
1147	static const char *deity_fmtstr;
1148
1149	fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr;
1150	deity_fmtstr = iflags.menu_tab_sep ?
1151			tabbed_deity_fmtstr : untabbed_deity_fmtstr;
1152	any.a_void = 0;
1153	buf[0] = buf2[0] = '\0';
1154	tmpwin = create_nhwindow(NHW_MENU);
1155	start_menu(tmpwin);
1156	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, "Starting", FALSE);
1157
1158	/* Starting name, race, role, gender */
1159	Sprintf(buf, fmtstr, "name", plname);
1160	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1161	Sprintf(buf, fmtstr, "race", urace.noun);
1162	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1163	Sprintf(buf, fmtstr, "role",
1164		(flags.initgend && urole.name.f) ? urole.name.f : urole.name.m);
1165	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1166	Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj);
1167	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1168
1169	/* Starting alignment */
1170	Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL]));
1171	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1172
1173	/* Current name, race, role, gender */
1174	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
1175	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, "Current", FALSE);
1176	Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun);
1177	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1178	if (Upolyd) {
1179	    Sprintf(buf, fmtstr, "role (base)",
1180		(u.mfemale && urole.name.f) ? urole.name.f : urole.name.m);
1181	    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1182	} else {
1183	    Sprintf(buf, fmtstr, "role",
1184		(flags.female && urole.name.f) ? urole.name.f : urole.name.m);
1185	    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1186	}
1187	/* don't want poly_gender() here; it forces `2' for non-humanoids */
1188	genidx = is_neuter(youmonst.data) ? 2 : flags.female;
1189	Sprintf(buf, fmtstr, "gender", genders[genidx].adj);
1190	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1191	if (Upolyd && (int)u.mfemale != genidx) {
1192	    Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj);
1193	    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1194	}
1195
1196	/* Current alignment */
1197	Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type));
1198	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1199
1200	/* Deity list */
1201	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
1202	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, "Deities", FALSE);
1203	Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC),
1204	    (u.ualignbase[A_ORIGINAL] == u.ualign.type
1205		&& u.ualign.type == A_CHAOTIC) ? " (s,c)" :
1206	    (u.ualignbase[A_ORIGINAL] == A_CHAOTIC)       ? " (s)" :
1207	    (u.ualign.type   == A_CHAOTIC)       ? " (c)" : "");
1208	Sprintf(buf, fmtstr, "Chaotic", buf2);
1209	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1210
1211	Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL),
1212	    (u.ualignbase[A_ORIGINAL] == u.ualign.type
1213		&& u.ualign.type == A_NEUTRAL) ? " (s,c)" :
1214	    (u.ualignbase[A_ORIGINAL] == A_NEUTRAL)       ? " (s)" :
1215	    (u.ualign.type   == A_NEUTRAL)       ? " (c)" : "");
1216	Sprintf(buf, fmtstr, "Neutral", buf2);
1217	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1218
1219	Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL),
1220	    (u.ualignbase[A_ORIGINAL] == u.ualign.type &&
1221		u.ualign.type == A_LAWFUL)  ? " (s,c)" :
1222	    (u.ualignbase[A_ORIGINAL] == A_LAWFUL)        ? " (s)" :
1223	    (u.ualign.type   == A_LAWFUL)        ? " (c)" : "");
1224	Sprintf(buf, fmtstr, "Lawful", buf2);
1225	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
1226
1227	end_menu(tmpwin, "Base Attributes");
1228	n = select_menu(tmpwin, PICK_NONE, &selected);
1229	destroy_nhwindow(tmpwin);
1230	return (n != -1);
1231}
1232
1233STATIC_PTR int
1234doattributes()
1235{
1236	if (!minimal_enlightenment())
1237		return 0;
1238	if (wizard || discover)
1239		enlightenment(0);
1240	return 0;
1241}
1242
1243/* KMH, #conduct
1244 * (shares enlightenment's tense handling)
1245 */
1246STATIC_PTR int
1247doconduct()
1248{
1249	show_conduct(0);
1250	return 0;
1251}
1252
1253void
1254show_conduct(final)
1255int final;
1256{
1257	char buf[BUFSZ];
1258	int ngenocided;
1259
1260	/* Create the conduct window */
1261	en_win = create_nhwindow(NHW_MENU);
1262	putstr(en_win, 0, "Voluntary challenges:");
1263	putstr(en_win, 0, "");
1264
1265	if (!u.uconduct.food)
1266	    enl_msg(You_, "have gone", "went", " without food");
1267	    /* But beverages are okay */
1268	else if (!u.uconduct.unvegan)
1269	    you_have_X("followed a strict vegan diet");
1270	else if (!u.uconduct.unvegetarian)
1271	    you_have_been("vegetarian");
1272
1273	if (!u.uconduct.gnostic)
1274	    you_have_been("an atheist");
1275
1276	if (!u.uconduct.weaphit)
1277	    you_have_never("hit with a wielded weapon");
1278#ifdef WIZARD
1279	else if (wizard) {
1280	    Sprintf(buf, "used a wielded weapon %ld time%s",
1281		    u.uconduct.weaphit, plur(u.uconduct.weaphit));
1282	    you_have_X(buf);
1283	}
1284#endif
1285	if (!u.uconduct.killer)
1286	    you_have_been("a pacifist");
1287
1288	if (!u.uconduct.literate)
1289	    you_have_been("illiterate");
1290#ifdef WIZARD
1291	else if (wizard) {
1292	    Sprintf(buf, "read items or engraved %ld time%s",
1293		    u.uconduct.literate, plur(u.uconduct.literate));
1294	    you_have_X(buf);
1295	}
1296#endif
1297
1298	ngenocided = num_genocides();
1299	if (ngenocided == 0) {
1300	    you_have_never("genocided any monsters");
1301	} else {
1302	    Sprintf(buf, "genocided %d type%s of monster%s",
1303		    ngenocided, plur(ngenocided), plur(ngenocided));
1304	    you_have_X(buf);
1305	}
1306
1307	if (!u.uconduct.polypiles)
1308	    you_have_never("polymorphed an object");
1309#ifdef WIZARD
1310	else if (wizard) {
1311	    Sprintf(buf, "polymorphed %ld item%s",
1312		    u.uconduct.polypiles, plur(u.uconduct.polypiles));
1313	    you_have_X(buf);
1314	}
1315#endif
1316
1317	if (!u.uconduct.polyselfs)
1318	    you_have_never("changed form");
1319#ifdef WIZARD
1320	else if (wizard) {
1321	    Sprintf(buf, "changed form %ld time%s",
1322		    u.uconduct.polyselfs, plur(u.uconduct.polyselfs));
1323	    you_have_X(buf);
1324	}
1325#endif
1326
1327	if (!u.uconduct.wishes)
1328	    you_have_X("used no wishes");
1329	else {
1330	    Sprintf(buf, "used %ld wish%s",
1331		    u.uconduct.wishes, (u.uconduct.wishes > 1L) ? "es" : "");
1332	    you_have_X(buf);
1333
1334	    if (!u.uconduct.wisharti)
1335		enl_msg(You_, "have not wished", "did not wish",
1336			" for any artifacts");
1337	}
1338
1339	/* Pop up the window and wait for a key */
1340	display_nhwindow(en_win, TRUE);
1341	destroy_nhwindow(en_win);
1342}
1343
1344#endif /* OVLB */
1345#ifdef OVL1
1346
1347#ifndef M
1348# ifndef NHSTDC
1349#  define M(c)		(0x80 | (c))
1350# else
1351#  define M(c)		((c) - 128)
1352# endif /* NHSTDC */
1353#endif
1354#ifndef C
1355#define C(c)		(0x1f & (c))
1356#endif
1357
1358static const struct func_tab cmdlist[] = {
1359	{C('d'), FALSE, dokick}, /* "D" is for door!...?  Msg is in dokick.c */
1360#ifdef WIZARD
1361	{C('e'), TRUE, wiz_detect},
1362	{C('f'), TRUE, wiz_map},
1363	{C('g'), TRUE, wiz_genesis},
1364	{C('i'), TRUE, wiz_identify},
1365#endif
1366	{C('l'), TRUE, doredraw}, /* if number_pad is set */
1367#ifdef WIZARD
1368	{C('o'), TRUE, wiz_where},
1369#endif
1370	{C('p'), TRUE, doprev_message},
1371	{C('r'), TRUE, doredraw},
1372	{C('t'), TRUE, dotele},
1373#ifdef WIZARD
1374	{C('v'), TRUE, wiz_level_tele},
1375	{C('w'), TRUE, wiz_wish},
1376#endif
1377	{C('x'), TRUE, doattributes},
1378#ifdef SUSPEND
1379	{C('z'), TRUE, dosuspend},
1380#endif
1381	{'a', FALSE, doapply},
1382	{'A', FALSE, doddoremarm},
1383	{M('a'), TRUE, doorganize},
1384/*	'b', 'B' : go sw */
1385	{'c', FALSE, doclose},
1386	{'C', TRUE, do_mname},
1387	{M('c'), TRUE, dotalk},
1388	{'d', FALSE, dodrop},
1389	{'D', FALSE, doddrop},
1390	{M('d'), FALSE, dodip},
1391	{'e', FALSE, doeat},
1392	{'E', FALSE, doengrave},
1393	{M('e'), TRUE, enhance_weapon_skill},
1394	{'f', FALSE, dofire},
1395/*	'F' : fight (one time) */
1396	{M('f'), FALSE, doforce},
1397/*	'g', 'G' : multiple go */
1398/*	'h', 'H' : go west */
1399	{'h', TRUE, dohelp}, /* if number_pad is set */
1400	{'i', TRUE, ddoinv},
1401	{'I', TRUE, dotypeinv},		/* Robert Viduya */
1402	{M('i'), TRUE, doinvoke},
1403/*	'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */
1404	{'j', FALSE, dojump}, /* if number_pad is on */
1405	{M('j'), FALSE, dojump},
1406	{'k', FALSE, dokick}, /* if number_pad is on */
1407	{'l', FALSE, doloot}, /* if number_pad is on */
1408	{M('l'), FALSE, doloot},
1409/*	'n' prefixes a count if number_pad is on */
1410	{M('m'), TRUE, domonability},
1411	{'N', TRUE, ddocall}, /* if number_pad is on */
1412	{M('n'), TRUE, ddocall},
1413	{M('N'), TRUE, ddocall},
1414	{'o', FALSE, doopen},
1415	{'O', TRUE, doset},
1416	{M('o'), FALSE, dosacrifice},
1417	{'p', FALSE, dopay},
1418	{'P', FALSE, doputon},
1419	{M('p'), TRUE, dopray},
1420	{'q', FALSE, dodrink},
1421	{'Q', FALSE, dowieldquiver},
1422	{M('q'), TRUE, done2},
1423	{'r', FALSE, doread},
1424	{'R', FALSE, doremring},
1425	{M('r'), FALSE, dorub},
1426	{'s', TRUE, dosearch, "searching"},
1427	{'S', TRUE, dosave},
1428	{M('s'), FALSE, dosit},
1429	{'t', FALSE, dothrow},
1430	{'T', FALSE, dotakeoff},
1431	{M('t'), TRUE, doturn},
1432/*	'u', 'U' : go ne */
1433	{'u', FALSE, dountrap}, /* if number_pad is on */
1434	{M('u'), FALSE, dountrap},
1435	{'v', TRUE, doversion},
1436	{'V', TRUE, dohistory},
1437	{M('v'), TRUE, doextversion},
1438	{'w', FALSE, dowield},
1439	{'W', FALSE, dowear},
1440	{M('w'), FALSE, dowipe},
1441	{'x', FALSE, doswapweapon},
1442	{'X', TRUE, enter_explore_mode},
1443/*	'y', 'Y' : go nw */
1444	{'z', FALSE, dozap},
1445	{'Z', TRUE, docast},
1446	{'<', FALSE, doup},
1447	{'>', FALSE, dodown},
1448	{'/', TRUE, dowhatis},
1449	{'&', TRUE, dowhatdoes},
1450	{'?', TRUE, dohelp},
1451	{M('?'), TRUE, doextlist},
1452#ifdef SHELL
1453	{'!', TRUE, dosh},
1454#endif
1455	{'.', TRUE, donull, "waiting"},
1456	{' ', TRUE, donull, "waiting"},
1457	{',', FALSE, dopickup},
1458	{':', TRUE, dolook},
1459	{';', TRUE, doquickwhatis},
1460	{'^', TRUE, doidtrap},
1461	{'\\', TRUE, dodiscovered},		/* Robert Viduya */
1462	{'@', TRUE, dotogglepickup},
1463	{M('2'), FALSE, dotwoweapon},
1464	{WEAPON_SYM,  TRUE, doprwep},
1465	{ARMOR_SYM,  TRUE, doprarm},
1466	{RING_SYM,  TRUE, doprring},
1467	{AMULET_SYM, TRUE, dopramulet},
1468	{TOOL_SYM, TRUE, doprtool},
1469	{'*', TRUE, doprinuse},	/* inventory of all equipment in use */
1470	{GOLD_SYM, TRUE, doprgold},
1471	{SPBOOK_SYM, TRUE, dovspell},			/* Mike Stephenson */
1472	{'#', TRUE, doextcmd},
1473	{'_', TRUE, dotravel},
1474	{0,0,0,0}
1475};
1476
1477struct ext_func_tab extcmdlist[] = {
1478	{"adjust", "adjust inventory letters", doorganize, TRUE},
1479	{"chat", "talk to someone", dotalk, TRUE},	/* converse? */
1480	{"conduct", "list which challenges you have adhered to", doconduct, TRUE},
1481	{"dip", "dip an object into something", dodip, FALSE},
1482	{"enhance", "advance or check weapons skills", enhance_weapon_skill,
1483							TRUE},
1484	{"force", "force a lock", doforce, FALSE},
1485	{"invoke", "invoke an object's powers", doinvoke, TRUE},
1486	{"jump", "jump to a location", dojump, FALSE},
1487	{"loot", "loot a box on the floor", doloot, FALSE},
1488	{"monster", "use a monster's special ability", domonability, TRUE},
1489	{"name", "name an item or type of object", ddocall, TRUE},
1490	{"offer", "offer a sacrifice to the gods", dosacrifice, FALSE},
1491	{"pray", "pray to the gods for help", dopray, TRUE},
1492	{"quit", "exit without saving current game", done2, TRUE},
1493#ifdef STEED
1494	{"ride", "ride (or stop riding) a monster", doride, FALSE},
1495#endif
1496	{"rub", "rub a lamp or a stone", dorub, FALSE},
1497	{"sit", "sit down", dosit, FALSE},
1498	{"turn", "turn undead", doturn, TRUE},
1499	{"twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE},
1500	{"untrap", "untrap something", dountrap, FALSE},
1501	{"version", "list compile time options for this version of NetHack",
1502		doextversion, TRUE},
1503	{"wipe", "wipe off your face", dowipe, FALSE},
1504	{"?", "get this list of extended commands", doextlist, TRUE},
1505#if defined(WIZARD)
1506	/*
1507	 * There must be a blank entry here for every entry in the table
1508	 * below.
1509	 */
1510	{(char *)0, (char *)0, donull, TRUE},
1511	{(char *)0, (char *)0, donull, TRUE},
1512#ifdef NETHACK_DEBUG_MIGRATING_MONS
1513	{(char *)0, (char *)0, donull, TRUE},
1514#endif
1515	{(char *)0, (char *)0, donull, TRUE},
1516	{(char *)0, (char *)0, donull, TRUE},
1517	{(char *)0, (char *)0, donull, TRUE},
1518#ifdef PORT_DEBUG
1519	{(char *)0, (char *)0, donull, TRUE},
1520#endif
1521	{(char *)0, (char *)0, donull, TRUE},
1522        {(char *)0, (char *)0, donull, TRUE},
1523	{(char *)0, (char *)0, donull, TRUE},
1524	{(char *)0, (char *)0, donull, TRUE},
1525#ifdef NETHACK_DEBUG
1526	{(char *)0, (char *)0, donull, TRUE},
1527#endif
1528	{(char *)0, (char *)0, donull, TRUE},
1529#endif
1530	{(char *)0, (char *)0, donull, TRUE}	/* sentinel */
1531};
1532
1533#if defined(WIZARD)
1534static const struct ext_func_tab debug_extcmdlist[] = {
1535	{"levelchange", "change experience level", wiz_level_change, TRUE},
1536	{"lightsources", "show mobile light sources", wiz_light_sources, TRUE},
1537#ifdef NETHACK_DEBUG_MIGRATING_MONS
1538	{"migratemons", "migrate n random monsters", wiz_migrate_mons, TRUE},
1539#endif
1540	{"monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol, TRUE},
1541	{"panic", "test panic routine (fatal to game)", wiz_panic, TRUE},
1542	{"polyself", "polymorph self", wiz_polyself, TRUE},
1543#ifdef PORT_DEBUG
1544	{"portdebug", "wizard port debug command", wiz_port_debug, TRUE},
1545#endif
1546	{"seenv", "show seen vectors", wiz_show_seenv, TRUE},
1547	{"stats", "show memory statistics", wiz_show_stats, TRUE},
1548	{"timeout", "look at timeout queue", wiz_timeout_queue, TRUE},
1549	{"vision", "show vision array", wiz_show_vision, TRUE},
1550#ifdef NETHACK_DEBUG
1551	{"wizdebug", "wizard debug command", wiz_debug_cmd, TRUE},
1552#endif
1553	{"wmode", "show wall modes", wiz_show_wmodes, TRUE},
1554	{(char *)0, (char *)0, donull, TRUE}
1555};
1556
1557/*
1558 * Insert debug commands into the extended command list.  This function
1559 * assumes that the last entry will be the help entry.
1560 *
1561 * You must add entries in ext_func_tab every time you add one to the
1562 * debug_extcmdlist().
1563 */
1564void
1565add_debug_extended_commands()
1566{
1567	int i, j, k, n;
1568
1569	/* count the # of help entries */
1570	for (n = 0; extcmdlist[n].ef_txt[0] != '?'; n++)
1571	    ;
1572
1573	for (i = 0; debug_extcmdlist[i].ef_txt; i++) {
1574	    for (j = 0; j < n; j++)
1575		if (strcmp(debug_extcmdlist[i].ef_txt, extcmdlist[j].ef_txt) < 0) break;
1576
1577	    /* insert i'th debug entry into extcmdlist[j], pushing down  */
1578	    for (k = n; k >= j; --k)
1579		extcmdlist[k+1] = extcmdlist[k];
1580	    extcmdlist[j] = debug_extcmdlist[i];
1581	    n++;	/* now an extra entry */
1582	}
1583}
1584
1585
1586static const char template[] = "%-18s %4ld  %6ld";
1587static const char count_str[] = "                   count  bytes";
1588static const char separator[] = "------------------ -----  ------";
1589
1590STATIC_OVL void
1591count_obj(chain, total_count, total_size, top, recurse)
1592	struct obj *chain;
1593	long *total_count;
1594	long *total_size;
1595	boolean top;
1596	boolean recurse;
1597{
1598	long count, size;
1599	struct obj *obj;
1600
1601	for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
1602	    if (top) {
1603		count++;
1604		size += sizeof(struct obj) + obj->oxlth + obj->onamelth;
1605	    }
1606	    if (recurse && obj->cobj)
1607		count_obj(obj->cobj, total_count, total_size, TRUE, TRUE);
1608	}
1609	*total_count += count;
1610	*total_size += size;
1611}
1612
1613STATIC_OVL void
1614obj_chain(win, src, chain, total_count, total_size)
1615	winid win;
1616	const char *src;
1617	struct obj *chain;
1618	long *total_count;
1619	long *total_size;
1620{
1621	char buf[BUFSZ];
1622	long count = 0, size = 0;
1623
1624	count_obj(chain, &count, &size, TRUE, FALSE);
1625	*total_count += count;
1626	*total_size += size;
1627	Sprintf(buf, template, src, count, size);
1628	putstr(win, 0, buf);
1629}
1630
1631STATIC_OVL void
1632mon_invent_chain(win, src, chain, total_count, total_size)
1633	winid win;
1634	const char *src;
1635	struct monst *chain;
1636	long *total_count;
1637	long *total_size;
1638{
1639	char buf[BUFSZ];
1640	long count = 0, size = 0;
1641	struct monst *mon;
1642
1643	for (mon = chain; mon; mon = mon->nmon)
1644	    count_obj(mon->minvent, &count, &size, TRUE, FALSE);
1645	*total_count += count;
1646	*total_size += size;
1647	Sprintf(buf, template, src, count, size);
1648	putstr(win, 0, buf);
1649}
1650
1651STATIC_OVL void
1652contained(win, src, total_count, total_size)
1653	winid win;
1654	const char *src;
1655	long *total_count;
1656	long *total_size;
1657{
1658	char buf[BUFSZ];
1659	long count = 0, size = 0;
1660	struct monst *mon;
1661
1662	count_obj(invent, &count, &size, FALSE, TRUE);
1663	count_obj(fobj, &count, &size, FALSE, TRUE);
1664	count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE);
1665	count_obj(migrating_objs, &count, &size, FALSE, TRUE);
1666	/* DEADMONSTER check not required in this loop since they have no inventory */
1667	for (mon = fmon; mon; mon = mon->nmon)
1668	    count_obj(mon->minvent, &count, &size, FALSE, TRUE);
1669	for (mon = migrating_mons; mon; mon = mon->nmon)
1670	    count_obj(mon->minvent, &count, &size, FALSE, TRUE);
1671
1672	*total_count += count; *total_size += size;
1673
1674	Sprintf(buf, template, src, count, size);
1675	putstr(win, 0, buf);
1676}
1677
1678STATIC_OVL void
1679mon_chain(win, src, chain, total_count, total_size)
1680	winid win;
1681	const char *src;
1682	struct monst *chain;
1683	long *total_count;
1684	long *total_size;
1685{
1686	char buf[BUFSZ];
1687	long count, size;
1688	struct monst *mon;
1689
1690	for (count = size = 0, mon = chain; mon; mon = mon->nmon) {
1691	    count++;
1692	    size += sizeof(struct monst) + mon->mxlth + mon->mnamelth;
1693	}
1694	*total_count += count;
1695	*total_size += size;
1696	Sprintf(buf, template, src, count, size);
1697	putstr(win, 0, buf);
1698}
1699
1700/*
1701 * Display memory usage of all monsters and objects on the level.
1702 */
1703static int
1704wiz_show_stats()
1705{
1706	char buf[BUFSZ];
1707	winid win;
1708	long total_obj_size = 0, total_obj_count = 0;
1709	long total_mon_size = 0, total_mon_count = 0;
1710
1711	win = create_nhwindow(NHW_TEXT);
1712	putstr(win, 0, "Current memory statistics:");
1713	putstr(win, 0, "");
1714	Sprintf(buf, "Objects, size %d", (int) sizeof(struct obj));
1715	putstr(win, 0, buf);
1716	putstr(win, 0, "");
1717	putstr(win, 0, count_str);
1718
1719	obj_chain(win, "invent", invent, &total_obj_count, &total_obj_size);
1720	obj_chain(win, "fobj", fobj, &total_obj_count, &total_obj_size);
1721	obj_chain(win, "buried", level.buriedobjlist,
1722				&total_obj_count, &total_obj_size);
1723	obj_chain(win, "migrating obj", migrating_objs,
1724				&total_obj_count, &total_obj_size);
1725	mon_invent_chain(win, "minvent", fmon,
1726				&total_obj_count,&total_obj_size);
1727	mon_invent_chain(win, "migrating minvent", migrating_mons,
1728				&total_obj_count, &total_obj_size);
1729
1730	contained(win, "contained",
1731				&total_obj_count, &total_obj_size);
1732
1733	putstr(win, 0, separator);
1734	Sprintf(buf, template, "Total", total_obj_count, total_obj_size);
1735	putstr(win, 0, buf);
1736
1737	putstr(win, 0, "");
1738	putstr(win, 0, "");
1739	Sprintf(buf, "Monsters, size %d", (int) sizeof(struct monst));
1740	putstr(win, 0, buf);
1741	putstr(win, 0, "");
1742
1743	mon_chain(win, "fmon", fmon,
1744				&total_mon_count, &total_mon_size);
1745	mon_chain(win, "migrating", migrating_mons,
1746				&total_mon_count, &total_mon_size);
1747
1748	putstr(win, 0, separator);
1749	Sprintf(buf, template, "Total", total_mon_count, total_mon_size);
1750	putstr(win, 0, buf);
1751
1752#if defined(__BORLANDC__) && !defined(_WIN32)
1753	show_borlandc_stats(win);
1754#endif
1755
1756	display_nhwindow(win, FALSE);
1757	destroy_nhwindow(win);
1758	return 0;
1759}
1760
1761void
1762sanity_check()
1763{
1764	obj_sanity_check();
1765	timer_sanity_check();
1766}
1767
1768#ifdef NETHACK_DEBUG_MIGRATING_MONS
1769static int
1770wiz_migrate_mons()
1771{
1772	int mcount = 0;
1773	char inbuf[BUFSZ];
1774	struct permonst *ptr;
1775	struct monst *mtmp;
1776	d_level tolevel;
1777	getlin("How many random monsters to migrate? [0]", inbuf);
1778	if (*inbuf == '\033') return 0;
1779	mcount = atoi(inbuf);
1780	if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz))
1781		return 0;
1782	while (mcount > 0) {
1783		if (Is_stronghold(&u.uz))
1784		    assign_level(&tolevel, &valley_level);
1785		else
1786		    get_level(&tolevel, depth(&u.uz) + 1);
1787		ptr = rndmonst();
1788		mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS);
1789		if (mtmp) migrate_to_level(mtmp, ledger_no(&tolevel),
1790				MIGR_RANDOM, (coord *)0);
1791		mcount--;
1792	}
1793	return 0;
1794}
1795#endif
1796
1797#endif /* WIZARD */
1798
1799#define unctrl(c)	((c) <= C('z') ? (0x60 | (c)) : (c))
1800#define unmeta(c)	(0x7f & (c))
1801
1802
1803void
1804rhack(cmd)
1805register char *cmd;
1806{
1807	boolean do_walk, do_rush, prefix_seen, bad_command,
1808		firsttime = (cmd == 0);
1809
1810	iflags.menu_requested = FALSE;
1811	if (firsttime) {
1812		flags.nopick = 0;
1813		cmd = parse();
1814	}
1815	if (*cmd == '\033') {
1816		flags.move = FALSE;
1817		return;
1818	}
1819#ifdef REDO
1820	if (*cmd == DOAGAIN && !in_doagain && saveq[0]) {
1821		in_doagain = TRUE;
1822		stail = 0;
1823		rhack((char *)0);	/* read and execute command */
1824		in_doagain = FALSE;
1825		return;
1826	}
1827	/* Special case of *cmd == ' ' handled better below */
1828	if(!*cmd || *cmd == (char)0377)
1829#else
1830	if(!*cmd || *cmd == (char)0377 || (!flags.rest_on_space && *cmd == ' '))
1831#endif
1832	{
1833		nhbell();
1834		flags.move = FALSE;
1835		return;		/* probably we just had an interrupt */
1836	}
1837	if (iflags.num_pad && iflags.num_pad_mode == 1) {
1838		/* This handles very old inconsistent DOS/Windows behaviour
1839		 * in a new way: earlier, the keyboard handler mapped these,
1840		 * which caused counts to be strange when entered from the
1841		 * number pad. Now do not map them until here.
1842		 */
1843		switch (*cmd) {
1844		    case '5':       *cmd = 'g'; break;
1845		    case M('5'):    *cmd = 'G'; break;
1846		    case M('0'):    *cmd = 'I'; break;
1847        	}
1848        }
1849	/* handle most movement commands */
1850	do_walk = do_rush = prefix_seen = FALSE;
1851	flags.travel = iflags.travel1 = 0;
1852	switch (*cmd) {
1853	 case 'g':  if (movecmd(cmd[1])) {
1854			flags.run = 2;
1855			do_rush = TRUE;
1856		    } else
1857			prefix_seen = TRUE;
1858		    break;
1859	 case '5':  if (!iflags.num_pad) break;	/* else FALLTHRU */
1860	 case 'G':  if (movecmd(lowc(cmd[1]))) {
1861			flags.run = 3;
1862			do_rush = TRUE;
1863		    } else
1864			prefix_seen = TRUE;
1865		    break;
1866	 case '-':  if (!iflags.num_pad) break;	/* else FALLTHRU */
1867	/* Effects of movement commands and invisible monsters:
1868	 * m: always move onto space (even if 'I' remembered)
1869	 * F: always attack space (even if 'I' not remembered)
1870	 * normal movement: attack if 'I', move otherwise
1871	 */
1872	 case 'F':  if (movecmd(cmd[1])) {
1873			flags.forcefight = 1;
1874			do_walk = TRUE;
1875		    } else
1876			prefix_seen = TRUE;
1877		    break;
1878	 case 'm':  if (movecmd(cmd[1]) || u.dz) {
1879			flags.run = 0;
1880			flags.nopick = 1;
1881			if (!u.dz) do_walk = TRUE;
1882			else cmd[0] = cmd[1];	/* "m<" or "m>" */
1883		    } else
1884			prefix_seen = TRUE;
1885		    break;
1886	 case 'M':  if (movecmd(lowc(cmd[1]))) {
1887			flags.run = 1;
1888			flags.nopick = 1;
1889			do_rush = TRUE;
1890		    } else
1891			prefix_seen = TRUE;
1892		    break;
1893	 case '0':  if (!iflags.num_pad) break;
1894		    (void)ddoinv(); /* a convenience borrowed from the PC */
1895		    flags.move = FALSE;
1896		    multi = 0;
1897		    return;
1898	 case CMD_TRAVEL:
1899		    if (iflags.travelcmd) {
1900			    flags.travel = 1;
1901			    iflags.travel1 = 1;
1902			    flags.run = 8;
1903			    flags.nopick = 1;
1904			    do_rush = TRUE;
1905			    break;
1906		    }
1907		    /*FALLTHRU*/
1908	 default:   if (movecmd(*cmd)) {	/* ordinary movement */
1909			flags.run = 0;	/* only matters here if it was 8 */
1910			do_walk = TRUE;
1911		    } else if (movecmd(iflags.num_pad ?
1912				       unmeta(*cmd) : lowc(*cmd))) {
1913			flags.run = 1;
1914			do_rush = TRUE;
1915		    } else if (movecmd(unctrl(*cmd))) {
1916			flags.run = 3;
1917			do_rush = TRUE;
1918		    }
1919		    break;
1920	}
1921
1922	/* some special prefix handling */
1923	/* overload 'm' prefix for ',' to mean "request a menu" */
1924	if (prefix_seen && cmd[1] == ',') {
1925		iflags.menu_requested = TRUE;
1926		++cmd;
1927	}
1928
1929	if (do_walk) {
1930	    if (multi) flags.mv = TRUE;
1931	    domove();
1932	    flags.forcefight = 0;
1933	    return;
1934	} else if (do_rush) {
1935	    if (firsttime) {
1936		if (!multi) multi = max(COLNO,ROWNO);
1937		u.last_str_turn = 0;
1938	    }
1939	    flags.mv = TRUE;
1940	    domove();
1941	    return;
1942	} else if (prefix_seen && cmd[1] == '\033') {	/* <prefix><escape> */
1943	    /* don't report "unknown command" for change of heart... */
1944	    bad_command = FALSE;
1945	} else if (*cmd == ' ' && !flags.rest_on_space) {
1946	    bad_command = TRUE;		/* skip cmdlist[] loop */
1947
1948	/* handle all other commands */
1949	} else {
1950	    register const struct func_tab *tlist;
1951	    int res, NDECL((*func));
1952
1953	    for (tlist = cmdlist; tlist->f_char; tlist++) {
1954		if ((*cmd & 0xff) != (tlist->f_char & 0xff)) continue;
1955
1956		if (u.uburied && !tlist->can_if_buried) {
1957		    You_cant("do that while you are buried!");
1958		    res = 0;
1959		} else {
1960		    /* we discard 'const' because some compilers seem to have
1961		       trouble with the pointer passed to set_occupation() */
1962		    func = ((struct func_tab *)tlist)->f_funct;
1963		    if (tlist->f_text && !occupation && multi)
1964			set_occupation(func, tlist->f_text, multi);
1965		    res = (*func)();		/* perform the command */
1966		}
1967		if (!res) {
1968		    flags.move = FALSE;
1969		    multi = 0;
1970		}
1971		return;
1972	    }
1973	    /* if we reach here, cmd wasn't found in cmdlist[] */
1974	    bad_command = TRUE;
1975	}
1976
1977	if (bad_command) {
1978	    char expcmd[10];
1979	    register char *cp = expcmd;
1980
1981	    while (*cmd && (int)(cp - expcmd) < (int)(sizeof expcmd - 3)) {
1982		if (*cmd >= 040 && *cmd < 0177) {
1983		    *cp++ = *cmd++;
1984		} else if (*cmd & 0200) {
1985		    *cp++ = 'M';
1986		    *cp++ = '-';
1987		    *cp++ = *cmd++ &= ~0200;
1988		} else {
1989		    *cp++ = '^';
1990		    *cp++ = *cmd++ ^ 0100;
1991		}
1992	    }
1993	    *cp = '\0';
1994	    if (!prefix_seen || !iflags.cmdassist ||
1995		!help_dir(0, "Invalid direction key!"))
1996		Norep("Unknown command '%s'.", expcmd);
1997	}
1998	/* didn't move */
1999	flags.move = FALSE;
2000	multi = 0;
2001	return;
2002}
2003
2004int
2005xytod(x, y)	/* convert an x,y pair into a direction code */
2006schar x, y;
2007{
2008	register int dd;
2009
2010	for(dd = 0; dd < 8; dd++)
2011	    if(x == xdir[dd] && y == ydir[dd]) return dd;
2012
2013	return -1;
2014}
2015
2016void
2017dtoxy(cc,dd)	/* convert a direction code into an x,y pair */
2018coord *cc;
2019register int dd;
2020{
2021	cc->x = xdir[dd];
2022	cc->y = ydir[dd];
2023	return;
2024}
2025
2026int
2027movecmd(sym)	/* also sets u.dz, but returns false for <> */
2028char sym;
2029{
2030	register const char *dp;
2031	register const char *sdp;
2032	if(iflags.num_pad) sdp = ndir; else sdp = sdir;	/* DICE workaround */
2033
2034	u.dz = 0;
2035	if(!(dp = index(sdp, sym))) return 0;
2036	u.dx = xdir[dp-sdp];
2037	u.dy = ydir[dp-sdp];
2038	u.dz = zdir[dp-sdp];
2039	if (u.dx && u.dy && u.umonnum == PM_GRID_BUG) {
2040		u.dx = u.dy = 0;
2041		return 0;
2042	}
2043	return !u.dz;
2044}
2045
2046/*
2047 * uses getdir() but unlike getdir() it specifically
2048 * produces coordinates using the direction from getdir()
2049 * and verifies that those coordinates are ok.
2050 *
2051 * If the call to getdir() returns 0, Never_mind is displayed.
2052 * If the resulting coordinates are not okay, emsg is displayed.
2053 *
2054 * Returns non-zero if coordinates in cc are valid.
2055 */
2056int get_adjacent_loc(prompt,emsg,x,y,cc)
2057const char *prompt, *emsg;
2058xchar x,y;
2059coord *cc;
2060{
2061	xchar new_x, new_y;
2062	if (!getdir(prompt)) {
2063		pline(Never_mind);
2064		return 0;
2065	}
2066	new_x = x + u.dx;
2067	new_y = y + u.dy;
2068	if (cc && isok(new_x,new_y)) {
2069		cc->x = new_x;
2070		cc->y = new_y;
2071	} else {
2072		if (emsg) pline(emsg);
2073		return 0;
2074	}
2075	return 1;
2076}
2077
2078int
2079getdir(s)
2080const char *s;
2081{
2082	char dirsym;
2083
2084#ifdef REDO
2085	if(in_doagain || *readchar_queue)
2086	    dirsym = readchar();
2087	else
2088#endif
2089	    dirsym = yn_function ((s && *s != '^') ? s : "In what direction?",
2090					(char *)0, '\0');
2091#ifdef REDO
2092	savech(dirsym);
2093#endif
2094	if(dirsym == '.' || dirsym == 's')
2095		u.dx = u.dy = u.dz = 0;
2096	else if(!movecmd(dirsym) && !u.dz) {
2097		boolean did_help = FALSE;
2098		if(!index(quitchars, dirsym)) {
2099		    if (iflags.cmdassist) {
2100			did_help = help_dir((s && *s == '^') ? dirsym : 0,
2101					    "Invalid direction key!");
2102		    }
2103		    if (!did_help) pline("What a strange direction!");
2104		}
2105		return 0;
2106	}
2107	if(!u.dz && (Stunned || (Confusion && !rn2(5)))) confdir();
2108	return 1;
2109}
2110
2111STATIC_OVL boolean
2112help_dir(sym, msg)
2113char sym;
2114const char *msg;
2115{
2116	char ctrl;
2117	winid win;
2118	static const char wiz_only_list[] = "EFGIOVW";
2119	char buf[BUFSZ], buf2[BUFSZ], *expl;
2120
2121	win = create_nhwindow(NHW_TEXT);
2122	if (!win) return FALSE;
2123	if (msg) {
2124		Sprintf(buf, "cmdassist: %s", msg);
2125		putstr(win, 0, buf);
2126		putstr(win, 0, "");
2127	}
2128	if (letter(sym)) {
2129	    sym = highc(sym);
2130	    ctrl = (sym - 'A') + 1;
2131	    if ((expl = dowhatdoes_core(ctrl, buf2))
2132		&& (!index(wiz_only_list, sym)
2133#ifdef WIZARD
2134		    || wizard
2135#endif
2136	                     )) {
2137		Sprintf(buf, "Are you trying to use ^%c%s?", sym,
2138			index(wiz_only_list, sym) ? "" :
2139			" as specified in the Guidebook");
2140		putstr(win, 0, buf);
2141		putstr(win, 0, "");
2142		putstr(win, 0, expl);
2143		putstr(win, 0, "");
2144		putstr(win, 0, "To use that command, you press");
2145		Sprintf(buf,
2146			"the <Ctrl> key, and the <%c> key at the same time.", sym);
2147		putstr(win, 0, buf);
2148		putstr(win, 0, "");
2149	    }
2150	}
2151	if (iflags.num_pad && u.umonnum == PM_GRID_BUG) {
2152	    putstr(win, 0, "Valid direction keys in your current form (with number_pad on) are:");
2153	    putstr(win, 0, "             8   ");
2154	    putstr(win, 0, "             |   ");
2155	    putstr(win, 0, "          4- . -6");
2156	    putstr(win, 0, "             |   ");
2157	    putstr(win, 0, "             2   ");
2158	} else if (u.umonnum == PM_GRID_BUG) {
2159	    putstr(win, 0, "Valid direction keys in your current form are:");
2160	    putstr(win, 0, "             k   ");
2161	    putstr(win, 0, "             |   ");
2162	    putstr(win, 0, "          h- . -l");
2163	    putstr(win, 0, "             |   ");
2164	    putstr(win, 0, "             j   ");
2165	} else if (iflags.num_pad) {
2166	    putstr(win, 0, "Valid direction keys (with number_pad on) are:");
2167	    putstr(win, 0, "          7  8  9");
2168	    putstr(win, 0, "           \\ | / ");
2169	    putstr(win, 0, "          4- . -6");
2170	    putstr(win, 0, "           / | \\ ");
2171	    putstr(win, 0, "          1  2  3");
2172	} else {
2173	    putstr(win, 0, "Valid direction keys are:");
2174	    putstr(win, 0, "          y  k  u");
2175	    putstr(win, 0, "           \\ | / ");
2176	    putstr(win, 0, "          h- . -l");
2177	    putstr(win, 0, "           / | \\ ");
2178	    putstr(win, 0, "          b  j  n");
2179	};
2180	putstr(win, 0, "");
2181	putstr(win, 0, "          <  up");
2182	putstr(win, 0, "          >  down");
2183	putstr(win, 0, "          .  direct at yourself");
2184	putstr(win, 0, "");
2185	putstr(win, 0, "(Suppress this message with !cmdassist in config file.)");
2186	display_nhwindow(win, FALSE);
2187	destroy_nhwindow(win);
2188	return TRUE;
2189}
2190
2191#endif /* OVL1 */
2192#ifdef OVLB
2193
2194void
2195confdir()
2196{
2197	register int x = (u.umonnum == PM_GRID_BUG) ? 2*rn2(4) : rn2(8);
2198	u.dx = xdir[x];
2199	u.dy = ydir[x];
2200	return;
2201}
2202
2203#endif /* OVLB */
2204#ifdef OVL0
2205
2206int
2207isok(x,y)
2208register int x, y;
2209{
2210	/* x corresponds to curx, so x==1 is the first column. Ach. %% */
2211	return x >= 1 && x <= COLNO-1 && y >= 0 && y <= ROWNO-1;
2212}
2213
2214static NEARDATA int last_multi;
2215
2216/*
2217 * convert a MAP window position into a movecmd
2218 */
2219const char *
2220click_to_cmd(x, y, mod)
2221    int x, y, mod;
2222{
2223    int dir;
2224    static char cmd[4];
2225    cmd[1]=0;
2226
2227    x -= u.ux;
2228    y -= u.uy;
2229
2230    if (iflags.travelcmd) {
2231        if (abs(x) <= 1 && abs(y) <= 1 ) {
2232            x = sgn(x), y = sgn(y);
2233        } else {
2234            u.tx = u.ux+x;
2235            u.ty = u.uy+y;
2236            cmd[0] = CMD_TRAVEL;
2237            return cmd;
2238        }
2239
2240        if(x == 0 && y == 0) {
2241            /* here */
2242            if(IS_FOUNTAIN(levl[u.ux][u.uy].typ) || IS_SINK(levl[u.ux][u.uy].typ)) {
2243                cmd[0]=mod == CLICK_1 ? 'q' : M('d');
2244                return cmd;
2245            } else if(IS_THRONE(levl[u.ux][u.uy].typ)) {
2246                cmd[0]=M('s');
2247                return cmd;
2248            } else if((u.ux == xupstair && u.uy == yupstair)
2249                      || (u.ux == sstairs.sx && u.uy == sstairs.sy && sstairs.up)
2250                      || (u.ux == xupladder && u.uy == yupladder)) {
2251                return "<";
2252            } else if((u.ux == xdnstair && u.uy == ydnstair)
2253                      || (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up)
2254                      || (u.ux == xdnladder && u.uy == ydnladder)) {
2255                return ">";
2256            } else if(OBJ_AT(u.ux, u.uy)) {
2257                cmd[0] = Is_container(level.objects[u.ux][u.uy]) ? M('l') : ',';
2258                return cmd;
2259            } else {
2260                return "."; /* just rest */
2261            }
2262        }
2263
2264        /* directional commands */
2265
2266        dir = xytod(x, y);
2267
2268	if (!m_at(u.ux+x, u.uy+y) && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
2269            cmd[1] = (iflags.num_pad ? ndir[dir] : sdir[dir]);
2270            cmd[2] = 0;
2271            if (IS_DOOR(levl[u.ux+x][u.uy+y].typ)) {
2272                /* slight assistance to the player: choose kick/open for them */
2273                if (levl[u.ux+x][u.uy+y].doormask & D_LOCKED) {
2274                    cmd[0] = C('d');
2275                    return cmd;
2276                }
2277                if (levl[u.ux+x][u.uy+y].doormask & D_CLOSED) {
2278                    cmd[0] = 'o';
2279                    return cmd;
2280                }
2281            }
2282            if (levl[u.ux+x][u.uy+y].typ <= SCORR) {
2283                cmd[0] = 's';
2284                cmd[1] = 0;
2285                return cmd;
2286            }
2287        }
2288    } else {
2289        /* convert without using floating point, allowing sloppy clicking */
2290        if(x > 2*abs(y))
2291            x = 1, y = 0;
2292        else if(y > 2*abs(x))
2293            x = 0, y = 1;
2294        else if(x < -2*abs(y))
2295            x = -1, y = 0;
2296        else if(y < -2*abs(x))
2297            x = 0, y = -1;
2298        else
2299            x = sgn(x), y = sgn(y);
2300
2301        if(x == 0 && y == 0)	/* map click on player to "rest" command */
2302            return ".";
2303
2304        dir = xytod(x, y);
2305    }
2306
2307    /* move, attack, etc. */
2308    cmd[1] = 0;
2309    if(mod == CLICK_1) {
2310	cmd[0] = (iflags.num_pad ? ndir[dir] : sdir[dir]);
2311    } else {
2312	cmd[0] = (iflags.num_pad ? M(ndir[dir]) :
2313		(sdir[dir] - 'a' + 'A')); /* run command */
2314    }
2315
2316    return cmd;
2317}
2318
2319STATIC_OVL char *
2320parse()
2321{
2322#ifdef LINT	/* static char in_line[COLNO]; */
2323	char in_line[COLNO];
2324#else
2325	static char in_line[COLNO];
2326#endif
2327	register int foo;
2328	boolean prezero = FALSE;
2329
2330	multi = 0;
2331	flags.move = 1;
2332	flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
2333
2334	if (!iflags.num_pad || (foo = readchar()) == 'n')
2335	    for (;;) {
2336		foo = readchar();
2337		if (foo >= '0' && foo <= '9') {
2338		    multi = 10 * multi + foo - '0';
2339		    if (multi < 0 || multi >= LARGEST_INT) multi = LARGEST_INT;
2340		    if (multi > 9) {
2341			clear_nhwindow(WIN_MESSAGE);
2342			Sprintf(in_line, "Count: %d", multi);
2343			pline(in_line);
2344			mark_synch();
2345		    }
2346		    last_multi = multi;
2347		    if (!multi && foo == '0') prezero = TRUE;
2348		} else break;	/* not a digit */
2349	    }
2350
2351	if (foo == '\033') {   /* esc cancels count (TH) */
2352	    clear_nhwindow(WIN_MESSAGE);
2353	    multi = last_multi = 0;
2354# ifdef REDO
2355	} else if (foo == DOAGAIN || in_doagain) {
2356	    multi = last_multi;
2357	} else {
2358	    last_multi = multi;
2359	    savech(0);	/* reset input queue */
2360	    savech((char)foo);
2361# endif
2362	}
2363
2364	if (multi) {
2365	    multi--;
2366	    save_cm = in_line;
2367	} else {
2368	    save_cm = (char *)0;
2369	}
2370	in_line[0] = foo;
2371	in_line[1] = '\0';
2372	if (foo == 'g' || foo == 'G' || foo == 'm' || foo == 'M' ||
2373	    foo == 'F' || (iflags.num_pad && (foo == '5' || foo == '-'))) {
2374	    foo = readchar();
2375#ifdef REDO
2376	    savech((char)foo);
2377#endif
2378	    in_line[1] = foo;
2379	    in_line[2] = 0;
2380	}
2381	clear_nhwindow(WIN_MESSAGE);
2382	if (prezero) in_line[0] = '\033';
2383	return(in_line);
2384}
2385
2386#endif /* OVL0 */
2387#ifdef OVLB
2388
2389#ifdef UNIX
2390static
2391void
2392end_of_input()
2393{
2394#ifndef NOSAVEONHANGUP
2395	if (!program_state.done_hup++ && program_state.something_worth_saving)
2396	    (void) dosave0();
2397#endif
2398	exit_nhwindows((char *)0);
2399	clearlocks();
2400	terminate(EXIT_SUCCESS);
2401}
2402#endif
2403
2404#endif /* OVLB */
2405#ifdef OVL0
2406
2407char
2408readchar()
2409{
2410	register int sym;
2411	int x = u.ux, y = u.uy, mod = 0;
2412
2413	if ( *readchar_queue )
2414	    sym = *readchar_queue++;
2415	else
2416#ifdef REDO
2417	    sym = in_doagain ? Getchar() : nh_poskey(&x, &y, &mod);
2418#else
2419	    sym = Getchar();
2420#endif
2421
2422#ifdef UNIX
2423# ifdef NR_OF_EOFS
2424	if (sym == EOF) {
2425	    register int cnt = NR_OF_EOFS;
2426	  /*
2427	   * Some SYSV systems seem to return EOFs for various reasons
2428	   * (?like when one hits break or for interrupted systemcalls?),
2429	   * and we must see several before we quit.
2430	   */
2431	    do {
2432		clearerr(stdin);	/* omit if clearerr is undefined */
2433		sym = Getchar();
2434	    } while (--cnt && sym == EOF);
2435	}
2436# endif /* NR_OF_EOFS */
2437	if (sym == EOF)
2438	    end_of_input();
2439#endif /* UNIX */
2440
2441	if(sym == 0) {
2442	    /* click event */
2443	    readchar_queue = click_to_cmd(x, y, mod);
2444	    sym = *readchar_queue++;
2445	}
2446	return((char) sym);
2447}
2448
2449STATIC_PTR int
2450dotravel()
2451{
2452	/* Keyboard travel command */
2453	static char cmd[2];
2454	coord cc;
2455
2456	if (!iflags.travelcmd) return 0;
2457	cmd[1]=0;
2458	cc.x = iflags.travelcc.x;
2459	cc.y = iflags.travelcc.y;
2460	if (cc.x == -1 && cc.y == -1) {
2461	    /* No cached destination, start attempt from current position */
2462	    cc.x = u.ux;
2463	    cc.y = u.uy;
2464	}
2465	pline("Where do you want to travel to?");
2466	if (getpos(&cc, TRUE, "the desired destination") < 0) {
2467		/* user pressed ESC */
2468		return 0;
2469	}
2470	iflags.travelcc.x = u.tx = cc.x;
2471	iflags.travelcc.y = u.ty = cc.y;
2472	cmd[0] = CMD_TRAVEL;
2473	readchar_queue = cmd;
2474	return 0;
2475}
2476
2477#ifdef PORT_DEBUG
2478# ifdef WIN32CON
2479extern void NDECL(win32con_debug_keystrokes);
2480extern void NDECL(win32con_handler_info);
2481# endif
2482
2483int
2484wiz_port_debug()
2485{
2486	int n, k;
2487	winid win;
2488	anything any;
2489	int item = 'a';
2490	int num_menu_selections;
2491	struct menu_selection_struct {
2492		char *menutext;
2493		void NDECL((*fn));
2494	} menu_selections[] = {
2495#ifdef WIN32CON
2496		{"test win32 keystrokes", win32con_debug_keystrokes},
2497		{"show keystroke handler information", win32con_handler_info},
2498#endif
2499		{(char *)0, (void NDECL((*)))0}		/* array terminator */
2500	};
2501
2502	num_menu_selections = SIZE(menu_selections) - 1;
2503	if (num_menu_selections > 0) {
2504		menu_item *pick_list;
2505		win = create_nhwindow(NHW_MENU);
2506		start_menu(win);
2507		for (k=0; k < num_menu_selections; ++k) {
2508			any.a_int = k+1;
2509			add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE,
2510				menu_selections[k].menutext, MENU_UNSELECTED);
2511		}
2512		end_menu(win, "Which port debugging feature?");
2513		n = select_menu(win, PICK_ONE, &pick_list);
2514		destroy_nhwindow(win);
2515		if (n > 0) {
2516			n = pick_list[0].item.a_int - 1;
2517			free((genericptr_t) pick_list);
2518			/* execute the function */
2519			(*menu_selections[n].fn)();
2520		}
2521	} else
2522		pline("No port-specific debug capability defined.");
2523	return 0;
2524}
2525# endif /*PORT_DEBUG*/
2526
2527#endif /* OVL0 */
2528#ifdef OVLB
2529/*
2530 *   Parameter validator for generic yes/no function to prevent
2531 *   the core from sending too long a prompt string to the
2532 *   window port causing a buffer overflow there.
2533 */
2534char
2535yn_function(query,resp, def)
2536const char *query,*resp;
2537char def;
2538{
2539	char qbuf[QBUFSZ];
2540	unsigned truncspot, reduction = sizeof(" [N]  ?") + 1;
2541
2542	if (resp) reduction += strlen(resp) + sizeof(" () ");
2543	if (strlen(query) < (QBUFSZ - reduction))
2544		return (*windowprocs.win_yn_function)(query, resp, def);
2545	paniclog("Query truncated: ", query);
2546	reduction += sizeof("...");
2547	truncspot = QBUFSZ - reduction;
2548	(void) strncpy(qbuf, query, (int)truncspot);
2549	qbuf[truncspot] = '\0';
2550	Strcat(qbuf,"...");
2551	return (*windowprocs.win_yn_function)(qbuf, resp, def);
2552}
2553#endif
2554
2555/*cmd.c*/
2556