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