diff --git a/src/cmd.c b/src/cmd.c new file mode 100644 index 000000000..ecd0915d0 --- /dev/null +++ b/src/cmd.c @@ -0,0 +1,2088 @@ +/* SCCS Id: @(#)cmd.c 3.3 2000/05/05 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "func_tab.h" +/* #define DEBUG */ /* uncomment for debugging */ + +/* + * Some systems may have getchar() return EOF for various reasons, and + * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs. + */ +#if defined(SYSV) || defined(DGUX) || defined(HPUX) +#define NR_OF_EOFS 20 +#endif + +#define CMD_TRAVEL (char)0x90 + +#ifdef DEBUG +/* + * only one "wiz_debug_cmd" routine should be available (in whatever + * module you are trying to debug) or things are going to get rather + * hard to link :-) + */ +extern void NDECL(wiz_debug_cmd); +#endif + +#ifdef DUMB /* stuff commented out in extern.h, but needed here */ +extern int NDECL(doapply); /**/ +extern int NDECL(dorub); /**/ +extern int NDECL(dojump); /**/ +extern int NDECL(doextlist); /**/ +extern int NDECL(dodrop); /**/ +extern int NDECL(doddrop); /**/ +extern int NDECL(dodown); /**/ +extern int NDECL(doup); /**/ +extern int NDECL(donull); /**/ +extern int NDECL(dowipe); /**/ +extern int NDECL(do_mname); /**/ +extern int NDECL(ddocall); /**/ +extern int NDECL(dotakeoff); /**/ +extern int NDECL(doremring); /**/ +extern int NDECL(dowear); /**/ +extern int NDECL(doputon); /**/ +extern int NDECL(doddoremarm); /**/ +extern int NDECL(dokick); /**/ +extern int NDECL(dofire); /**/ +extern int NDECL(dothrow); /**/ +extern int NDECL(doeat); /**/ +extern int NDECL(done2); /**/ +extern int NDECL(doengrave); /**/ +extern int NDECL(dopickup); /**/ +extern int NDECL(ddoinv); /**/ +extern int NDECL(dotypeinv); /**/ +extern int NDECL(dolook); /**/ +extern int NDECL(doprgold); /**/ +extern int NDECL(doprwep); /**/ +extern int NDECL(doprarm); /**/ +extern int NDECL(doprring); /**/ +extern int NDECL(dopramulet); /**/ +extern int NDECL(doprtool); /**/ +extern int NDECL(dosuspend); /**/ +extern int NDECL(doforce); /**/ +extern int NDECL(doopen); /**/ +extern int NDECL(doclose); /**/ +extern int NDECL(dosh); /**/ +extern int NDECL(dodiscovered); /**/ +extern int NDECL(doset); /**/ +extern int NDECL(dotogglepickup); /**/ +extern int NDECL(dowhatis); /**/ +extern int NDECL(doquickwhatis); /**/ +extern int NDECL(dowhatdoes); /**/ +extern int NDECL(dohelp); /**/ +extern int NDECL(dohistory); /**/ +extern int NDECL(doloot); /**/ +extern int NDECL(dodrink); /**/ +extern int NDECL(dodip); /**/ +extern int NDECL(dosacrifice); /**/ +extern int NDECL(dopray); /**/ +extern int NDECL(doturn); /**/ +extern int NDECL(doredraw); /**/ +extern int NDECL(doread); /**/ +extern int NDECL(dosave); /**/ +extern int NDECL(dosearch); /**/ +extern int NDECL(doidtrap); /**/ +extern int NDECL(dopay); /**/ +extern int NDECL(dosit); /**/ +extern int NDECL(dotalk); /**/ +extern int NDECL(docast); /**/ +extern int NDECL(dovspell); /**/ +extern int NDECL(dotele); /**/ +extern int NDECL(dountrap); /**/ +extern int NDECL(doversion); /**/ +extern int NDECL(doextversion); /**/ +extern int NDECL(doswapweapon); /**/ +extern int NDECL(dowield); /**/ +extern int NDECL(dowieldquiver); /**/ +extern int NDECL(dozap); /**/ +extern int NDECL(doorganize); /**/ +#endif /* DUMB */ + +#ifdef OVL1 +static int NDECL((*timed_occ_fn)); +#endif /* OVL1 */ + +STATIC_PTR int NDECL(doprev_message); +STATIC_PTR int NDECL(timed_occupation); +STATIC_PTR int NDECL(doextcmd); +STATIC_PTR int NDECL(domonability); +STATIC_PTR int NDECL(dotravel); +# ifdef WIZARD +STATIC_PTR int NDECL(wiz_wish); +STATIC_PTR int NDECL(wiz_identify); +STATIC_PTR int NDECL(wiz_map); +STATIC_PTR int NDECL(wiz_genesis); +STATIC_PTR int NDECL(wiz_where); +STATIC_PTR int NDECL(wiz_detect); +STATIC_PTR int NDECL(wiz_level_tele); +STATIC_PTR int NDECL(wiz_show_seenv); +STATIC_PTR int NDECL(wiz_show_vision); +STATIC_PTR int NDECL(wiz_mon_polycontrol); +STATIC_PTR int NDECL(wiz_show_wmodes); +#if defined(__BORLANDC__) && !defined(_WIN32) +extern void FDECL(show_borlandc_stats, (winid)); +#endif +STATIC_DCL void FDECL(count_obj, (struct obj *, long *, long *, BOOLEAN_P, BOOLEAN_P)); +STATIC_DCL void FDECL(obj_chain, (winid, const char *, struct obj *, long *, long *)); +STATIC_DCL void FDECL(mon_invent_chain, (winid, const char *, struct monst *, long *, long *)); +STATIC_DCL void FDECL(mon_chain, (winid, const char *, struct monst *, long *, long *)); +STATIC_DCL void FDECL(contained, (winid, const char *, long *, long *)); +STATIC_PTR int NDECL(wiz_show_stats); +# endif +STATIC_PTR int NDECL(enter_explore_mode); +STATIC_PTR int NDECL(doattributes); +STATIC_PTR int NDECL(doconduct); /**/ +STATIC_PTR void NDECL(minimal_enlightenment); + +#ifdef OVLB +STATIC_DCL void FDECL(enlght_line, (const char *,const char *,const char *)); +#ifdef UNIX +static void NDECL(end_of_input); +#endif +#endif /* OVLB */ + +static const char* readchar_queue=""; + +STATIC_DCL char *NDECL(parse); + +#ifdef OVL1 + +STATIC_PTR int +doprev_message() +{ + return nh_doprev_message(); +} + +/* Count down by decrementing multi */ +STATIC_PTR int +timed_occupation() +{ + (*timed_occ_fn)(); + if (multi > 0) + multi--; + return multi > 0; +} + +/* If you have moved since initially setting some occupations, they + * now shouldn't be able to restart. + * + * The basic rule is that if you are carrying it, you can continue + * since it is with you. If you are acting on something at a distance, + * your orientation to it must have changed when you moved. + * + * The exception to this is taking off items, since they can be taken + * off in a number of ways in the intervening time, screwing up ordering. + * + * Currently: Take off all armor. + * Picking Locks / Forcing Chests. + * Setting traps. + */ +void +reset_occupations() +{ + reset_remarm(); + reset_pick(); + reset_trapset(); +} + +/* If a time is given, use it to timeout this function, otherwise the + * function times out by its own means. + */ +void +set_occupation(fn, txt, xtime) +int NDECL((*fn)); +const char *txt; +int xtime; +{ + if (xtime) { + occupation = timed_occupation; + timed_occ_fn = fn; + } else + occupation = fn; + occtxt = txt; + occtime = 0; + return; +} + +#ifdef REDO + +static char NDECL(popch); + +/* Provide a means to redo the last command. The flag `in_doagain' is set + * to true while redoing the command. This flag is tested in commands that + * require additional input (like `throw' which requires a thing and a + * direction), and the input prompt is not shown. Also, while in_doagain is + * TRUE, no keystrokes can be saved into the saveq. + */ +#define BSIZE 20 +static char pushq[BSIZE], saveq[BSIZE]; +static NEARDATA int phead, ptail, shead, stail; + +static char +popch() { + /* If occupied, return '\0', letting tgetch know a character should + * be read from the keyboard. If the character read is not the + * ABORT character (as checked in pcmain.c), that character will be + * pushed back on the pushq. + */ + if (occupation) return '\0'; + if (in_doagain) return(char)((shead != stail) ? saveq[stail++] : '\0'); + else return(char)((phead != ptail) ? pushq[ptail++] : '\0'); +} + +char +pgetchar() { /* curtesy of aeb@cwi.nl */ + register int ch; + + if(!(ch = popch())) + ch = nhgetch(); + return((char)ch); +} + +/* A ch == 0 resets the pushq */ +void +pushch(ch) +char ch; +{ + if (!ch) + phead = ptail = 0; + if (phead < BSIZE) + pushq[phead++] = ch; + return; +} + +/* A ch == 0 resets the saveq. Only save keystrokes when not + * replaying a previous command. + */ +void +savech(ch) +char ch; +{ + if (!in_doagain) { + if (!ch) + phead = ptail = shead = stail = 0; + else if (shead < BSIZE) + saveq[shead++] = ch; + } + return; +} +#endif /* REDO */ + +#endif /* OVL1 */ +#ifdef OVLB + +STATIC_PTR int +doextcmd() /* here after # - now read a full-word command */ +{ + int idx, retval; + + /* keep repeating until we don't run help or quit */ + do { + idx = get_ext_cmd(); + if (idx < 0) return 0; /* quit */ + + retval = (*extcmdlist[idx].ef_funct)(); + } while (extcmdlist[idx].ef_funct == doextlist); + + return retval; +} + +int +doextlist() /* here after #? - now list all full-word commands */ +{ + register const struct ext_func_tab *efp; + char buf[BUFSZ]; + winid datawin; + + datawin = create_nhwindow(NHW_TEXT); + putstr(datawin, 0, ""); + putstr(datawin, 0, " Extended Commands List"); + putstr(datawin, 0, ""); + putstr(datawin, 0, " Press '#', then type:"); + putstr(datawin, 0, ""); + + for(efp = extcmdlist; efp->ef_txt; efp++) { + Sprintf(buf, " %-14s - %s.", efp->ef_txt, efp->ef_desc); + putstr(datawin, 0, buf); + } + display_nhwindow(datawin, FALSE); + destroy_nhwindow(datawin); + return 0; +} + +#ifdef TTY_GRAPHICS +#define MAX_EXT_CMD 40 /* Change if we ever have > 40 ext cmds */ +/* + * This is currently used only by the tty port and is + * controlled via runtime option 'extmenu' + */ +int +extcmd_via_menu() /* here after # - now show pick-list of possible commands */ +{ + const struct ext_func_tab *efp; + menu_item *pick_list = (menu_item *)0; + winid win; + anything any; + const struct ext_func_tab *choices[MAX_EXT_CMD]; + char buf[BUFSZ]; + char cbuf[QBUFSZ], prompt[QBUFSZ], fmtstr[20]; + int i, n, nchoices, acount; + int ret, biggest; + int accelerator, prevaccelerator; + int matchlevel = 0; + + ret = 0; + cbuf[0] = '\0'; + biggest = 0; + while (!ret) { + i = n = 0; + accelerator = 0; + any.a_void = 0; + /* populate choices */ + for(efp = extcmdlist; efp->ef_txt; efp++) { + if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) { + choices[i++] = efp; + if ((int)strlen(efp->ef_desc) > biggest) { + biggest = strlen(efp->ef_desc); + Sprintf(fmtstr,"%%-%ds", biggest + 15); + } +#ifdef DEBUG + if (i >= MAX_EXT_CMD - 2) { + impossible("Exceeded %d extended commands in doextcmd() menu", + MAX_EXT_CMD - 2); + return 0; + } +#endif + } + } + choices[i] = (struct ext_func_tab *)0; + nchoices = i; + /* if we're down to one, we have our selection so get out of here */ + if (nchoices == 1) { + for (i = 0; extcmdlist[i].ef_txt != (char *)0; i++) + if (!strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) { + ret = i; + break; + } + break; + } + + /* otherwise... */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + prevaccelerator = 0; + acount = 0; + for(i = 0; choices[i]; ++i) { + accelerator = choices[i]->ef_txt[matchlevel]; + if (accelerator != prevaccelerator || nchoices < (ROWNO - 3)) { + if (acount) { + /* flush the extended commands for that letter already in buf */ + Sprintf(buf, fmtstr, prompt); + any.a_char = prevaccelerator; + add_menu(win, NO_GLYPH, &any, any.a_char, 0, + ATR_NONE, buf, FALSE); + acount = 0; + } + } + prevaccelerator = accelerator; + if (!acount || nchoices < (ROWNO - 3)) { + Sprintf(prompt, "%s [%s]", choices[i]->ef_txt, + choices[i]->ef_desc); + } else if (acount == 1) { + Sprintf(prompt, "%s or %s", choices[i-1]->ef_txt, + choices[i]->ef_txt); + } else { + Strcat(prompt," or "); + Strcat(prompt, choices[i]->ef_txt); + } + ++acount; + } + if (acount) { + /* flush buf */ + Sprintf(buf, fmtstr, prompt); + any.a_char = prevaccelerator; + add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE, buf, FALSE); + } + Sprintf(prompt, "Extended Command: %s", cbuf); + end_menu(win, prompt); + n = select_menu(win, PICK_ONE, &pick_list); + destroy_nhwindow(win); + if (n==1) { + if (matchlevel > (QBUFSZ - 2)) { + free((genericptr_t)pick_list); +#ifdef DEBUG + impossible("Too many characters (%d) entered in extcmd_via_menu()", + matchlevel); +#endif + ret = -1; + } else { + cbuf[matchlevel++] = pick_list[0].item.a_char; + cbuf[matchlevel] = '\0'; + free((genericptr_t)pick_list); + } + } else { + if (matchlevel) { + ret = 0; + matchlevel = 0; + } else + ret = -1; + } + } + return ret; +} +#endif + +STATIC_PTR int +domonability() +{ + if (can_breathe(youmonst.data)) return dobreathe(); + else if (attacktype(youmonst.data, AT_SPIT)) return dospit(); + else if (youmonst.data->mlet == S_NYMPH) return doremove(); + else if (attacktype(youmonst.data, AT_GAZE)) return dogaze(); + else if (is_were(youmonst.data)) return dosummon(); + else if (webmaker(youmonst.data)) return dospinweb(); + else if (is_hider(youmonst.data)) return dohide(); + else if (is_mind_flayer(youmonst.data)) return domindblast(); + else if (u.umonnum == PM_GREMLIN) { + if(IS_FOUNTAIN(levl[u.ux][u.uy].typ)) { + if (split_mon(&youmonst, (struct monst *)0)) + dryup(u.ux, u.uy, TRUE); + } else There("is no fountain here."); + } else if (is_unicorn(youmonst.data)) { + use_unicorn_horn((struct obj *)0); + return 1; + } else if (youmonst.data->msound == MS_SHRIEK) { + You("shriek."); + if(u.uburied) + pline("Unfortunately sound does not carry well through rock."); + else aggravate(); + } else if (Upolyd) + pline("Any special ability you may have is purely reflexive."); + else You("don't have a special ability in your normal form!"); + return 0; +} + +STATIC_PTR int +enter_explore_mode() +{ + if(!discover && !wizard) { + pline("Beware! From explore mode there will be no return to normal game."); + if (yn("Do you want to enter explore mode?") == 'y') { + clear_nhwindow(WIN_MESSAGE); + You("are now in non-scoring explore mode."); + discover = TRUE; + } + else { + clear_nhwindow(WIN_MESSAGE); + pline("Resuming normal game."); + } + } + return 0; +} + +#ifdef WIZARD +STATIC_PTR int +wiz_wish() /* Unlimited wishes for debug mode by Paul Polderman */ +{ + if (wizard) { + boolean save_verbose = flags.verbose; + + flags.verbose = FALSE; + makewish(); + flags.verbose = save_verbose; + (void) encumber_msg(); + } else + pline("Unavailable command '^W'."); + return 0; +} + +STATIC_PTR int +wiz_identify() +{ + if (wizard) identify_pack(0); + else pline("Unavailable command '^I'."); + return 0; +} + +/* reveal the level map and any traps on it */ +STATIC_PTR int +wiz_map() +{ + if (wizard) { + struct trap *t; + + for (t = ftrap; t != 0; t = t->ntrap) { + t->tseen = 1; + map_trap(t, TRUE); + } + do_mapping(); + } else + pline("Unavailable command '^F'."); + return 0; +} + +STATIC_PTR int +wiz_genesis() +{ + if (wizard) (void) create_particular(); + else pline("Unavailable command '^G'."); + return 0; +} + +STATIC_PTR int +wiz_where() +{ + if (wizard) print_dungeon(); + else pline("Unavailable command '^O'."); + return 0; +} + +STATIC_PTR int +wiz_detect() +{ + if(wizard) (void) findit(); + else pline("Unavailable command '^E'."); + return 0; +} + +STATIC_PTR int +wiz_level_tele() +{ + if (wizard) level_tele(); + else pline("Unavailable command '^V'."); + return 0; +} + +STATIC_PTR int +wiz_mon_polycontrol() +{ + iflags.mon_polycontrol = !iflags.mon_polycontrol; + pline("Monster polymorph control is %s.", iflags.mon_polycontrol ? "on" : "off"); + return 0; +} + +STATIC_PTR int +wiz_show_seenv() +{ + winid win; + int x, y, v, startx, stopx, curx; + char row[COLNO+1]; + + win = create_nhwindow(NHW_TEXT); + /* + * Each seenv description takes up 2 characters, so center + * the seenv display around the hero. + */ + startx = max(1, u.ux-(COLNO/4)); + stopx = min(startx+(COLNO/2), COLNO); + /* can't have a line exactly 80 chars long */ + if (stopx - startx == COLNO/2) startx++; + + for (y = 0; y < ROWNO; y++) { + for (x = startx, curx = 0; x < stopx; x++, curx += 2) { + if (x == u.ux && y == u.uy) { + row[curx] = row[curx+1] = '@'; + } else { + v = levl[x][y].seenv & 0xff; + if (v == 0) + row[curx] = row[curx+1] = ' '; + else + Sprintf(&row[curx], "%02x", v); + } + } + /* remove trailing spaces */ + for (x = curx-1; x >= 0; x--) + if (row[x] != ' ') break; + row[x+1] = '\0'; + + putstr(win, 0, row); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return 0; +} + +STATIC_PTR int +wiz_show_vision() +{ + winid win; + int x, y, v; + char row[COLNO+1]; + + win = create_nhwindow(NHW_TEXT); + Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit", + COULD_SEE, IN_SIGHT, TEMP_LIT); + putstr(win, 0, row); + putstr(win, 0, ""); + for (y = 0; y < ROWNO; y++) { + for (x = 1; x < COLNO; x++) { + if (x == u.ux && y == u.uy) + row[x] = '@'; + else { + v = viz_array[y][x]; /* data access should be hidden */ + if (v == 0) + row[x] = ' '; + else + row[x] = '0' + viz_array[y][x]; + } + } + /* remove trailing spaces */ + for (x = COLNO-1; x >= 1; x--) + if (row[x] != ' ') break; + row[x+1] = '\0'; + + putstr(win, 0, &row[1]); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return 0; +} + +STATIC_PTR int +wiz_show_wmodes() +{ + winid win; + int x,y; + char row[COLNO+1]; + struct rm *lev; + + win = create_nhwindow(NHW_TEXT); + for (y = 0; y < ROWNO; y++) { + for (x = 0; x < COLNO; x++) { + lev = &levl[x][y]; + if (x == u.ux && y == u.uy) + row[x] = '@'; + else if (IS_WALL(lev->typ) || lev->typ == SDOOR) + row[x] = '0' + (lev->wall_info & WM_MASK); + else if (lev->typ == CORR) + row[x] = '#'; + else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ)) + row[x] = '.'; + else + row[x] = 'x'; + } + row[COLNO] = '\0'; + putstr(win, 0, row); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return 0; +} + +#endif /* WIZARD */ + + +/* -enlightenment and conduct- */ +static winid en_win; +static const char + *You_ = "You ", + *are = "are ", *were = "were ", + *have = "have ", *had = "had ", + *can = "can ", *could = "could "; +static const char + *have_been = "have been ", + *have_never = "have never ", *never = "never "; + +#define enl_msg(prefix,present,past,suffix) \ + enlght_line(prefix, final ? past : present, suffix) +#define you_are(attr) enl_msg(You_,are,were,attr) +#define you_have(attr) enl_msg(You_,have,had,attr) +#define you_can(attr) enl_msg(You_,can,could,attr) +#define you_have_been(goodthing) enl_msg(You_,have_been,were,goodthing) +#define you_have_never(badthing) enl_msg(You_,have_never,never,badthing) +#define you_have_X(something) enl_msg(You_,have,(const char *)"",something) + +static void +enlght_line(start, middle, end) +const char *start, *middle, *end; +{ + char buf[BUFSZ]; + + Sprintf(buf, "%s%s%s.", start, middle, end); + putstr(en_win, 0, buf); +} + +void +enlightenment(final) +int final; /* 0 => still in progress; 1 => over, survived; 2 => dead */ +{ + int ltmp; + char buf[BUFSZ]; + + en_win = create_nhwindow(NHW_MENU); + putstr(en_win, 0, final ? "Final Attributes:" : "Current Attributes:"); + putstr(en_win, 0, ""); + +#ifdef ELBERETH + if (u.uevent.uhand_of_elbereth) { + static const char *hofe_titles[3] = { + "the Hand of Elbereth", + "the Envoy of Balance", + "the Glory of Arioch" + }; + you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1]); + } +#endif + + /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */ + if (u.ualign.record >= 20) you_are("piously aligned"); + else if (u.ualign.record > 13) you_are("devoutly aligned"); + else if (u.ualign.record > 8) you_are("fervently aligned"); + else if (u.ualign.record > 3) you_are("stridently aligned"); + else if (u.ualign.record == 3) you_are("aligned"); + else if (u.ualign.record > 0) you_are("haltingly aligned"); + else if (u.ualign.record == 0) you_are("nominally aligned"); + else if (u.ualign.record >= -3) you_have("strayed"); + else if (u.ualign.record >= -8) you_have("sinned"); + else you_have("transgressed"); +#ifdef WIZARD + if (wizard) { + Sprintf(buf, " %d", u.ualign.record); + enl_msg("Your alignment ", "is", "was", buf); + } +#endif + + /*** Resistances to troubles ***/ + if (Fire_resistance) you_are("fire resistant"); + if (Cold_resistance) you_are("cold resistant"); + if (Sleep_resistance) you_are("sleep resistant"); + if (Disint_resistance) you_are("disintegration-resistant"); + if (Shock_resistance) you_are("shock resistant"); + if (Poison_resistance) you_are("poison resistant"); + if (Drain_resistance) you_are("level-drain resistant"); + if (Sick_resistance) you_are("immune to sickness"); + if (Antimagic) you_are("magic-protected"); + if (Acid_resistance) you_are("acid resistant"); + if (Stone_resistance) + you_are("petrification resistant"); + if (Invulnerable) you_are("invulnerable"); + if (u.uedibility) you_can("recognize detrimental food"); + + /*** Troubles ***/ + if (Halluc_resistance) + enl_msg("You resist", "", "ed", " hallucinations"); + if (final) { + if (Hallucination) you_are("hallucinating"); + if (Stunned) you_are("stunned"); + if (Confusion) you_are("confused"); + if (Blinded) you_are("blinded"); + if (Sick) { + if (u.usick_type & SICK_VOMITABLE) + you_are("sick from food poisoning"); + if (u.usick_type & SICK_NONVOMITABLE) + you_are("sick from illness"); + } + } + if (Stoned) you_are("turning to stone"); + if (Slimed) you_are("turning into slime"); + if (Strangled) you_are((u.uburied) ? "buried" : "being strangled"); + if (Glib) { + Sprintf(buf, "slippery %s", makeplural(body_part(FINGER))); + you_have(buf); + } + if (Fumbling) enl_msg("You fumble", "", "d", ""); + if (Wounded_legs +#ifdef STEED + && !u.usteed +#endif + ) { + Sprintf(buf, "wounded %s", makeplural(body_part(LEG))); + you_have(buf); + } + if (Sleeping) enl_msg("You ", "fall", "fell", " asleep"); + if (Hunger) enl_msg("You hunger", "", "ed", " rapidly"); + + /*** Vision and senses ***/ + if (See_invisible) enl_msg(You_, "see", "saw", " invisible"); + if (Blind_telepat) you_are("telepathic"); + if (Warning) you_are("warned"); + if (Warn_of_mon && flags.warntype) { + Sprintf(buf, "aware of the presence of %s", + (flags.warntype & M2_ORC) ? "orcs" : + (flags.warntype & M2_DEMON) ? "demons" : + something); + you_are(buf); + } + if (Undead_warning) you_are("warned of undead"); + if (Searching) you_have("automatic searching"); + if (Clairvoyant) you_are("clairvoyant"); + if (Infravision) you_have("infravision"); + if (Detect_monsters) you_are("sensing the presence of monsters"); + if (u.umconf) you_are("going to confuse monsters"); + + /*** Appearance and behavior ***/ + if (Adornment) you_are("adorned"); + if (Invisible) you_are("invisible"); + else if (Invis) you_are("invisible to others"); + /* ordinarily "visible" is redundant; this is a special case for + the situation when invisibility would be an expected attribute */ + else if ((HInvis || EInvis || pm_invisible(youmonst.data)) && BInvis) + you_are("visible"); + if (Displaced) you_are("displaced"); + if (Stealth) you_are("stealthy"); + if (Aggravate_monster) enl_msg("You aggravate", "", "d", " monsters"); + if (Conflict) enl_msg("You cause", "", "d", " conflict"); + + /*** Transportation ***/ + if (Jumping) you_can("jump"); + if (Teleportation) you_can("teleport"); + if (Teleport_control) you_have("teleport control"); + if (Lev_at_will) you_are("levitating, at will"); + else if (Levitation) you_are("levitating"); /* without control */ + else if (Flying) you_can("fly"); + if (Wwalking) you_can("walk on water"); + if (Swimming) you_can("swim"); + if (Breathless) you_can("survive without air"); + else if (Amphibious) you_can("breathe water"); + if (Passes_walls) you_can("walk through walls"); +#ifdef STEED + if (u.usteed) { + Sprintf(buf, "riding %s", y_monnam(u.usteed)); + you_are(buf); + } +#endif + if (u.uswallow) { + Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck)); +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%u)", u.uswldtim); +#endif + you_are(buf); + } else if (u.ustuck) { + Sprintf(buf, "%s %s", + (Upolyd && sticks(youmonst.data)) ? "holding" : "held by", + a_monnam(u.ustuck)); + you_are(buf); + } + + /*** Physical attributes ***/ + if (Slow_digestion) you_have("slower digestion"); + if (Regeneration) enl_msg("You regenerate", "", "d", ""); + if (u.uspellprot || Protection) you_are("protected"); + if (Protection_from_shape_changers) + you_are("protected from shape changers"); + if (Polymorph) you_are("polymorphing"); + if (Polymorph_control) you_have("polymorph control"); + if (u.ulycn >= LOW_PM) { + Strcpy(buf, an(mons[u.ulycn].mname)); + you_are(buf); + } + if (Upolyd) { + if (u.ulycn >= LOW_PM) Strcpy(buf, "in beast form"); + else Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname)); +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%d)", u.mtimedone); +#endif + you_are(buf); + } + if (Unchanging) you_can("not change from your current form"); + if (Fast) you_are(Very_fast ? "very fast" : "fast"); + if (Reflecting) you_have("reflection"); + if (Free_action) you_have("free action"); + if (Fixed_abil) you_have("fixed abilities"); + if (Lifesaved) + enl_msg("Your life ", "will be", "would have been", " saved"); + if (u.twoweap) you_are("wielding two weapons at once"); + + /*** Miscellany ***/ + if (Luck) { + ltmp = abs((int)Luck); + Sprintf(buf, "%s%slucky", + ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "", + Luck < 0 ? "un" : ""); +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%d)", Luck); +#endif + you_are(buf); + } +#ifdef WIZARD + else if (wizard) enl_msg("Your luck ", "is", "was", " zero"); +#endif + if (u.moreluck > 0) you_have("extra luck"); + else if (u.moreluck < 0) you_have("reduced luck"); + if (carrying(LUCKSTONE) || stone_luck(TRUE)) { + ltmp = stone_luck(FALSE); + if (ltmp <= 0) + enl_msg("Bad luck ", "does", "did", " not time out for you"); + if (ltmp >= 0) + enl_msg("Good luck ", "does", "did", " not time out for you"); + } + + if (u.ugangr) { + Sprintf(buf, " %sangry with you", + u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : ""); +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%d)", u.ugangr); +#endif + enl_msg(u_gname(), " is", " was", buf); + } else + /* + * We need to suppress this when the game is over, because death + * can change the value calculated by can_pray(), potentially + * resulting in a false claim that you could have prayed safely. + */ + if (!final) { +#if 0 + /* "can [not] safely pray" vs "could [not] have safely prayed" */ + Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ", + final ? "have " : "", final ? "ed" : ""); +#else + Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not "); +#endif +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%d)", u.ublesscnt); +#endif + you_can(buf); + } + + { + const char *p; + + buf[0] = '\0'; + if (final < 2) { /* still in progress, or quit/escaped/ascended */ + p = "survived after being killed "; + switch (u.umortality) { + case 0: p = !final ? (char *)0 : "survived"; break; + case 1: Strcpy(buf, "once"); break; + case 2: Strcpy(buf, "twice"); break; + case 3: Strcpy(buf, "thrice"); break; + default: Sprintf(buf, "%d times", u.umortality); + break; + } + } else { /* game ended in character's death */ + p = "are dead"; + switch (u.umortality) { + case 0: impossible("dead without dying?"); + case 1: break; /* just "are dead" */ + default: Sprintf(buf, " (%d%s time!)", u.umortality, + ordin(u.umortality)); + break; + } + } + if (p) enl_msg(You_, "have been killed ", p, buf); + } + + display_nhwindow(en_win, TRUE); + destroy_nhwindow(en_win); + return; +} + +/* + * Courtesy function for non-debug, non-explorer mode players + * to help refresh them about who/what they are. + */ +STATIC_OVL void +minimal_enlightenment() +{ + winid tmpwin; + menu_item *selected; + anything any; + char buf[BUFSZ], buf2[BUFSZ]; + static char fmtstr[] = "%-15s: %-12s"; + + any.a_void = 0; + buf[0] = buf2[0] = '\0'; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, "Starting", FALSE); + + /* Starting name, race, role, gender */ + Sprintf(buf, fmtstr, "name", plname); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + Sprintf(buf, fmtstr, "race", urace.noun); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + Sprintf(buf, fmtstr, "role", + (flags.initgend && urole.name.f) ? urole.name.f : urole.name.m); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + /* Starting alignment */ + Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL])); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + /* Current name, race, role, gender */ + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, "Current", FALSE); + Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + if (Upolyd) { + Sprintf(buf, fmtstr, "role (base)", + (u.mfemale && urole.name.f) ? urole.name.f : urole.name.m); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + } else { + Sprintf(buf, fmtstr, "role", + (flags.female && urole.name.f) ? urole.name.f : urole.name.m); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + } + Sprintf(buf, fmtstr, "gender", genders[poly_gender()].adj); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + if (Upolyd) { + Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + } + + /* Current alignment */ + Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type)); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + /* Deity list */ + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, "Deities", FALSE); + Sprintf(buf2, "%-17s%s", align_gname(A_CHAOTIC), + (u.ualignbase[A_ORIGINAL] == u.ualign.type + && u.ualign.type == A_CHAOTIC) ? " (s,c)" : + (u.ualignbase[A_ORIGINAL] == A_CHAOTIC) ? " (s)" : + (u.ualign.type == A_CHAOTIC) ? " (c)" : ""); + Sprintf(buf, fmtstr, "chaotic deity", buf2); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + Sprintf(buf2, "%-17s%s", align_gname(A_NEUTRAL), + (u.ualignbase[A_ORIGINAL] == u.ualign.type + && u.ualign.type == A_NEUTRAL) ? " (s,c)" : + (u.ualignbase[A_ORIGINAL] == A_NEUTRAL) ? " (s)" : + (u.ualign.type == A_NEUTRAL) ? " (c)" : ""); + Sprintf(buf, fmtstr, "neutral deity", buf2); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + Sprintf(buf2, "%-17s%s", align_gname(A_LAWFUL), + (u.ualignbase[A_ORIGINAL] == u.ualign.type && u.ualign.type == A_LAWFUL) ? " (s,c)" : + (u.ualignbase[A_ORIGINAL] == A_LAWFUL) ? " (s)" : + (u.ualign.type == A_LAWFUL) ? " (c)" : ""); + Sprintf(buf, fmtstr, "lawful deity", buf2); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + end_menu(tmpwin, "Base Attributes"); + (void) select_menu(tmpwin, PICK_NONE, &selected); + destroy_nhwindow(tmpwin); +} + +STATIC_PTR int +doattributes() +{ + minimal_enlightenment(); + if (wizard || discover) + enlightenment(0); + return 0; +} + +/* KMH, #conduct + * (shares enlightenment's tense handling) + */ +STATIC_PTR int +doconduct() +{ + show_conduct(0); + return 0; +} + +void +show_conduct(final) +int final; +{ + char buf[BUFSZ]; + int ngenocided; + + /* Create the conduct window */ + en_win = create_nhwindow(NHW_MENU); + putstr(en_win, 0, "Voluntary challenges:"); + putstr(en_win, 0, ""); + + if (!u.uconduct.food) + enl_msg(You_, "have gone", "went", " without food"); + /* But beverages are okay */ + else if (!u.uconduct.unvegan) + you_have_X("followed a strict vegan diet"); + else if (!u.uconduct.unvegetarian) + you_have_been("vegetarian"); + + if (!u.uconduct.gnostic) + you_have_been("an atheist"); + + if (!u.uconduct.weaphit) + you_have_never("hit with a wielded weapon"); +#ifdef WIZARD + else if (wizard) { + Sprintf(buf, "used a wielded weapon %ld time%s", + u.uconduct.weaphit, plur(u.uconduct.weaphit)); + you_have_X(buf); + } +#endif + if (!u.uconduct.killer) + you_have_been("a pacifist"); + + if (!u.uconduct.literate) + you_have_been("illiterate"); +#ifdef WIZARD + else if (wizard) { + Sprintf(buf, "read items or engraved %ld time%s", + u.uconduct.literate, plur(u.uconduct.literate)); + you_have_X(buf); + } +#endif + + ngenocided = num_genocides(); + if (ngenocided == 0) { + you_have_never("genocided any monsters"); + } else { + Sprintf(buf, "genocided %d type%s of monster%s", + ngenocided, plur(ngenocided), plur(ngenocided)); + you_have_X(buf); + } + + if (!u.uconduct.polypiles) + you_have_never("polymorphed an object"); +#ifdef WIZARD + else if (wizard) { + Sprintf(buf, "polymorphed %ld item%s", + u.uconduct.polypiles, plur(u.uconduct.polypiles)); + you_have_X(buf); + } +#endif + + if (!u.uconduct.polyselfs) + you_have_never("changed form"); +#ifdef WIZARD + else if (wizard) { + Sprintf(buf, "changed form %ld time%s", + u.uconduct.polyselfs, plur(u.uconduct.polyselfs)); + you_have_X(buf); + } +#endif + + if (!u.uconduct.wishes) + you_have_X("used no wishes"); + else { + Sprintf(buf, "used %ld wish%s", + u.uconduct.wishes, (u.uconduct.wishes > 1L) ? "es" : ""); + you_have_X(buf); + + if (!u.uconduct.wisharti) + enl_msg(You_, "have not wished", "did not wish", + " for any artifacts"); + } + + /* Pop up the window and wait for a key */ + display_nhwindow(en_win, TRUE); + destroy_nhwindow(en_win); +} + +#endif /* OVLB */ +#ifdef OVL1 + +#ifndef M +# ifndef NHSTDC +# define M(c) (0x80 | (c)) +# else +# define M(c) ((c) - 128) +# endif /* NHSTDC */ +#endif +#ifndef C +#define C(c) (0x1f & (c)) +#endif + +static const struct func_tab cmdlist[] = { + {C('d'), FALSE, dokick}, /* "D" is for door!...? Msg is in dokick.c */ +#ifdef WIZARD + {C('e'), TRUE, wiz_detect}, + {C('f'), TRUE, wiz_map}, + {C('g'), TRUE, wiz_genesis}, + {C('i'), TRUE, wiz_identify}, +#endif + {C('l'), TRUE, doredraw}, /* if number_pad is set */ +#ifdef WIZARD + {C('o'), TRUE, wiz_where}, +#endif + {C('p'), TRUE, doprev_message}, + {C('r'), TRUE, doredraw}, + {C('t'), TRUE, dotele}, +#ifdef WIZARD + {C('v'), TRUE, wiz_level_tele}, + {C('w'), TRUE, wiz_wish}, +#endif + {C('x'), TRUE, doattributes}, +#ifdef SUSPEND + {C('z'), TRUE, dosuspend}, +#endif + {'a', FALSE, doapply}, + {'A', FALSE, doddoremarm}, + {M('a'), TRUE, doorganize}, +/* 'b', 'B' : go sw */ + {'c', FALSE, doclose}, + {'C', TRUE, do_mname}, + {M('c'), TRUE, dotalk}, + {'d', FALSE, dodrop}, + {'D', FALSE, doddrop}, + {M('d'), FALSE, dodip}, + {'e', FALSE, doeat}, + {'E', FALSE, doengrave}, + {M('e'), TRUE, enhance_weapon_skill}, + {'f', FALSE, dofire}, +/* 'F' : fight (one time) */ + {M('f'), FALSE, doforce}, +/* 'g', 'G' : multiple go */ +/* 'h', 'H' : go west */ + {'h', TRUE, dohelp}, /* if number_pad is set */ + {'i', TRUE, ddoinv}, + {'I', TRUE, dotypeinv}, /* Robert Viduya */ + {M('i'), TRUE, doinvoke}, +/* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */ + {'j', FALSE, dojump}, /* if number_pad is on */ + {M('j'), FALSE, dojump}, + {'k', FALSE, dokick}, /* if number_pad is on */ + {'l', FALSE, doloot}, /* if number_pad is on */ + {M('l'), FALSE, doloot}, +/* 'n' prefixes a count if number_pad is on */ + {M('m'), TRUE, domonability}, + {'N', TRUE, ddocall}, /* if number_pad is on */ + {M('n'), TRUE, ddocall}, + {M('N'), TRUE, ddocall}, + {'o', FALSE, doopen}, + {'O', TRUE, doset}, + {M('o'), FALSE, dosacrifice}, + {'p', FALSE, dopay}, + {'P', FALSE, doputon}, + {M('p'), TRUE, dopray}, + {'q', FALSE, dodrink}, + {'Q', FALSE, dowieldquiver}, + {M('q'), TRUE, done2}, + {'r', FALSE, doread}, + {'R', FALSE, doremring}, + {M('r'), FALSE, dorub}, + {'s', TRUE, dosearch, "searching"}, + {'S', TRUE, dosave}, + {M('s'), FALSE, dosit}, + {'t', FALSE, dothrow}, + {'T', FALSE, dotakeoff}, + {M('t'), TRUE, doturn}, +/* 'u', 'U' : go ne */ + {'u', FALSE, dountrap}, /* if number_pad is on */ + {M('u'), FALSE, dountrap}, + {'v', TRUE, doversion}, + {'V', TRUE, dohistory}, + {M('v'), TRUE, doextversion}, + {'w', FALSE, dowield}, + {'W', FALSE, dowear}, + {M('w'), FALSE, dowipe}, + {'x', FALSE, doswapweapon}, + {'X', TRUE, enter_explore_mode}, +/* 'y', 'Y' : go nw */ + {'z', FALSE, dozap}, + {'Z', TRUE, docast}, + {'<', FALSE, doup}, + {'>', FALSE, dodown}, + {'/', TRUE, dowhatis}, + {'&', TRUE, dowhatdoes}, + {'?', TRUE, dohelp}, + {M('?'), TRUE, doextlist}, +#ifdef SHELL + {'!', TRUE, dosh}, +#endif + {'.', TRUE, donull, "waiting"}, + {' ', TRUE, donull, "waiting"}, + {',', FALSE, dopickup}, + {':', TRUE, dolook}, + {';', TRUE, doquickwhatis}, + {'^', TRUE, doidtrap}, + {'\\', TRUE, dodiscovered}, /* Robert Viduya */ + {'@', TRUE, dotogglepickup}, + {M('2'), FALSE, dotwoweapon}, + {WEAPON_SYM, TRUE, doprwep}, + {ARMOR_SYM, TRUE, doprarm}, + {RING_SYM, TRUE, doprring}, + {AMULET_SYM, TRUE, dopramulet}, + {TOOL_SYM, TRUE, doprtool}, + {'*', TRUE, doprinuse}, /* inventory of all equipment in use */ + {GOLD_SYM, TRUE, doprgold}, + {SPBOOK_SYM, TRUE, dovspell}, /* Mike Stephenson */ + {'#', TRUE, doextcmd}, + {'_', TRUE, dotravel}, + {0,0,0,0} +}; + +struct ext_func_tab extcmdlist[] = { + {"adjust", "adjust inventory letters", doorganize, TRUE}, + {"chat", "talk to someone", dotalk, TRUE}, /* converse? */ + {"conduct", "list which challenges you have adhered to", doconduct, TRUE}, + {"dip", "dip an object into something", dodip, FALSE}, + {"enhance", "advance or check weapons skills", enhance_weapon_skill, + TRUE}, + {"force", "force a lock", doforce, FALSE}, + {"invoke", "invoke an object's powers", doinvoke, TRUE}, + {"jump", "jump to a location", dojump, FALSE}, + {"loot", "loot a box on the floor", doloot, FALSE}, + {"monster", "use a monster's special ability", domonability, TRUE}, + {"name", "name an item or type of object", ddocall, TRUE}, + {"offer", "offer a sacrifice to the gods", dosacrifice, FALSE}, + {"pray", "pray to the gods for help", dopray, TRUE}, + {"quit", "exit without saving current game", done2, TRUE}, +#ifdef STEED + {"ride", "ride (or stop riding) a monster", doride, FALSE}, +#endif + {"rub", "rub a lamp", dorub, FALSE}, + {"sit", "sit down", dosit, FALSE}, + {"turn", "turn undead", doturn, TRUE}, + {"twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE}, + {"untrap", "untrap something", dountrap, FALSE}, + {"version", "list compile time options for this version of NetHack", + doextversion, TRUE}, + {"wipe", "wipe off your face", dowipe, FALSE}, + {"?", "get this list of extended commands", doextlist, TRUE}, +#if defined(WIZARD) + /* + * There must be a blank entry here for every entry in the table + * below. + */ + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, +#ifdef DEBUG + {(char *)0, (char *)0, donull, TRUE}, +#endif + {(char *)0, (char *)0, donull, TRUE}, +#endif + {(char *)0, (char *)0, donull, TRUE} /* sentinel */ +}; + +#if defined(WIZARD) +static const struct ext_func_tab debug_extcmdlist[] = { + {"light sources", "show mobile light sources", wiz_light_sources, TRUE}, + {"monpoly_control", "control monster polymorphs", wiz_mon_polycontrol, TRUE}, + {"seenv", "show seen vectors", wiz_show_seenv, TRUE}, + {"stats", "show memory statistics", wiz_show_stats, TRUE}, + {"timeout", "look at timeout queue", wiz_timeout_queue, TRUE}, + {"vision", "show vision array", wiz_show_vision, TRUE}, +#ifdef DEBUG + {"wizdebug", "wizard debug command", wiz_debug_cmd, TRUE}, +#endif + {"wmode", "show wall modes", wiz_show_wmodes, TRUE}, + {(char *)0, (char *)0, donull, TRUE} +}; + +/* + * Insert debug commands into the extended command list. This function + * assumes that the last entry will be the help entry. + * + * You must add entries in ext_func_tab every time you add one to the + * debug_extcmdlist(). + */ +void +add_debug_extended_commands() +{ + int i, j, k, n; + + /* count the # of help entries */ + for (n = 0; extcmdlist[n].ef_txt[0] != '?'; n++) + ; + + for (i = 0; debug_extcmdlist[i].ef_txt; i++) { + for (j = 0; j < n; j++) + if (strcmp(debug_extcmdlist[i].ef_txt, extcmdlist[j].ef_txt) < 0) break; + + /* insert i'th debug entry into extcmdlist[j], pushing down */ + for (k = n; k >= j; --k) + extcmdlist[k+1] = extcmdlist[k]; + extcmdlist[j] = debug_extcmdlist[i]; + n++; /* now an extra entry */ + } +} + + +static const char *template = "%-18s %4ld %6ld"; +static const char *count_str = " count bytes"; +static const char *separator = "------------------ ----- ------"; + +STATIC_OVL void +count_obj(chain, total_count, total_size, top, recurse) + struct obj *chain; + long *total_count; + long *total_size; + boolean top; + boolean recurse; +{ + long count, size; + struct obj *obj; + + for (count = size = 0, obj = chain; obj; obj = obj->nobj) { + if (top) { + count++; + size += sizeof(struct obj) + obj->oxlth + obj->onamelth; + } + if (recurse && obj->cobj) + count_obj(obj->cobj, total_count, total_size, TRUE, TRUE); + } + *total_count += count; + *total_size += size; +} + +STATIC_OVL void +obj_chain(win, src, chain, total_count, total_size) + winid win; + const char *src; + struct obj *chain; + long *total_count; + long *total_size; +{ + char buf[BUFSZ]; + long count = 0, size = 0; + + count_obj(chain, &count, &size, TRUE, FALSE); + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); +} + +STATIC_OVL void +mon_invent_chain(win, src, chain, total_count, total_size) + winid win; + const char *src; + struct monst *chain; + long *total_count; + long *total_size; +{ + char buf[BUFSZ]; + long count = 0, size = 0; + struct monst *mon; + + for (mon = chain; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, TRUE, FALSE); + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); +} + +STATIC_OVL void +contained(win, src, total_count, total_size) + winid win; + const char *src; + long *total_count; + long *total_size; +{ + char buf[BUFSZ]; + long count = 0, size = 0; + struct monst *mon; + + count_obj(invent, &count, &size, FALSE, TRUE); + count_obj(fobj, &count, &size, FALSE, TRUE); + count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE); + count_obj(migrating_objs, &count, &size, FALSE, TRUE); + /* DEADMONSTER check not required in this loop since they have no inventory */ + for (mon = fmon; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, FALSE, TRUE); + for (mon = migrating_mons; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, FALSE, TRUE); + + *total_count += count; *total_size += size; + + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); +} + +STATIC_OVL void +mon_chain(win, src, chain, total_count, total_size) + winid win; + const char *src; + struct monst *chain; + long *total_count; + long *total_size; +{ + char buf[BUFSZ]; + long count, size; + struct monst *mon; + + for (count = size = 0, mon = chain; mon; mon = mon->nmon) { + count++; + size += sizeof(struct monst) + mon->mxlth + mon->mnamelth; + } + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); +} + +/* + * Display memory usage of all monsters and objects on the level. + */ +static int +wiz_show_stats() +{ + char buf[BUFSZ]; + winid win; + long total_obj_size = 0, total_obj_count = 0; + long total_mon_size = 0, total_mon_count = 0; + + win = create_nhwindow(NHW_TEXT); + putstr(win, 0, "Current memory statistics:"); + putstr(win, 0, ""); + Sprintf(buf, "Objects, size %d", (int) sizeof(struct obj)); + putstr(win, 0, buf); + putstr(win, 0, ""); + putstr(win, 0, count_str); + + obj_chain(win, "invent", invent, &total_obj_count, &total_obj_size); + obj_chain(win, "fobj", fobj, &total_obj_count, &total_obj_size); + obj_chain(win, "buried", level.buriedobjlist, + &total_obj_count, &total_obj_size); + obj_chain(win, "migrating obj", migrating_objs, + &total_obj_count, &total_obj_size); + mon_invent_chain(win, "minvent", fmon, + &total_obj_count,&total_obj_size); + mon_invent_chain(win, "migrating minvent", migrating_mons, + &total_obj_count, &total_obj_size); + + contained(win, "contained", + &total_obj_count, &total_obj_size); + + putstr(win, 0, separator); + Sprintf(buf, template, "Total", total_obj_count, total_obj_size); + putstr(win, 0, buf); + + putstr(win, 0, ""); + putstr(win, 0, ""); + Sprintf(buf, "Monsters, size %d", (int) sizeof(struct monst)); + putstr(win, 0, buf); + putstr(win, 0, ""); + + mon_chain(win, "fmon", fmon, + &total_mon_count, &total_mon_size); + mon_chain(win, "migrating", migrating_mons, + &total_mon_count, &total_mon_size); + + putstr(win, 0, separator); + Sprintf(buf, template, "Total", total_mon_count, total_mon_size); + putstr(win, 0, buf); + +#if defined(__BORLANDC__) && !defined(_WIN32) + show_borlandc_stats(win); +#endif + + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return 0; +} + +void +sanity_check() +{ + obj_sanity_check(); + timer_sanity_check(); +} + +#endif /* WIZARD */ + +#define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c)) +#define unmeta(c) (0x7f & (c)) + + +void +rhack(cmd) +register char *cmd; +{ + boolean do_walk, do_rush, prefix_seen, bad_command, + firsttime = (cmd == 0); + + if (firsttime) { + flags.nopick = 0; + cmd = parse(); + } + if (*cmd == '\033') { + flags.move = FALSE; + return; + } +#ifdef REDO + if (*cmd == DOAGAIN && !in_doagain && saveq[0]) { + in_doagain = TRUE; + stail = 0; + rhack((char *)0); /* read and execute command */ + in_doagain = FALSE; + return; + } + /* Special case of *cmd == ' ' handled better below */ + if(!*cmd || *cmd == (char)0377) +#else + if(!*cmd || *cmd == (char)0377 || (!flags.rest_on_space && *cmd == ' ')) +#endif + { + nhbell(); + flags.move = FALSE; + return; /* probably we just had an interrupt */ + } + + /* handle most movement commands */ + do_walk = do_rush = prefix_seen = FALSE; + flags.travel = 0; + switch (*cmd) { + case 'g': if (movecmd(cmd[1])) { + flags.run = 2; + do_rush = TRUE; + } else + prefix_seen = TRUE; + break; + case '5': if (!iflags.num_pad) break; /* else FALLTHRU */ + case 'G': if (movecmd(lowc(cmd[1]))) { + flags.run = 3; + do_rush = TRUE; + } else + prefix_seen = TRUE; + break; + case '-': if (!iflags.num_pad) break; /* else FALLTHRU */ + /* Effects of movement commands and invisible monsters: + * m: always move onto space (even if 'I' remembered) + * F: always attack space (even if 'I' not remembered) + * normal movement: attack if 'I', move otherwise + */ + case 'F': if (movecmd(cmd[1])) { + flags.forcefight = 1; + do_walk = TRUE; + } else + prefix_seen = TRUE; + break; + case 'm': if (movecmd(cmd[1]) || u.dz) { + flags.run = 0; + flags.nopick = 1; + if (!u.dz) do_walk = TRUE; + else cmd[0] = cmd[1]; /* "m<" or "m>" */ + } else + prefix_seen = TRUE; + break; + case 'M': if (movecmd(lowc(cmd[1]))) { + flags.run = 1; + flags.nopick = 1; + do_rush = TRUE; + } else + prefix_seen = TRUE; + break; + case '0': if (!iflags.num_pad) break; + (void)ddoinv(); /* a convenience borrowed from the PC */ + flags.move = FALSE; + multi = 0; + return; + case CMD_TRAVEL: + flags.travel = 1; + flags.run = 8; + flags.nopick = 1; + do_rush = TRUE; + break; + default: if (movecmd(*cmd)) { /* ordinary movement */ + do_walk = TRUE; + } else if (movecmd(iflags.num_pad ? + unmeta(*cmd) : lowc(*cmd))) { + flags.run = 1; + do_rush = TRUE; + } else if (movecmd(unctrl(*cmd))) { + flags.run = 3; + do_rush = TRUE; + } + break; + } + if (do_walk) { + if (multi) flags.mv = TRUE; + domove(); + flags.forcefight = 0; + return; + } else if (do_rush) { + if (firsttime) { + if (!multi) multi = max(COLNO,ROWNO); + u.last_str_turn = 0; + } + flags.mv = TRUE; + domove(); + return; + } else if (prefix_seen && cmd[1] == '\033') { /* */ + /* don't report "unknown command" for change of heart... */ + bad_command = FALSE; + } else if (*cmd == ' ' && !flags.rest_on_space) { + bad_command = TRUE; /* skip cmdlist[] loop */ + + /* handle all other commands */ + } else { + register const struct func_tab *tlist; + int res, NDECL((*func)); + + for (tlist = cmdlist; tlist->f_char; tlist++) { + if ((*cmd & 0xff) != (tlist->f_char & 0xff)) continue; + + if (u.uburied && !tlist->can_if_buried) { + You_cant("do that while you are buried!"); + res = 0; + } else { + /* we discard 'const' because some compilers seem to have + trouble with the pointer passed to set_occupation() */ + func = ((struct func_tab *)tlist)->f_funct; + if (tlist->f_text && !occupation && multi) + set_occupation(func, tlist->f_text, multi); + res = (*func)(); /* perform the command */ + } + if (!res) { + flags.move = FALSE; + multi = 0; + } + return; + } + /* if we reach here, cmd wasn't found in cmdlist[] */ + bad_command = TRUE; + } + + if (bad_command) { + char expcmd[10]; + register char *cp = expcmd; + + while (*cmd && (int)(cp - expcmd) < (int)(sizeof expcmd - 3)) { + if (*cmd >= 040 && *cmd < 0177) { + *cp++ = *cmd++; + } else if (*cmd & 0200) { + *cp++ = 'M'; + *cp++ = '-'; + *cp++ = *cmd++ &= ~0200; + } else { + *cp++ = '^'; + *cp++ = *cmd++ ^ 0100; + } + } + *cp = '\0'; + Norep("Unknown command '%s'.", expcmd); + } + /* didn't move */ + flags.move = FALSE; + multi = 0; + return; +} + +int +xytod(x, y) /* convert an x,y pair into a direction code */ +schar x, y; +{ + register int dd; + + for(dd = 0; dd < 8; dd++) + if(x == xdir[dd] && y == ydir[dd]) return dd; + + return -1; +} + +void +dtoxy(cc,dd) /* convert a direction code into an x,y pair */ +coord *cc; +register int dd; +{ + cc->x = xdir[dd]; + cc->y = ydir[dd]; + return; +} + +int +movecmd(sym) /* also sets u.dz, but returns false for <> */ +char sym; +{ + register const char *dp; + register const char *sdp; + if(iflags.num_pad) sdp = ndir; else sdp = sdir; /* DICE workaround */ + + u.dz = 0; + if(!(dp = index(sdp, sym))) return 0; + u.dx = xdir[dp-sdp]; + u.dy = ydir[dp-sdp]; + u.dz = zdir[dp-sdp]; + if (u.dx && u.dy && u.umonnum == PM_GRID_BUG) { + u.dx = u.dy = 0; + return 0; + } + return !u.dz; +} + +int +getdir(s) +const char *s; +{ + char dirsym; + +#ifdef REDO + if(in_doagain || *readchar_queue) + dirsym = readchar(); + else +#endif + dirsym = yn_function (s ? s : "In what direction?", + (char *)0, '\0'); +#ifdef REDO + savech(dirsym); +#endif + if(dirsym == '.' || dirsym == 's') + u.dx = u.dy = u.dz = 0; + else if(!movecmd(dirsym) && !u.dz) { + if(!index(quitchars, dirsym)) + pline("What a strange direction!"); + return 0; + } + if(!u.dz && (Stunned || (Confusion && !rn2(5)))) confdir(); + return 1; +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +confdir() +{ + register int x = (u.umonnum == PM_GRID_BUG) ? 2*rn2(4) : rn2(8); + u.dx = xdir[x]; + u.dy = ydir[x]; + return; +} + +#endif /* OVLB */ +#ifdef OVL0 + +int +isok(x,y) +register int x, y; +{ + /* x corresponds to curx, so x==1 is the first column. Ach. %% */ + return x >= 1 && x <= COLNO-1 && y >= 0 && y <= ROWNO-1; +} + +static NEARDATA int last_multi; + +/* + * convert a MAP window position into a movecmd + */ +const char* +click_to_cmd(x, y, mod) + int x, y, mod; +{ + int dir; + static char cmd[4]; + cmd[1]=0; + + x -= u.ux; + y -= u.uy; + + if ( abs(x) <= 1 && abs(y) <= 1 ) { + x = sgn(x), y = sgn(y); + } else { + u.tx = u.ux+x; + u.ty = u.uy+y; + cmd[0] = CMD_TRAVEL; + return cmd; + } + + if(x == 0 && y == 0) { + /* here */ + if(IS_FOUNTAIN(levl[u.ux][u.uy].typ) || IS_SINK(levl[u.ux][u.uy].typ)) { + cmd[0]=mod == CLICK_1 ? 'q' : M('d'); + return cmd; + } else if(IS_THRONE(levl[u.ux][u.uy].typ)) { + cmd[0]=M('s'); + return cmd; + } else if((u.ux == xupstair && u.uy == yupstair) + || (u.ux == sstairs.sx && u.uy == sstairs.sy && sstairs.up) + || (u.ux == xupladder && u.uy == yupladder)) { + return "<"; + } else if((u.ux == xdnstair && u.uy == ydnstair) + || (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up) + || (u.ux == xdnladder && u.uy == ydnladder)) { + return ">"; + } else if(OBJ_AT(u.ux, u.uy)) { + cmd[0] = Is_container(level.objects[u.ux][u.uy]) ? M('l') : ','; + return cmd; + } else { + return "."; /* just rest */ + } + } + + /* directional commands */ + + dir = xytod(x, y); + + if (!m_at(u.ux+x, u.uy+y) && !test_move(u.ux, u.uy, x, y, 1)) { + cmd[1] = (iflags.num_pad ? ndir[dir] : sdir[dir]); + cmd[2] = 0; + if (IS_DOOR(levl[u.ux+x][u.uy+y].typ)) { + /* slight assistance to the player: choose kick/open for them */ + if (levl[u.ux+x][u.uy+y].doormask & D_LOCKED) { + cmd[0] = C('d'); + return cmd; + } + if (levl[u.ux+x][u.uy+y].doormask & D_CLOSED) { + cmd[0] = 'o'; + return cmd; + } + } + if (levl[u.ux+x][u.uy+y].typ <= SCORR) { + cmd[0] = 's'; + cmd[1] = 0; + return cmd; + } + } + + /* move, attack, etc. */ + cmd[1] = 0; + if(mod == CLICK_1) { + cmd[0] = (iflags.num_pad ? ndir[dir] : sdir[dir]); + } else { + cmd[0] = (iflags.num_pad ? M(ndir[dir]) : + (sdir[dir] - 'a' + 'A')); /* run command */ + } + + return cmd; +} + +STATIC_OVL char * +parse() +{ +#ifdef LINT /* static char in_line[COLNO]; */ + char in_line[COLNO]; +#else + static char in_line[COLNO]; +#endif + register int foo; + boolean prezero = FALSE; + + multi = 0; + flags.move = 1; + flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */ + + if (!iflags.num_pad || (foo = readchar()) == 'n') + for (;;) { + foo = readchar(); + if (foo >= '0' && foo <= '9') { + multi = 10 * multi + foo - '0'; + if (multi < 0 || multi >= LARGEST_INT) multi = LARGEST_INT; + if (multi > 9) { + clear_nhwindow(WIN_MESSAGE); + Sprintf(in_line, "Count: %d", multi); + pline(in_line); + mark_synch(); + } + last_multi = multi; + if (!multi && foo == '0') prezero = TRUE; + } else break; /* not a digit */ + } + + if (foo == '\033') { /* esc cancels count (TH) */ + clear_nhwindow(WIN_MESSAGE); + multi = last_multi = 0; +# ifdef REDO + } else if (foo == DOAGAIN || in_doagain) { + multi = last_multi; + } else { + last_multi = multi; + savech(0); /* reset input queue */ + savech((char)foo); +# endif + } + + if (multi) { + multi--; + save_cm = in_line; + } else { + save_cm = (char *)0; + } + in_line[0] = foo; + in_line[1] = '\0'; + if (foo == 'g' || foo == 'G' || foo == 'm' || foo == 'M' || + foo == 'F' || (iflags.num_pad && (foo == '5' || foo == '-'))) { + foo = readchar(); +#ifdef REDO + savech((char)foo); +#endif + in_line[1] = foo; + in_line[2] = 0; + } + clear_nhwindow(WIN_MESSAGE); + if (prezero) in_line[0] = '\033'; + return(in_line); +} + +#endif /* OVL0 */ +#ifdef OVLB + +#ifdef UNIX +static +void +end_of_input() +{ + exit_nhwindows("End of input?"); +#ifndef NOSAVEONHANGUP + if (!program_state.done_hup++) + (void) dosave0(); +#endif + clearlocks(); + terminate(EXIT_SUCCESS); +} +#endif + +#endif /* OVLB */ +#ifdef OVL0 + +char +readchar() +{ + register int sym; + int x = u.ux, y = u.uy, mod = 0; + + if ( *readchar_queue ) + sym = *readchar_queue++; + else +#ifdef REDO + sym = in_doagain ? Getchar() : nh_poskey(&x, &y, &mod); +#else + sym = Getchar(); +#endif + +#ifdef UNIX +# ifdef NR_OF_EOFS + if (sym == EOF) { + register int cnt = NR_OF_EOFS; + /* + * Some SYSV systems seem to return EOFs for various reasons + * (?like when one hits break or for interrupted systemcalls?), + * and we must see several before we quit. + */ + do { + clearerr(stdin); /* omit if clearerr is undefined */ + sym = Getchar(); + } while (--cnt && sym == EOF); + } +# endif /* NR_OF_EOFS */ + if (sym == EOF) + end_of_input(); +#endif /* UNIX */ + + if(sym == 0) { + /* click event */ + readchar_queue = click_to_cmd(x, y, mod); + sym = *readchar_queue++; + } + return((char) sym); +} + +STATIC_PTR int +dotravel() +{ + /* Keyboard travel command */ + static char cmd[2]; + coord cc; + cmd[1]=0; + cc.x = u.ux; + cc.y = u.uy; + pline("Where do you want to travel to?"); + if (getpos(&cc, TRUE, "the desired destination") < 0) { + /* user pressed ESC */ + return 0; + } + u.tx = cc.x; + u.ty = cc.y; + cmd[0] = CMD_TRAVEL; + readchar_queue = cmd; + return 0; +} + +#endif /* OVL0 */ + +/*cmd.c*/