/* SCCS Id: @(#)cmd.c 3.5 2006/07/08 */ /* 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 */ struct cmd Cmd = { 0 }; /* flag.h */ /* * 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 #define CMD_CLICKLOOK (char)0x8F #define NODIAG(monnum) ((monnum) == PM_GRID_BUG) #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 int 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(dotip); /**/ 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 */ static int NDECL((*timed_occ_fn)); STATIC_PTR int NDECL(doprev_message); STATIC_PTR int NDECL(timed_occupation); STATIC_PTR int NDECL(doextcmd); STATIC_PTR int NDECL(domonability); #ifdef DUNGEON_OVERVIEW STATIC_PTR int NDECL(dooverview_or_wiz_where); #endif /* DUNGEON_OVERVIEW */ 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_panic); STATIC_PTR int NDECL(wiz_polyself); STATIC_PTR int NDECL(wiz_level_tele); STATIC_PTR int NDECL(wiz_level_change); STATIC_PTR int NDECL(wiz_show_seenv); STATIC_PTR int NDECL(wiz_show_vision); STATIC_PTR int NDECL(wiz_smell); STATIC_PTR int NDECL(wiz_mon_polycontrol); STATIC_PTR int NDECL(wiz_show_wmodes); STATIC_PTR int NDECL(wiz_map_terrain); #if defined(__BORLANDC__) && !defined(_WIN32) extern void FDECL(show_borlandc_stats, (winid)); #endif #ifdef DEBUG_MIGRATING_MONS STATIC_PTR int NDECL(wiz_migrate_mons); #endif STATIC_DCL int FDECL(size_monst, (struct monst *)); STATIC_DCL int FDECL(size_obj, (struct obj *)); 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); # ifdef PORT_DEBUG STATIC_DCL int NDECL(wiz_port_debug); # endif STATIC_PTR int NDECL(wiz_rumor_check); STATIC_DCL char FDECL(cmd_from_func, (int NDECL((*)))); # endif /* WIZARD */ STATIC_PTR int NDECL(enter_explore_mode); STATIC_PTR int NDECL(doattributes); STATIC_PTR int NDECL(doconduct); /**/ STATIC_PTR boolean NDECL(minimal_enlightenment); STATIC_DCL void FDECL(enlght_line, (const char *,const char *,const char *,char *)); STATIC_DCL char *FDECL(enlght_combatinc, (const char *,int,int,char *)); #if defined(UNIX) || defined(SAFERHANGUP) static void NDECL(end_of_input); #endif static const char* readchar_queue=""; static coord clicklook_cc; STATIC_DCL char *NDECL(parse); STATIC_DCL boolean FDECL(help_dir, (CHAR_P,const char *)); STATIC_PTR int doprev_message(VOID_ARGS) { return nh_doprev_message(); } /* Count down by decrementing multi */ STATIC_PTR int timed_occupation(VOID_ARGS) { (*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_DCL 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_OVL 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 */ STATIC_PTR int doextcmd(VOID_ARGS) /* 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(VOID_ARGS) /* 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, " %-15s - %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 = zeroany; /* 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 /* #monster command - use special monster ability while polymorphed */ STATIC_PTR int domonability(VOID_ARGS) { 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 (youmonst.data->mlet == S_VAMPIRE) return dopoly(); 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(VOID_ARGS) { 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 DUNGEON_OVERVIEW STATIC_PTR int dooverview_or_wiz_where(VOID_ARGS) { #ifdef WIZARD if (wizard) return wiz_where(); else #endif dooverview(); return 0; } #endif /* DUNGEON_OVERVIEW */ #ifdef WIZARD /* ^W command - wish for something */ STATIC_PTR int wiz_wish(VOID_ARGS) /* 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 '%s'.", visctrl((int)cmd_from_func(wiz_wish))); return 0; } /* ^I command - reveal and optionally identify hero's inventory */ STATIC_PTR int wiz_identify(VOID_ARGS) { if (wizard) { iflags.override_ID = (int)cmd_from_func(wiz_identify); if (display_inventory((char *)0, TRUE) == -1) identify_pack(0, FALSE); iflags.override_ID = 0; } else pline("Unavailable command '%s'.", visctrl((int)cmd_from_func(wiz_identify))); return 0; } /* ^F command - reveal the level map and any traps on it */ STATIC_PTR int wiz_map(VOID_ARGS) { if (wizard) { struct trap *t; long save_Hconf = HConfusion, save_Hhallu = HHallucination; HConfusion = HHallucination = 0L; for (t = ftrap; t != 0; t = t->ntrap) { t->tseen = 1; map_trap(t, TRUE); } do_mapping(); HConfusion = save_Hconf; HHallucination = save_Hhallu; } else pline("Unavailable command '%s'.", visctrl((int)cmd_from_func(wiz_map))); return 0; } /* ^G command - generate monster(s); a count prefix will be honored */ STATIC_PTR int wiz_genesis(VOID_ARGS) { if (wizard) (void) create_particular(); else pline("Unavailable command '%s'.", visctrl((int)cmd_from_func(wiz_genesis))); return 0; } /* ^O command - display dungeon layout */ STATIC_PTR int wiz_where(VOID_ARGS) { if (wizard) (void) print_dungeon(FALSE, (schar *)0, (xchar *)0); else pline("Unavailable command '%s'.", visctrl((int)cmd_from_func(wiz_where))); return 0; } /* ^E command - detect unseen (secret doors, traps, hidden monsters) */ STATIC_PTR int wiz_detect(VOID_ARGS) { if(wizard) (void) findit(); else pline("Unavailable command '%s'.", visctrl((int)cmd_from_func(wiz_detect))); return 0; } /* ^V command - level teleport */ STATIC_PTR int wiz_level_tele(VOID_ARGS) { if (wizard) level_tele(); else pline("Unavailable command '%s'.", visctrl((int)cmd_from_func(wiz_level_tele))); return 0; } /* #monpolycontrol command - choose new form for shapechangers, polymorphees */ STATIC_PTR int wiz_mon_polycontrol(VOID_ARGS) { iflags.mon_polycontrol = !iflags.mon_polycontrol; pline("Monster polymorph control is %s.", iflags.mon_polycontrol ? "on" : "off"); return 0; } /* #levelchange command - adjust hero's experience level */ STATIC_PTR int wiz_level_change(VOID_ARGS) { char buf[BUFSZ]; int newlevel; int ret; getlin("To what experience level do you want to be set?", buf); (void)mungspaces(buf); if (buf[0] == '\033' || buf[0] == '\0') ret = 0; else ret = sscanf(buf, "%d", &newlevel); if (ret != 1) { pline(Never_mind); return 0; } if (newlevel == u.ulevel) { You("are already that experienced."); } else if (newlevel < u.ulevel) { if (u.ulevel == 1) { You("are already as inexperienced as you can get."); return 0; } if (newlevel < 1) newlevel = 1; while (u.ulevel > newlevel) losexp("#levelchange"); } else { if (u.ulevel >= MAXULEV) { You("are already as experienced as you can get."); return 0; } if (newlevel > MAXULEV) newlevel = MAXULEV; while (u.ulevel < newlevel) pluslvl(FALSE); } u.ulevelmax = u.ulevel; return 0; } /* #panic command - test program's panic handling */ STATIC_PTR int wiz_panic(VOID_ARGS) { if (yn("Do you want to call panic() and end your game?") == 'y') panic("crash test."); return 0; } /* #polyself command - change hero's form */ STATIC_PTR int wiz_polyself(VOID_ARGS) { polyself(1); return 0; } /* #seenv command */ STATIC_PTR int wiz_show_seenv(VOID_ARGS) { 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; } /* #vision command */ STATIC_PTR int wiz_show_vision(VOID_ARGS) { 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; } /* #wmode command */ STATIC_PTR int wiz_show_wmodes(VOID_ARGS) { winid win; int x,y; char row[COLNO+1]; struct rm *lev; boolean istty = !strcmp(windowprocs.name, "tty"); win = create_nhwindow(NHW_TEXT); if (istty) putstr(win, 0, ""); /* tty only: blank top line */ 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'; /* map column 0, levl[0][], is off the left edge of the screen */ putstr(win, 0, &row[1]); } display_nhwindow(win, TRUE); destroy_nhwindow(win); return 0; } /* #terrain command */ STATIC_PTR int wiz_map_terrain(VOID_ARGS) { winid win; int x, y, terrain; char row[COLNO+1]; boolean istty = !strcmp(windowprocs.name, "tty"); win = create_nhwindow(NHW_TEXT); /* map row 0, levl[][0], is drawn on the second line of tty screen */ if (istty) putstr(win, 0, ""); /* tty only: blank top line */ for (y = 0; y < ROWNO; y++) { /* map column 0, levl[0][], is off the left edge of the screen; it should always have terrain type "undiggable stone" */ for (x = 1; x < COLNO; x++) { terrain = levl[x][y].typ; /* assumes there aren't more than 10+26+26 terrain types */ row[x-1] = (char)((terrain == 0 && !may_dig(x, y)) ? '*' : (terrain < 10) ? '0' + terrain : (terrain < 36) ? 'a' + terrain - 10 : 'A' + terrain - 36); } if (levl[0][y].typ != 0 || may_dig(0, y)) row[x++] = '!'; row[x] = '\0'; putstr(win, 0, row); } { char dsc[BUFSZ]; s_level *slev = Is_special(&u.uz); Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel); /* [dungeon branch features currently omitted] */ /* special level features */ if (slev) { Sprintf(eos(dsc), " \"%s\"", slev->proto); /* special level flags (note: dungeon.def doesn't set `maze' or `hell' for any specific levels so those never show up) */ if (slev->flags.maze_like) Strcat(dsc, " mazelike"); if (slev->flags.hellish) Strcat(dsc, " hellish"); if (slev->flags.town) Strcat(dsc, " town"); if (slev->flags.rogue_like) Strcat(dsc, " roguelike"); /* alignment currently omitted to save space */ } /* level features */ if (level.flags.nfountains) Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym, (int)level.flags.nfountains); #ifdef SINKS if (level.flags.nsinks) Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym, (int)level.flags.nsinks); #endif if (level.flags.has_vault) Strcat(dsc, " vault"); if (level.flags.has_shop) Strcat(dsc, " shop"); if (level.flags.has_temple) Strcat(dsc, " temple"); if (level.flags.has_court) Strcat(dsc, " throne"); if (level.flags.has_zoo) Strcat(dsc, " zoo"); if (level.flags.has_morgue) Strcat(dsc, " morgue"); if (level.flags.has_barracks) Strcat(dsc, " barracks"); if (level.flags.has_beehive) Strcat(dsc, " hive"); if (level.flags.has_swamp) Strcat(dsc, " swamp"); /* level flags */ if (level.flags.noteleport) Strcat(dsc, " noTport"); if (level.flags.hardfloor) Strcat(dsc, " noDig"); if (level.flags.nommap) Strcat(dsc, " noMMap"); if (!level.flags.hero_memory) Strcat(dsc, " noMem"); if (level.flags.shortsighted) Strcat(dsc, " shortsight"); if (level.flags.graveyard) Strcat(dsc, " graveyard"); if (level.flags.is_maze_lev) Strcat(dsc, " maze"); if (level.flags.is_cavernous_lev) Strcat(dsc, " cave"); if (level.flags.arboreal) Strcat(dsc, " tree"); /* non-flag info; probably should include dungeon branching checks (extra stairs and magic portals) here */ if (Invocation_lev(&u.uz)) Strcat(dsc, " invoke"); if (On_W_tower_level(&u.uz)) Strcat(dsc, " tower"); /* append a branch identifier for completeness' sake */ if (u.uz.dnum == 0) Strcat(dsc, " dungeon"); else if (u.uz.dnum == mines_dnum) Strcat(dsc, " mines"); else if (In_sokoban(&u.uz)) Strcat(dsc, " sokoban"); else if (u.uz.dnum == quest_dnum) Strcat(dsc, " quest"); else if (Is_knox(&u.uz)) Strcat(dsc, " ludios"); else if (u.uz.dnum == 1) Strcat(dsc, " gehennom"); else if (u.uz.dnum == tower_dnum) Strcat(dsc, " vlad"); else if (In_endgame(&u.uz)) Strcat(dsc, " endgame"); else { /* somebody's added a dungeon branch we're not expecting */ const char *brname = dungeons[u.uz.dnum].dname; if (!brname || !*brname) brname = "unknown"; if (!strncmpi(brname, "the ", 4)) brname += 4; Sprintf(eos(dsc), " %s", brname); } /* limit the line length to map width */ if (strlen(dsc) >= COLNO) dsc[COLNO-1] = '\0'; /* truncate */ putstr(win, 0, dsc); } display_nhwindow(win, TRUE); /* TODO? create legend of levl[][].typ codes and allow switching back and forth between it and coded map display */ destroy_nhwindow(win); return 0; } /* #wizsmell command - test usmellmon(). */ STATIC_PTR int wiz_smell(VOID_ARGS) { char out_str[BUFSZ]; struct permonst *pm = 0; int ans = 0; int mndx; /* monster index */ int found; /* count of matching mndxs found */ coord cc; /* screen pos of unknown glyph */ int glyph; /* glyph at selected position */ cc.x = u.ux; cc.y = u.uy; mndx = 0; /* gcc -Wall lint */ if (!olfaction(youmonst.data)) { You("are incapable of detecting odors in your present form."); return 0; } pline("You can move the cursor to a monster that you want to smell."); do { /* Reset some variables. */ pm = (struct permonst *)0; found = 0; out_str[0] = '\0'; pline("Pick a monster to smell."); ans = getpos(&cc, TRUE, "a monster"); if (ans < 0 || cc.x < 0) { return 0; /* done */ } /* Convert the glyph at the selected position to a mndxbol. */ glyph = glyph_at(cc.x,cc.y); if (glyph_is_monster(glyph)) mndx = glyph_to_mon(glyph); else mndx = 0; /* Is it a monster? */ if (mndx) { if (!usmellmon(&mons[mndx])) pline("That monster seems to give off no smell."); } else pline("That is not a monster."); } while (TRUE); return 0; } /* #wizrumorcheck command - verify each rumor access */ STATIC_PTR int wiz_rumor_check(VOID_ARGS) { rumor_check(); 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,ps) \ enlght_line(prefix, final ? past : present, suffix, ps) #define you_are(attr,ps) enl_msg(You_,are,were,attr,ps) #define you_have(attr,ps) enl_msg(You_,have,had,attr,ps) #define you_can(attr,ps) enl_msg(You_,can,could,attr,ps) #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, ps) const char *start, *middle, *end; char *ps; { char buf[BUFSZ]; Sprintf(buf, "%s%s%s%s.", start, middle, end, ps); putstr(en_win, 0, buf); } /* format increased chance to hit or damage or defense (Protection) */ static char * enlght_combatinc(inctyp, incamt, final, outbuf) const char *inctyp; int incamt, final; char *outbuf; { const char *modif, *bonus; boolean invrt; int absamt; absamt = abs(incamt); /* Protection amount is typically larger than damage or to-hit; reduce magnitude by a third in order to stretch modifier ranges (small:1..5, moderate:6..10, large:11..19, huge:20+) */ if (!strcmp(inctyp, "defense")) absamt = (absamt * 2) / 3; if (absamt <= 3) modif = "small"; else if (absamt <= 6) modif = "moderate"; else if (absamt <= 12) modif = "large"; else modif = "huge"; modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */ bonus = (incamt >= 0) ? "bonus" : "penalty"; /* "bonus " (to hit) vs " bonus" (damage, defense) */ invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE; Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus, invrt ? bonus : inctyp); if (final || wizard) Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt); return outbuf; } void enlightenment(final) int final; /* 0 => still in progress; 1 => over, survived; 2 => dead */ { int ltmp, armpro; 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 * const 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",from_what(FIRE_RES)); if (Cold_resistance) you_are("cold resistant",from_what(COLD_RES)); if (Sleep_resistance) you_are("sleep resistant",from_what(SLEEP_RES)); if (Disint_resistance) you_are("disintegration-resistant",from_what(DISINT_RES)); if (Shock_resistance) you_are("shock resistant",from_what(SHOCK_RES)); if (Poison_resistance) you_are("poison resistant",from_what(POISON_RES)); if (Drain_resistance) you_are("level-drain resistant",from_what(DRAIN_RES)); if (Sick_resistance) you_are("immune to sickness",from_what(SICK_RES)); if (Antimagic) you_are("magic-protected",from_what(ANTIMAGIC)); if (Acid_resistance) you_are("acid resistant",from_what(ACID_RES)); if (Stone_resistance) you_are("petrification resistant",from_what(STONE_RES)); if (Invulnerable) you_are("invulnerable",from_what(INVULNERABLE)); if (u.uedibility) you_can("recognize detrimental food",""); /*** Troubles ***/ if (Halluc_resistance) enl_msg("You resist", "", "ed", " hallucinations", from_what(HALLUC_RES)); if (final) { if (Hallucination) you_are("hallucinating",""); if (Stunned) you_are("stunned",""); if (Confusion) you_are("confused",""); if (Blinded) you_are("blinded",from_what(BLINDED)); if (Deaf) you_are("deaf",from_what(DEAF)); 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", "",from_what(FUMBLING)); if (Wounded_legs #ifdef STEED && !u.usteed #endif ) { Sprintf(buf, "wounded %s", makeplural(body_part(LEG))); you_have(buf,""); } #if defined(WIZARD) && defined(STEED) if (Wounded_legs && u.usteed && wizard) { Strcpy(buf, x_monnam(u.usteed, ARTICLE_YOUR, (char *)0, SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION, FALSE)); *buf = highc(*buf); enl_msg(buf, " has", " had", " wounded legs", ""); } #endif 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", from_what(SEE_INVIS)); if (Blind_telepat) you_are("telepathic",from_what(TELEPAT)); if (Warning) you_are("warned", from_what(WARNING)); if (Warn_of_mon && context.warntype.obj) { Sprintf(buf, "aware of the presence of %s", (context.warntype.obj & M2_ORC) ? "orcs" : (context.warntype.obj & M2_DEMON) ? "demons" : something); you_are(buf,from_what(WARN_OF_MON)); } if (Warn_of_mon && context.warntype.polyd) { Sprintf(buf, "aware of the presence of %s", ((context.warntype.polyd & (M2_HUMAN|M2_ELF))==(M2_HUMAN|M2_ELF)) ? "humans and elves" : (context.warntype.polyd & M2_HUMAN) ? "humans" : (context.warntype.polyd & M2_ELF) ? "elves" : (context.warntype.polyd & M2_ORC) ? "orcs" : (context.warntype.polyd & M2_DEMON) ? "demons" : "certain monsters"); you_are(buf,""); } if (Warn_of_mon && context.warntype.speciesidx) { Sprintf(buf, "aware of the presence of %s", makeplural(mons[context.warntype.speciesidx].mname)); you_are(buf,from_what(WARN_OF_MON)); } if (Undead_warning) you_are("warned of undead",from_what(WARN_UNDEAD)); if (Searching) you_have("automatic searching",from_what(SEARCHING)); if (Clairvoyant) you_are("clairvoyant",from_what(CLAIRVOYANT)); if (Infravision) you_have("infravision",from_what(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) { int adorn = 0; if(uleft && uleft->otyp == RIN_ADORNMENT) adorn += uleft->spe; if(uright && uright->otyp == RIN_ADORNMENT) adorn += uright->spe; if (adorn < 0) you_are("poorly adorned",""); else you_are("adorned",""); } if (Invisible) you_are("invisible",from_what(INVIS)); else if (Invis) you_are("invisible to others",from_what(INVIS)); /* 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",from_what(DISPLACED)); if (Stealth) you_are("stealthy",from_what(STEALTH)); if (Aggravate_monster) enl_msg("You aggravate", "", "d", " monsters",from_what(AGGRAVATE_MONSTER)); if (Conflict) enl_msg("You cause", "", "d", " conflict",from_what(CONFLICT)); /*** Transportation ***/ if (Jumping) you_can("jump",from_what(JUMPING)); if (Teleportation) you_can("teleport",from_what(TELEPORT)); if (Teleport_control) you_have("teleport control",from_what(TELEPORT_CONTROL)); if (Lev_at_will) you_are("levitating, at will", ""); else if (Levitation) you_are("levitating",from_what(LEVITATION)); /* without control */ else if (Flying) you_can("fly",from_what(FLYING)); if (Wwalking) you_can("walk on water",from_what(WWALKING)); if (Swimming) you_can("swim",from_what(SWIMMING)); if (Breathless) you_can("survive without air",from_what(MAGICAL_BREATHING)); else if (Amphibious) you_can("breathe water",from_what(MAGICAL_BREATHING)); if (Passes_walls) you_can("walk through walls",from_what(PASSES_WALLS)); #ifdef STEED /* If you die while dismounting, u.usteed is still set. Since several * places in the done() sequence depend on u.usteed, just detect this * special case. */ if (u.usteed && (final < 2 || strcmp(killer.name, "riding accident"))) { 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 (Regeneration) enl_msg("You regenerate", "", "d", "",from_what(REGENERATION)); if (Slow_digestion) you_have("slower digestion",from_what(SLOW_DIGESTION)); if (u.uhitinc) you_have(enlght_combatinc("to hit", u.uhitinc, final, buf),""); if (u.udaminc) you_have(enlght_combatinc("damage", u.udaminc, final, buf),""); if (u.uspellprot || Protection) { int prot = 0; if(uleft && uleft->otyp == RIN_PROTECTION) prot += uleft->spe; if(uright && uright->otyp == RIN_PROTECTION) prot += uright->spe; if (HProtection & INTRINSIC) prot += u.ublessed; prot += u.uspellprot; if (prot) you_have(enlght_combatinc("defense", prot, final, buf), ""); } if ((armpro = magic_negation(&youmonst)) > 0) { /* magic cancellation factor, conferred by worn armor */ static const char * const mc_types[] = { "" /*ordinary*/, "warded", "guarded", "protected", }; /* sanity check */ if (armpro >= SIZE(mc_types)) armpro = SIZE(mc_types) - 1; you_are(mc_types[armpro],""); } if (Protection_from_shape_changers) you_are("protected from shape changers",""); if (Polymorph) you_are("polymorphing",""); if (Polymorph_control) you_have("polymorph control",from_what(POLYMORPH_CONTROL)); if (u.ulycn >= LOW_PM) { Strcpy(buf, an(mons[u.ulycn].mname)); you_are(buf,""); } if (Upolyd) { if (u.umonnum == u.ulycn) 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",from_what(FAST)); if (Reflecting) you_have("reflection",from_what(REFLECTING)); if (Free_action) you_have("free action",from_what(FREE_ACTION)); if (Fixed_abil) you_have("fixed abilities",from_what(FIXED_ABIL)); if (Lifesaved) enl_msg("Your life ", "will be", "would have been", " saved",""); if (u.twoweap) you_are("wielding two weapons at once",""); if (u.utraptype == TT_BURIEDBALL) you_are("fastened to a buried ball",""); /*** 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(0); 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. * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise. */ STATIC_OVL boolean minimal_enlightenment() { winid tmpwin; menu_item *selected; anything any; int genidx, n; char buf[BUFSZ], buf2[BUFSZ]; static const char untabbed_fmtstr[] = "%-15s: %-12s"; static const char untabbed_deity_fmtstr[] = "%-17s%s"; static const char tabbed_fmtstr[] = "%s:\t%-12s"; static const char tabbed_deity_fmtstr[] = "%s\t%s"; static const char *fmtstr; static const char *deity_fmtstr; fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr; deity_fmtstr = iflags.menu_tab_sep ? tabbed_deity_fmtstr : untabbed_deity_fmtstr; any = zeroany; buf[0] = buf2[0] = '\0'; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, "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, iflags.menu_headings, "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); } /* don't want poly_gender() here; it forces `2' for non-humanoids */ genidx = is_neuter(youmonst.data) ? 2 : flags.female; Sprintf(buf, fmtstr, "gender", genders[genidx].adj); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); if (Upolyd && (int)u.mfemale != genidx) { 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, iflags.menu_headings, "Deities", FALSE); Sprintf(buf2, deity_fmtstr, 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", buf2); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); Sprintf(buf2, deity_fmtstr, 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", buf2); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); Sprintf(buf2, deity_fmtstr, 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", buf2); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); end_menu(tmpwin, "Base Attributes"); n = select_menu(tmpwin, PICK_NONE, &selected); destroy_nhwindow(tmpwin); return (n != -1); } /* ^X command */ STATIC_PTR int doattributes(VOID_ARGS) { if (!minimal_enlightenment()) return 0; if (wizard || discover) enlightenment(0); return 0; } /* KMH, #conduct * (shares enlightenment's tense handling) */ STATIC_PTR int doconduct(VOID_ARGS) { 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); } #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 */ #ifndef DUNGEON_OVERVIEW #ifdef WIZARD {C('o'), TRUE, wiz_where}, #endif #else {C('n'), TRUE, donamelevel}, /* if number_pad is set */ {C('o'), TRUE, dooverview_or_wiz_where}, /* depending on wizard status */ #endif /* DUNGEON_OVERVIEW */ {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}, #ifdef DUNGEON_OVERVIEW {"annotate", "name current level", donamelevel, TRUE}, #endif /* DUNGEON_OVERVIEW */ {"chat", "talk to someone", dotalk, TRUE}, /* converse? */ {"conduct", "list voluntary challenges you have maintained", doconduct, TRUE}, {"dip", "dip an object into something", dodip, FALSE}, {"enhance", "advance or check weapon and spell 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}, #ifdef DUNGEON_OVERVIEW {"overview", "show an overview of the dungeon", dooverview, TRUE}, #endif /* DUNGEON_OVERVIEW */ {"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 or a stone", dorub, FALSE}, {"sit", "sit down", dosit, FALSE}, {"tip", "empty a container", dotip, 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}, #ifdef DEBUG_MIGRATING_MONS {(char *)0, (char *)0, donull, TRUE}, #endif {(char *)0, (char *)0, donull, TRUE}, {(char *)0, (char *)0, donull, TRUE}, {(char *)0, (char *)0, donull, TRUE}, #ifdef PORT_DEBUG {(char *)0, (char *)0, donull, TRUE}, #endif {(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}, {(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[] = { {"levelchange", "change experience level", wiz_level_change, TRUE}, {"lightsources", "show mobile light sources", wiz_light_sources, TRUE}, #ifdef DEBUG_MIGRATING_MONS {"migratemons", "migrate n random monsters", wiz_migrate_mons, TRUE}, #endif {"monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol, TRUE}, {"panic", "test panic routine (fatal to game)", wiz_panic, TRUE}, {"polyself", "polymorph self", wiz_polyself, TRUE}, #ifdef PORT_DEBUG {"portdebug", "wizard port debug command", wiz_port_debug, TRUE}, #endif {"seenv", "show seen vectors", wiz_show_seenv, TRUE}, {"stats", "show memory statistics", wiz_show_stats, TRUE}, {"terrain", "show map topology", wiz_map_terrain, TRUE}, {"timeout", "look at timeout queue", wiz_timeout_queue, TRUE}, {"vision", "show vision array", wiz_show_vision, TRUE}, {"wizsmell", "smell monster", wiz_smell, TRUE}, #ifdef DEBUG {"wizdebug", "wizard debug command", wiz_debug_cmd, TRUE}, #endif {"wizrumorcheck", "verify rumor boundaries", wiz_rumor_check, TRUE}, {"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_OVL char cmd_from_func(fn) int NDECL((*fn)); { int i; for (i = 0; i < SIZE(cmdlist); ++i) if (cmdlist[i].f_funct == fn) return cmdlist[i].f_char; return 0; } static const char template[] = "%-18s %4ld %6ld"; static const char count_str[] = " count bytes"; static const char separator[] = "------------------ ----- ------"; STATIC_OVL int size_obj(otmp) struct obj *otmp; { int sz = (int)sizeof (struct obj); if (otmp->oextra) { if (ONAME(otmp)) sz += (int)strlen(ONAME(otmp)) + 1; if (OMONST(otmp)) sz += (int)sizeof (struct monst); if (OMID(otmp)) sz += (int)sizeof (unsigned); if (OLONG(otmp)) sz += (int)sizeof (long); if (OMAILCMD(otmp)) sz += (int)strlen(OMAILCMD(otmp)) + 1; } return sz; } 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 += size_obj(obj); } 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 int size_monst(mtmp) struct monst *mtmp; { int sz = (int)sizeof (struct monst); if (mtmp->mextra) { if (MNAME(mtmp)) sz += (int)strlen(MNAME(mtmp)) + 1; if (EGD(mtmp)) sz += (int)sizeof (struct egd); if (EPRI(mtmp)) sz += (int)sizeof (struct epri); if (ESHK(mtmp)) sz += (int)sizeof (struct eshk); if (EMIN(mtmp)) sz += (int)sizeof (struct emin); if (EDOG(mtmp)) sz += (int)sizeof (struct edog); } return sz; } 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 += size_monst(mon); } *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(); } #ifdef DEBUG_MIGRATING_MONS static int wiz_migrate_mons() { int mcount = 0; char inbuf[BUFSZ]; struct permonst *ptr; struct monst *mtmp; d_level tolevel; getlin("How many random monsters to migrate? [0]", inbuf); if (*inbuf == '\033') return 0; mcount = atoi(inbuf); if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz)) return 0; while (mcount > 0) { if (Is_stronghold(&u.uz)) assign_level(&tolevel, &valley_level); else get_level(&tolevel, depth(&u.uz) + 1); ptr = rndmonst(); mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS); if (mtmp) migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM, (coord *)0); mcount--; } return 0; } #endif #endif /* WIZARD */ #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c)) #define unmeta(c) (0x7f & (c)) /* called at startup and after number_pad is twiddled */ void reset_commands(initial) boolean initial; { static const char sdir[] = "hykulnjb><", sdir_swap_yz[] = "hzkulnjb><", ndir[] = "47896321><", ndir_phone_layout[] = "41236987><"; static const int ylist[] = { 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y')) }; const struct func_tab *cmdtmp; boolean flagtemp; int c, i, updated = 0; if (initial) { updated = 1; for (i = 0; i < SIZE(cmdlist); i++) { c = cmdlist[i].f_char & 0xff; Cmd.commands[c] = &cmdlist[i]; } Cmd.num_pad = FALSE; Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE; } else { /* basic num_pad */ flagtemp = iflags.num_pad; if (flagtemp != Cmd.num_pad) { Cmd.num_pad = flagtemp; ++updated; } /* swap_yz mode (only applicable for !num_pad) */ flagtemp = (iflags.num_pad_mode & 1) ? !Cmd.num_pad : FALSE; if (flagtemp != Cmd.swap_yz) { Cmd.swap_yz = flagtemp; ++updated; /* Cmd.swap_yz has been toggled; perform the swap (or reverse previous one) */ for (i = 0; i < SIZE(ylist); i++) { c = ylist[i] & 0xff; cmdtmp = Cmd.commands[c]; /* tmp = [y] */ Cmd.commands[c] = Cmd.commands[c + 1]; /* [y] = [z] */ Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */ } } /* MSDOS compatibility mode (only applicable for num_pad) */ flagtemp = (iflags.num_pad_mode & 1) ? Cmd.num_pad : FALSE; if (flagtemp != Cmd.pcHack_compat) { Cmd.pcHack_compat = flagtemp; ++updated; /* pcHack_compat has been toggled */ c = M('5') & 0xff; cmdtmp = Cmd.commands['5']; Cmd.commands['5'] = Cmd.commands[c]; Cmd.commands[c] = cmdtmp; c = M('0') & 0xff; Cmd.commands[c] = Cmd.pcHack_compat ? Cmd.commands['I'] : 0; } /* phone keypad layout (only applicable for num_pad) */ flagtemp = (iflags.num_pad_mode & 2) ? Cmd.num_pad : FALSE; if (flagtemp != Cmd.phone_layout) { Cmd.phone_layout = flagtemp; ++updated; /* phone_layout has been toggled */ for (i = 0; i < 3; i++) { c = '1' + i; /* 1,2,3 <-> 7,8,9 */ cmdtmp = Cmd.commands[c]; /* tmp = [1] */ Cmd.commands[c] = Cmd.commands[c + 6]; /* [1] = [7] */ Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */ c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */ cmdtmp = Cmd.commands[c]; /* tmp = [M-1] */ Cmd.commands[c] = Cmd.commands[c + 6]; /* [M-1] = [M-7] */ Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */ } } } /*?initial*/ if (updated) Cmd.serialno++; Cmd.dirchars = !Cmd.num_pad ? (!Cmd.swap_yz ? sdir : sdir_swap_yz) : (!Cmd.phone_layout ? ndir : ndir_phone_layout); Cmd.alphadirchars = !Cmd.num_pad ? Cmd.dirchars : sdir; Cmd.move_W = Cmd.dirchars[0]; Cmd.move_NW = Cmd.dirchars[1]; Cmd.move_N = Cmd.dirchars[2]; Cmd.move_NE = Cmd.dirchars[3]; Cmd.move_E = Cmd.dirchars[4]; Cmd.move_SE = Cmd.dirchars[5]; Cmd.move_S = Cmd.dirchars[6]; Cmd.move_SW = Cmd.dirchars[7]; } void rhack(cmd) register char *cmd; { boolean do_walk, do_rush, prefix_seen, bad_command, firsttime = (cmd == 0); iflags.menu_requested = FALSE; #ifdef SAFERHANGUP if (program_state.done_hup) end_of_input(); #endif if (firsttime) { context.nopick = 0; cmd = parse(); } if (*cmd == '\033') { context.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(); context.move = FALSE; return; /* probably we just had an interrupt */ } if (Cmd.pcHack_compat) { /* This handles very old inconsistent DOS/Windows behaviour * in a new way: earlier, the keyboard handler mapped these, * which caused counts to be strange when entered from the * number pad. Now do not map them until here. */ switch (*cmd) { case '5': *cmd = 'g'; break; case M('5'): *cmd = 'G'; break; case M('0'): *cmd = 'I'; break; } } /* handle most movement commands */ do_walk = do_rush = prefix_seen = FALSE; context.travel = context.travel1 = 0; switch (*cmd) { case 'g': if (movecmd(cmd[1])) { context.run = 2; do_rush = TRUE; } else prefix_seen = TRUE; break; case '5': if (!Cmd.num_pad) break; /* else FALLTHRU */ case 'G': if (movecmd(lowc(cmd[1]))) { context.run = 3; do_rush = TRUE; } else prefix_seen = TRUE; break; case '-': if (!Cmd.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])) { context.forcefight = 1; do_walk = TRUE; } else prefix_seen = TRUE; break; case 'm': if (movecmd(cmd[1]) || u.dz) { context.run = 0; context.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]))) { context.run = 1; context.nopick = 1; do_rush = TRUE; } else prefix_seen = TRUE; break; case '0': if (!Cmd.num_pad) break; (void)ddoinv(); /* a convenience borrowed from the PC */ context.move = FALSE; multi = 0; return; case CMD_CLICKLOOK: if (iflags.clicklook) { context.move = FALSE; do_look(2, &clicklook_cc); } return; case CMD_TRAVEL: if (flags.travelcmd) { context.travel = 1; context.travel1 = 1; context.run = 8; context.nopick = 1; do_rush = TRUE; break; } /*FALLTHRU*/ default: if (movecmd(*cmd)) { /* ordinary movement */ context.run = 0; /* only matters here if it was 8 */ do_walk = TRUE; } else if (movecmd(Cmd.num_pad ? unmeta(*cmd) : lowc(*cmd))) { context.run = 1; do_rush = TRUE; } else if (movecmd(unctrl(*cmd))) { context.run = 3; do_rush = TRUE; } break; } /* some special prefix handling */ /* overload 'm' prefix for ',' to mean "request a menu" */ if (prefix_seen && cmd[1] == ',') { iflags.menu_requested = TRUE; ++cmd; } if (do_walk) { if (multi) context.mv = TRUE; domove(); context.forcefight = 0; return; } else if (do_rush) { if (firsttime) { if (!multi) multi = max(COLNO,ROWNO); u.last_str_turn = 0; } context.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)); #if 0 for (tlist = cmdlist; tlist->f_char; tlist++) { if ((*cmd & 0xff) != (tlist->f_char & 0xff)) continue; #else if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) { #endif 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) { context.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 c, *cp = expcmd; while ((c = *cmd++) != '\0' && (int)(cp - expcmd) < (int)(sizeof expcmd - 3)) { if (c >= 040 && c < 0177) { *cp++ = c; } else if (c & 0200) { *cp++ = 'M'; *cp++ = '-'; *cp++ = c & ~0200; } else { *cp++ = '^'; *cp++ = c ^ 0100; } } *cp = '\0'; if (!prefix_seen || !iflags.cmdassist || !help_dir(0, "Invalid direction key!")) Norep("Unknown command '%s'.", expcmd); } /* didn't move */ context.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 = index(Cmd.dirchars, sym); u.dz = 0; if (!dp) return 0; u.dx = xdir[dp - Cmd.dirchars]; u.dy = ydir[dp - Cmd.dirchars]; u.dz = zdir[dp - Cmd.dirchars]; if (u.dx && u.dy && NODIAG(u.umonnum)) { u.dx = u.dy = 0; return 0; } return !u.dz; } /* decide whether a character (user input keystroke) requests screen repaint */ boolean redraw_cmd(c) char c; { return (c == C('r') || (Cmd.num_pad && c == C('l'))); } /* * uses getdir() but unlike getdir() it specifically * produces coordinates using the direction from getdir() * and verifies that those coordinates are ok. * * If the call to getdir() returns 0, Never_mind is displayed. * If the resulting coordinates are not okay, emsg is displayed. * * Returns non-zero if coordinates in cc are valid. */ int get_adjacent_loc(prompt,emsg,x,y,cc) const char *prompt, *emsg; xchar x,y; coord *cc; { xchar new_x, new_y; if (!getdir(prompt)) { pline(Never_mind); return 0; } new_x = x + u.dx; new_y = y + u.dy; if (cc && isok(new_x,new_y)) { cc->x = new_x; cc->y = new_y; } else { if (emsg) pline(emsg); return 0; } return 1; } int getdir(s) const char *s; { char dirsym; retry: #ifdef REDO if (in_doagain || *readchar_queue) dirsym = readchar(); else #endif dirsym = yn_function((s && *s != '^') ? s : "In what direction?", (char *)0, '\0'); /* remove the prompt string so caller won't have to */ clear_nhwindow(WIN_MESSAGE); if (redraw_cmd(dirsym)) { /* ^R */ docrt(); /* redraw */ goto retry; } #ifdef REDO savech(dirsym); #endif if (dirsym == '.' || dirsym == 's') { u.dx = u.dy = u.dz = 0; } else if (!movecmd(dirsym) && !u.dz) { boolean did_help = FALSE, help_requested; if (!index(quitchars, dirsym)) { help_requested = (dirsym == '?'); if (help_requested || iflags.cmdassist) { did_help = help_dir((s && *s == '^') ? dirsym : 0, help_requested ? (const char *)0 : "Invalid direction key!"); if (help_requested) goto retry; } if (!did_help) pline("What a strange direction!"); } return 0; } if (!u.dz && (Stunned || (Confusion && !rn2(5)))) confdir(); return 1; } STATIC_OVL boolean help_dir(sym, msg) char sym; const char *msg; { char ctrl; winid win; static const char wiz_only_list[] = "EFGIOVW"; char buf[BUFSZ], buf2[BUFSZ], *explain; win = create_nhwindow(NHW_TEXT); if (!win) return FALSE; if (msg) { Sprintf(buf, "cmdassist: %s", msg); putstr(win, 0, buf); putstr(win, 0, ""); } if (letter(sym)) { sym = highc(sym); ctrl = (sym - 'A') + 1; if ((explain = dowhatdoes_core(ctrl, buf2)) && (!index(wiz_only_list, sym) #ifdef WIZARD || wizard #endif )) { Sprintf(buf, "Are you trying to use ^%c%s?", sym, index(wiz_only_list, sym) ? "" : " as specified in the Guidebook"); putstr(win, 0, buf); putstr(win, 0, ""); putstr(win, 0, explain); putstr(win, 0, ""); putstr(win, 0, "To use that command, you press"); Sprintf(buf, "the key, and the <%c> key at the same time.", sym); putstr(win, 0, buf); putstr(win, 0, ""); } } if (NODIAG(u.umonnum)) { putstr(win, 0, "Valid direction keys in your current form are:"); Sprintf(buf, " %c ", Cmd.move_N); putstr(win, 0, buf); putstr(win, 0, " | "); Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E); putstr(win, 0, buf); putstr(win, 0, " | "); Sprintf(buf, " %c ", Cmd.move_S); putstr(win, 0, buf); } else { putstr(win, 0, "Valid direction keys are:"); Sprintf(buf, " %c %c %c", Cmd.move_NW, Cmd.move_N, Cmd.move_NE); putstr(win, 0, buf); putstr(win, 0, " \\ | / "); Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E); putstr(win, 0, buf); putstr(win, 0, " / | \\ "); Sprintf(buf, " %c %c %c", Cmd.move_SW, Cmd.move_S, Cmd.move_SE); putstr(win, 0, buf); }; putstr(win, 0, ""); putstr(win, 0, " < up"); putstr(win, 0, " > down"); putstr(win, 0, " . direct at yourself"); if (msg) { /* non-null msg means that this wasn't an explicit user request */ putstr(win, 0, ""); putstr(win, 0, "(Suppress this message with !cmdassist in config file.)"); } display_nhwindow(win, FALSE); destroy_nhwindow(win); return TRUE; } void confdir() { register int x = NODIAG(u.umonnum) ? 2*rn2(4) : rn2(8); u.dx = xdir[x]; u.dy = ydir[x]; return; } 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; if (iflags.clicklook && mod == CLICK_2) { clicklook_cc.x = x; clicklook_cc.y = y; cmd[0] = CMD_CLICKLOOK; return cmd; } x -= u.ux; y -= u.uy; if (flags.travelcmd) { 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, TEST_MOVE)) { cmd[1] = Cmd.dirchars[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; } } } else { /* convert without using floating point, allowing sloppy clicking */ if(x > 2*abs(y)) x = 1, y = 0; else if(y > 2*abs(x)) x = 0, y = 1; else if(x < -2*abs(y)) x = -1, y = 0; else if(y < -2*abs(x)) x = 0, y = -1; else x = sgn(x), y = sgn(y); if(x == 0 && y == 0) /* map click on player to "rest" command */ return "."; dir = xytod(x, y); } /* move, attack, etc. */ cmd[1] = 0; if(mod == CLICK_1) { cmd[0] = Cmd.dirchars[dir]; } else { cmd[0] = (Cmd.num_pad ? M(Cmd.dirchars[dir]) : (Cmd.dirchars[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; context.move = 1; flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */ if (!Cmd.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' || (Cmd.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); } #if defined(UNIX) || defined(SAFERHANGUP) static void end_of_input() { #ifndef NOSAVEONHANGUP # ifndef SAFERHANGUP if (!program_state.done_hup++) #endif if (program_state.something_worth_saving) (void) dosave0(); #endif exit_nhwindows((char *)0); clearlocks(); terminate(EXIT_SUCCESS); } #endif 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) { # ifndef SAFERHANGUP end_of_input(); # else program_state.done_hup++; sym = '\033'; # endif } #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(VOID_ARGS) { /* Keyboard travel command */ static char cmd[2]; coord cc; if (!flags.travelcmd) return 0; cmd[1]=0; cc.x = iflags.travelcc.x; cc.y = iflags.travelcc.y; if (cc.x == -1 && cc.y == -1) { /* No cached destination, start attempt from current position */ 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; } iflags.travelcc.x = u.tx = cc.x; iflags.travelcc.y = u.ty = cc.y; cmd[0] = CMD_TRAVEL; readchar_queue = cmd; return 0; } #ifdef PORT_DEBUG # ifdef WIN32CON extern void NDECL(win32con_debug_keystrokes); extern void NDECL(win32con_handler_info); # endif int wiz_port_debug() { int n, k; winid win; anything any; int item = 'a'; int num_menu_selections; struct menu_selection_struct { char *menutext; void NDECL((*fn)); } menu_selections[] = { #ifdef WIN32CON {"test win32 keystrokes", win32con_debug_keystrokes}, {"show keystroke handler information", win32con_handler_info}, #endif {(char *)0, (void NDECL((*)))0} /* array terminator */ }; num_menu_selections = SIZE(menu_selections) - 1; if (num_menu_selections > 0) { menu_item *pick_list; win = create_nhwindow(NHW_MENU); start_menu(win); for (k=0; k < num_menu_selections; ++k) { any.a_int = k+1; add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE, menu_selections[k].menutext, MENU_UNSELECTED); } end_menu(win, "Which port debugging feature?"); n = select_menu(win, PICK_ONE, &pick_list); destroy_nhwindow(win); if (n > 0) { n = pick_list[0].item.a_int - 1; free((genericptr_t) pick_list); /* execute the function */ (*menu_selections[n].fn)(); } } else pline("No port-specific debug capability defined."); return 0; } # endif /*PORT_DEBUG*/ /* * Parameter validator for generic yes/no function to prevent * the core from sending too long a prompt string to the * window port causing a buffer overflow there. */ char yn_function(query, resp, def) const char *query, *resp; char def; { char qbuf[QBUFSZ]; /* maximum acceptable length is QBUFSZ-1 */ if (strlen(query) < QBUFSZ) return (*windowprocs.win_yn_function)(query, resp, def); /* caller shouldn't have passed anything this long */ paniclog("Query truncated: ", query); (void) strncpy(qbuf, query, QBUFSZ-1 - 3); Strcpy(&qbuf[QBUFSZ-1 - 3], "..."); return (*windowprocs.win_yn_function)(qbuf, resp, def); } /*cmd.c*/