1/* SCCS Id: @(#)hack.c 3.4 2003/04/30 */ 2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5#include "hack.h" 6 7#ifdef OVL1 8STATIC_DCL void NDECL(maybe_wail); 9#endif /*OVL1*/ 10STATIC_DCL int NDECL(moverock); 11STATIC_DCL int FDECL(still_chewing,(XCHAR_P,XCHAR_P)); 12#ifdef SINKS 13STATIC_DCL void NDECL(dosinkfall); 14#endif 15STATIC_DCL boolean FDECL(findtravelpath, (BOOLEAN_P)); 16STATIC_DCL boolean FDECL(monstinroom, (struct permonst *,int)); 17 18STATIC_DCL void FDECL(move_update, (BOOLEAN_P)); 19 20#define IS_SHOP(x) (rooms[x].rtype >= SHOPBASE) 21 22#ifdef OVL2 23 24boolean 25revive_nasty(x, y, msg) 26int x,y; 27const char *msg; 28{ 29 register struct obj *otmp, *otmp2; 30 struct monst *mtmp; 31 coord cc; 32 boolean revived = FALSE; 33 34 for(otmp = level.objects[x][y]; otmp; otmp = otmp2) { 35 otmp2 = otmp->nexthere; 36 if (otmp->otyp == CORPSE && 37 (is_rider(&mons[otmp->corpsenm]) || 38 otmp->corpsenm == PM_WIZARD_OF_YENDOR)) { 39 /* move any living monster already at that location */ 40 if((mtmp = m_at(x,y)) && enexto(&cc, x, y, mtmp->data)) 41 rloc_to(mtmp, cc.x, cc.y); 42 if(msg) Norep("%s", msg); 43 revived = revive_corpse(otmp); 44 } 45 } 46 47 /* this location might not be safe, if not, move revived monster */ 48 if (revived) { 49 mtmp = m_at(x,y); 50 if (mtmp && !goodpos(x, y, mtmp, 0) && 51 enexto(&cc, x, y, mtmp->data)) { 52 rloc_to(mtmp, cc.x, cc.y); 53 } 54 /* else impossible? */ 55 } 56 57 return (revived); 58} 59 60STATIC_OVL int 61moverock() 62{ 63 register xchar rx, ry, sx, sy; 64 register struct obj *otmp; 65 register struct trap *ttmp; 66 register struct monst *mtmp; 67 68 sx = u.ux + u.dx, sy = u.uy + u.dy; /* boulder starting position */ 69 while ((otmp = sobj_at(BOULDER, sx, sy)) != 0) { 70 /* make sure that this boulder is visible as the top object */ 71 if (otmp != level.objects[sx][sy]) movobj(otmp, sx, sy); 72 73 rx = u.ux + 2 * u.dx; /* boulder destination position */ 74 ry = u.uy + 2 * u.dy; 75 nomul(0); 76 if (Levitation || Is_airlevel(&u.uz)) { 77 if (Blind) feel_location(sx, sy); 78 You("don't have enough leverage to push %s.", the(xname(otmp))); 79 /* Give them a chance to climb over it? */ 80 return -1; 81 } 82 if (verysmall(youmonst.data) 83#ifdef STEED 84 && !u.usteed 85#endif 86 ) { 87 if (Blind) feel_location(sx, sy); 88 pline("You're too small to push that %s.", xname(otmp)); 89 goto cannot_push; 90 } 91 if (isok(rx,ry) && !IS_ROCK(levl[rx][ry].typ) && 92 levl[rx][ry].typ != IRONBARS && 93 (!IS_DOOR(levl[rx][ry].typ) || !(u.dx && u.dy) || ( 94#ifdef REINCARNATION 95 !Is_rogue_level(&u.uz) && 96#endif 97 (levl[rx][ry].doormask & ~D_BROKEN) == D_NODOOR)) && 98 !sobj_at(BOULDER, rx, ry)) { 99 ttmp = t_at(rx, ry); 100 mtmp = m_at(rx, ry); 101 102 /* KMH -- Sokoban doesn't let you push boulders diagonally */ 103 if (In_sokoban(&u.uz) && u.dx && u.dy) { 104 if (Blind) feel_location(sx,sy); 105 pline("%s won't roll diagonally on this %s.", 106 The(xname(otmp)), surface(sx, sy)); 107 goto cannot_push; 108 } 109 110 if (revive_nasty(rx, ry, "You sense movement on the other side.")) 111 return (-1); 112 113 if (mtmp && !noncorporeal(mtmp->data) && 114 (!mtmp->mtrapped || 115 !(ttmp && ((ttmp->ttyp == PIT) || 116 (ttmp->ttyp == SPIKED_PIT))))) { 117 if (Blind) feel_location(sx, sy); 118 if (canspotmon(mtmp)) 119 pline("There's %s on the other side.", a_monnam(mtmp)); 120 else { 121 You_hear("a monster behind %s.", the(xname(otmp))); 122 map_invisible(rx, ry); 123 } 124 if (flags.verbose) 125 pline("Perhaps that's why %s cannot move it.", 126#ifdef STEED 127 u.usteed ? y_monnam(u.usteed) : 128#endif 129 "you"); 130 goto cannot_push; 131 } 132 133 if (ttmp) 134 switch(ttmp->ttyp) { 135 case LANDMINE: 136 if (rn2(10)) { 137 obj_extract_self(otmp); 138 place_object(otmp, rx, ry); 139 unblock_point(sx, sy); 140 newsym(sx, sy); 141 pline("KAABLAMM!!! %s %s land mine.", 142 Tobjnam(otmp, "trigger"), 143 ttmp->madeby_u ? "your" : "a"); 144 blow_up_landmine(ttmp); 145 /* if the boulder remains, it should fill the pit */ 146 fill_pit(u.ux, u.uy); 147 if (cansee(rx,ry)) newsym(rx,ry); 148 continue; 149 } 150 break; 151 case SPIKED_PIT: 152 case PIT: 153 obj_extract_self(otmp); 154 /* vision kludge to get messages right; 155 the pit will temporarily be seen even 156 if this is one among multiple boulders */ 157 if (!Blind) viz_array[ry][rx] |= IN_SIGHT; 158 if (!flooreffects(otmp, rx, ry, "fall")) { 159 place_object(otmp, rx, ry); 160 } 161 if (mtmp && !Blind) newsym(rx, ry); 162 continue; 163 case HOLE: 164 case TRAPDOOR: 165 if (Blind) 166 pline("Kerplunk! You no longer feel %s.", 167 the(xname(otmp))); 168 else 169 pline("%s%s and %s a %s in the %s!", 170 Tobjnam(otmp, 171 (ttmp->ttyp == TRAPDOOR) ? "trigger" : "fall"), 172 (ttmp->ttyp == TRAPDOOR) ? nul : " into", 173 otense(otmp, "plug"), 174 (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole", 175 surface(rx, ry)); 176 deltrap(ttmp); 177 delobj(otmp); 178 bury_objs(rx, ry); 179 if (cansee(rx,ry)) newsym(rx,ry); 180 continue; 181 case LEVEL_TELEP: 182 case TELEP_TRAP: 183#ifdef STEED 184 if (u.usteed) 185 pline("%s pushes %s and suddenly it disappears!", 186 upstart(y_monnam(u.usteed)), the(xname(otmp))); 187 else 188#endif 189 You("push %s and suddenly it disappears!", 190 the(xname(otmp))); 191 if (ttmp->ttyp == TELEP_TRAP) 192 rloco(otmp); 193 else { 194 int newlev = random_teleport_level(); 195 d_level dest; 196 197 if (newlev == depth(&u.uz) || In_endgame(&u.uz)) 198 continue; 199 obj_extract_self(otmp); 200 add_to_migration(otmp); 201 get_level(&dest, newlev); 202 otmp->ox = dest.dnum; 203 otmp->oy = dest.dlevel; 204 otmp->owornmask = (long)MIGR_RANDOM; 205 } 206 seetrap(ttmp); 207 continue; 208 } 209 if (closed_door(rx, ry)) 210 goto nopushmsg; 211 if (boulder_hits_pool(otmp, rx, ry, TRUE)) 212 continue; 213 /* 214 * Re-link at top of fobj chain so that pile order is preserved 215 * when level is restored. 216 */ 217 if (otmp != fobj) { 218 remove_object(otmp); 219 place_object(otmp, otmp->ox, otmp->oy); 220 } 221 222 { 223#ifdef LINT /* static long lastmovetime; */ 224 long lastmovetime; 225 lastmovetime = 0; 226#else 227 /* note: reset to zero after save/restore cycle */ 228 static NEARDATA long lastmovetime; 229#endif 230#ifdef STEED 231 if (!u.usteed) { 232#endif 233 if (moves > lastmovetime+2 || moves < lastmovetime) 234 pline("With %s effort you move %s.", 235 throws_rocks(youmonst.data) ? "little" : "great", 236 the(xname(otmp))); 237 exercise(A_STR, TRUE); 238#ifdef STEED 239 } else 240 pline("%s moves %s.", 241 upstart(y_monnam(u.usteed)), the(xname(otmp))); 242#endif 243 lastmovetime = moves; 244 } 245 246 /* Move the boulder *after* the message. */ 247 if (glyph_is_invisible(levl[rx][ry].glyph)) 248 unmap_object(rx, ry); 249 movobj(otmp, rx, ry); /* does newsym(rx,ry) */ 250 if (Blind) { 251 feel_location(rx,ry); 252 feel_location(sx, sy); 253 } else { 254 newsym(sx, sy); 255 } 256 } else { 257 nopushmsg: 258#ifdef STEED 259 if (u.usteed) 260 pline("%s tries to move %s, but cannot.", 261 upstart(y_monnam(u.usteed)), the(xname(otmp))); 262 else 263#endif 264 You("try to move %s, but in vain.", the(xname(otmp))); 265 if (Blind) feel_location(sx, sy); 266 cannot_push: 267 if (throws_rocks(youmonst.data)) { 268#ifdef STEED 269 if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) { 270 You("aren't skilled enough to %s %s from %s.", 271 (flags.pickup && !In_sokoban(&u.uz)) 272 ? "pick up" : "push aside", 273 the(xname(otmp)), y_monnam(u.usteed)); 274 } else 275#endif 276 { 277 pline("However, you can easily %s.", 278 (flags.pickup && !In_sokoban(&u.uz)) 279 ? "pick it up" : "push it aside"); 280 if (In_sokoban(&u.uz)) 281 change_luck(-1); /* Sokoban guilt */ 282 break; 283 } 284 break; 285 } 286 287 if ( 288#ifdef STEED 289 !u.usteed && 290#endif 291 (((!invent || inv_weight() <= -850) && 292 (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][sy].typ) 293 && IS_ROCK(levl[sx][u.uy].typ)))) 294 || verysmall(youmonst.data))) { 295 pline("However, you can squeeze yourself into a small opening."); 296 if (In_sokoban(&u.uz)) 297 change_luck(-1); /* Sokoban guilt */ 298 break; 299 } else 300 return (-1); 301 } 302 } 303 return (0); 304} 305 306/* 307 * still_chewing() 308 * 309 * Chew on a wall, door, or boulder. Returns TRUE if still eating, FALSE 310 * when done. 311 */ 312STATIC_OVL int 313still_chewing(x,y) 314 xchar x, y; 315{ 316 struct rm *lev = &levl[x][y]; 317 struct obj *boulder = sobj_at(BOULDER,x,y); 318 const char *digtxt = (char *)0, *dmgtxt = (char *)0; 319 320 if (digging.down) /* not continuing previous dig (w/ pick-axe) */ 321 (void) memset((genericptr_t)&digging, 0, sizeof digging); 322 323 if (!boulder && IS_ROCK(lev->typ) && !may_dig(x,y)) { 324 You("hurt your teeth on the %s.", 325 IS_TREE(lev->typ) ? "tree" : "hard stone"); 326 nomul(0); 327 return 1; 328 } else if (digging.pos.x != x || digging.pos.y != y || 329 !on_level(&digging.level, &u.uz)) { 330 digging.down = FALSE; 331 digging.chew = TRUE; 332 digging.warned = FALSE; 333 digging.pos.x = x; 334 digging.pos.y = y; 335 assign_level(&digging.level, &u.uz); 336 /* solid rock takes more work & time to dig through */ 337 digging.effort = 338 (IS_ROCK(lev->typ) && !IS_TREE(lev->typ) ? 30 : 60) + u.udaminc; 339 You("start chewing %s %s.", 340 (boulder || IS_TREE(lev->typ)) ? "on a" : "a hole in the", 341 boulder ? "boulder" : 342 IS_TREE(lev->typ) ? "tree" : IS_ROCK(lev->typ) ? "rock" : "door"); 343 watch_dig((struct monst *)0, x, y, FALSE); 344 return 1; 345 } else if ((digging.effort += (30 + u.udaminc)) <= 100) { 346 if (flags.verbose) 347 You("%s chewing on the %s.", 348 digging.chew ? "continue" : "begin", 349 boulder ? "boulder" : 350 IS_TREE(lev->typ) ? "tree" : 351 IS_ROCK(lev->typ) ? "rock" : "door"); 352 digging.chew = TRUE; 353 watch_dig((struct monst *)0, x, y, FALSE); 354 return 1; 355 } 356 357 /* Okay, you've chewed through something */ 358 u.uconduct.food++; 359 u.uhunger += rnd(20); 360 361 if (boulder) { 362 delobj(boulder); /* boulder goes bye-bye */ 363 You("eat the boulder."); /* yum */ 364 365 /* 366 * The location could still block because of 367 * 1. More than one boulder 368 * 2. Boulder stuck in a wall/stone/door. 369 * 370 * [perhaps use does_block() below (from vision.c)] 371 */ 372 if (IS_ROCK(lev->typ) || closed_door(x,y) || sobj_at(BOULDER,x,y)) { 373 block_point(x,y); /* delobj will unblock the point */ 374 /* reset dig state */ 375 (void) memset((genericptr_t)&digging, 0, sizeof digging); 376 return 1; 377 } 378 379 } else if (IS_WALL(lev->typ)) { 380 if (*in_rooms(x, y, SHOPBASE)) { 381 add_damage(x, y, 10L * ACURRSTR); 382 dmgtxt = "damage"; 383 } 384 digtxt = "chew a hole in the wall."; 385 if (level.flags.is_maze_lev) { 386 lev->typ = ROOM; 387 } else if (level.flags.is_cavernous_lev && !in_town(x, y)) { 388 lev->typ = CORR; 389 } else { 390 lev->typ = DOOR; 391 lev->doormask = D_NODOOR; 392 } 393 } else if (IS_TREE(lev->typ)) { 394 digtxt = "chew through the tree."; 395 lev->typ = ROOM; 396 } else if (lev->typ == SDOOR) { 397 if (lev->doormask & D_TRAPPED) { 398 lev->doormask = D_NODOOR; 399 b_trapped("secret door", 0); 400 } else { 401 digtxt = "chew through the secret door."; 402 lev->doormask = D_BROKEN; 403 } 404 lev->typ = DOOR; 405 406 } else if (IS_DOOR(lev->typ)) { 407 if (*in_rooms(x, y, SHOPBASE)) { 408 add_damage(x, y, 400L); 409 dmgtxt = "break"; 410 } 411 if (lev->doormask & D_TRAPPED) { 412 lev->doormask = D_NODOOR; 413 b_trapped("door", 0); 414 } else { 415 digtxt = "chew through the door."; 416 lev->doormask = D_BROKEN; 417 } 418 419 } else { /* STONE or SCORR */ 420 digtxt = "chew a passage through the rock."; 421 lev->typ = CORR; 422 } 423 424 unblock_point(x, y); /* vision */ 425 newsym(x, y); 426 if (digtxt) You(digtxt); /* after newsym */ 427 if (dmgtxt) pay_for_damage(dmgtxt, FALSE); 428 (void) memset((genericptr_t)&digging, 0, sizeof digging); 429 return 0; 430} 431 432#endif /* OVL2 */ 433#ifdef OVLB 434 435void 436movobj(obj, ox, oy) 437register struct obj *obj; 438register xchar ox, oy; 439{ 440 /* optimize by leaving on the fobj chain? */ 441 remove_object(obj); 442 newsym(obj->ox, obj->oy); 443 place_object(obj, ox, oy); 444 newsym(ox, oy); 445} 446 447#ifdef SINKS 448static NEARDATA const char fell_on_sink[] = "fell onto a sink"; 449 450STATIC_OVL void 451dosinkfall() 452{ 453 register struct obj *obj; 454 455 if (is_floater(youmonst.data) || (HLevitation & FROMOUTSIDE)) { 456 You("wobble unsteadily for a moment."); 457 } else { 458 long save_ELev = ELevitation, save_HLev = HLevitation; 459 460 /* fake removal of levitation in advance so that final 461 disclosure will be right in case this turns out to 462 be fatal; fortunately the fact that rings and boots 463 are really still worn has no effect on bones data */ 464 ELevitation = HLevitation = 0L; 465 You("crash to the floor!"); 466 losehp(rn1(8, 25 - (int)ACURR(A_CON)), 467 fell_on_sink, NO_KILLER_PREFIX); 468 exercise(A_DEX, FALSE); 469 selftouch("Falling, you"); 470 for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) 471 if (obj->oclass == WEAPON_CLASS || is_weptool(obj)) { 472 You("fell on %s.", doname(obj)); 473 losehp(rnd(3), fell_on_sink, NO_KILLER_PREFIX); 474 exercise(A_CON, FALSE); 475 } 476 ELevitation = save_ELev; 477 HLevitation = save_HLev; 478 } 479 480 ELevitation &= ~W_ARTI; 481 HLevitation &= ~(I_SPECIAL|TIMEOUT); 482 HLevitation++; 483 if(uleft && uleft->otyp == RIN_LEVITATION) { 484 obj = uleft; 485 Ring_off(obj); 486 off_msg(obj); 487 } 488 if(uright && uright->otyp == RIN_LEVITATION) { 489 obj = uright; 490 Ring_off(obj); 491 off_msg(obj); 492 } 493 if(uarmf && uarmf->otyp == LEVITATION_BOOTS) { 494 obj = uarmf; 495 (void)Boots_off(); 496 off_msg(obj); 497 } 498 HLevitation--; 499} 500#endif 501 502boolean 503may_dig(x,y) 504register xchar x,y; 505/* intended to be called only on ROCKs */ 506{ 507 return (boolean)(!(IS_STWALL(levl[x][y].typ) && 508 (levl[x][y].wall_info & W_NONDIGGABLE))); 509} 510 511boolean 512may_passwall(x,y) 513register xchar x,y; 514{ 515 return (boolean)(!(IS_STWALL(levl[x][y].typ) && 516 (levl[x][y].wall_info & W_NONPASSWALL))); 517} 518 519#endif /* OVLB */ 520#ifdef OVL1 521 522boolean 523bad_rock(mdat,x,y) 524struct permonst *mdat; 525register xchar x,y; 526{ 527 return((boolean) ((In_sokoban(&u.uz) && sobj_at(BOULDER,x,y)) || 528 (IS_ROCK(levl[x][y].typ) 529 && (!tunnels(mdat) || needspick(mdat) || !may_dig(x,y)) 530 && !(passes_walls(mdat) && may_passwall(x,y))))); 531} 532 533boolean 534invocation_pos(x, y) 535xchar x, y; 536{ 537 return((boolean)(Invocation_lev(&u.uz) && x == inv_pos.x && y == inv_pos.y)); 538} 539 540#endif /* OVL1 */ 541#ifdef OVL3 542 543/* return TRUE if (dx,dy) is an OK place to move 544 * mode is one of DO_MOVE, TEST_MOVE or TEST_TRAV 545 */ 546boolean 547test_move(ux, uy, dx, dy, mode) 548int ux, uy, dx, dy; 549int mode; 550{ 551 int x = ux+dx; 552 int y = uy+dy; 553 register struct rm *tmpr = &levl[x][y]; 554 register struct rm *ust; 555 556 /* 557 * Check for physical obstacles. First, the place we are going. 558 */ 559 if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) { 560 if (Blind && mode == DO_MOVE) feel_location(x,y); 561 if (Passes_walls && may_passwall(x,y)) { 562 ; /* do nothing */ 563 } else if (tmpr->typ == IRONBARS) { 564 if (!(Passes_walls || passes_bars(youmonst.data))) 565 return FALSE; 566 } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) { 567 /* Eat the rock. */ 568 if (mode == DO_MOVE && still_chewing(x,y)) return FALSE; 569 } else if (flags.autodig && !flags.run && !flags.nopick && 570 uwep && is_pick(uwep)) { 571 /* MRKR: Automatic digging when wielding the appropriate tool */ 572 if (mode == DO_MOVE) 573 (void) use_pick_axe2(uwep); 574 return FALSE; 575 } else { 576 if (mode == DO_MOVE) { 577 if (Is_stronghold(&u.uz) && is_db_wall(x,y)) 578 pline_The("drawbridge is up!"); 579 if (Passes_walls && !may_passwall(x,y) && In_sokoban(&u.uz)) 580 pline_The("Sokoban walls resist your ability."); 581 } 582 return FALSE; 583 } 584 } else if (IS_DOOR(tmpr->typ)) { 585 if (closed_door(x,y)) { 586 if (Blind && mode == DO_MOVE) feel_location(x,y); 587 if (Passes_walls) 588 ; /* do nothing */ 589 else if (can_ooze(&youmonst)) { 590 if (mode == DO_MOVE) You("ooze under the door."); 591 } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) { 592 /* Eat the door. */ 593 if (mode == DO_MOVE && still_chewing(x,y)) return FALSE; 594 } else { 595 if (mode == DO_MOVE) { 596 if (amorphous(youmonst.data)) 597 You("try to ooze under the door, but can't squeeze your possessions through."); 598 else if (x == ux || y == uy) { 599 if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) { 600#ifdef STEED 601 if (u.usteed) { 602 You_cant("lead %s through that closed door.", 603 y_monnam(u.usteed)); 604 } else 605#endif 606 { 607 pline("Ouch! You bump into a door."); 608 exercise(A_DEX, FALSE); 609 } 610 } else pline("That door is closed."); 611 } 612 } else if (mode == TEST_TRAV) goto testdiag; 613 return FALSE; 614 } 615 } else { 616 testdiag: 617 if (dx && dy && !Passes_walls 618 && ((tmpr->doormask & ~D_BROKEN) 619#ifdef REINCARNATION 620 || Is_rogue_level(&u.uz) 621#endif 622 || block_door(x,y))) { 623 /* Diagonal moves into a door are not allowed. */ 624 if (Blind && mode == DO_MOVE) 625 feel_location(x,y); 626 return FALSE; 627 } 628 } 629 } 630 if (dx && dy 631 && bad_rock(youmonst.data,ux,y) && bad_rock(youmonst.data,x,uy)) { 632 /* Move at a diagonal. */ 633 if (In_sokoban(&u.uz)) { 634 if (mode == DO_MOVE) 635 You("cannot pass that way."); 636 return FALSE; 637 } 638 if (bigmonst(youmonst.data)) { 639 if (mode == DO_MOVE) 640 Your("body is too large to fit through."); 641 return FALSE; 642 } 643 if (invent && (inv_weight() + weight_cap() > 600)) { 644 if (mode == DO_MOVE) 645 You("are carrying too much to get through."); 646 return FALSE; 647 } 648 } 649 /* Pick travel path that does not require crossing a trap. 650 * Avoid water and lava using the usual running rules. 651 * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */ 652 if (flags.run == 8 && mode != DO_MOVE && (x != u.ux || y != u.uy)) { 653 struct trap* t = t_at(x, y); 654 655 if ((t && t->tseen) || 656 (!Levitation && !Flying && 657 !is_clinger(youmonst.data) && 658 (is_pool(x, y) || is_lava(x, y)) && levl[x][y].seenv)) 659 return FALSE; 660 } 661 662 ust = &levl[ux][uy]; 663 664 /* Now see if other things block our way . . */ 665 if (dx && dy && !Passes_walls 666 && (IS_DOOR(ust->typ) && ((ust->doormask & ~D_BROKEN) 667#ifdef REINCARNATION 668 || Is_rogue_level(&u.uz) 669#endif 670 || block_entry(x, y)) 671 )) { 672 /* Can't move at a diagonal out of a doorway with door. */ 673 return FALSE; 674 } 675 676 if (sobj_at(BOULDER,x,y) && (In_sokoban(&u.uz) || !Passes_walls)) { 677 if (!(Blind || Hallucination) && (flags.run >= 2) && mode != TEST_TRAV) 678 return FALSE; 679 if (mode == DO_MOVE) { 680 /* tunneling monsters will chew before pushing */ 681 if (tunnels(youmonst.data) && !needspick(youmonst.data) && 682 !In_sokoban(&u.uz)) { 683 if (still_chewing(x,y)) return FALSE; 684 } else 685 if (moverock() < 0) return FALSE; 686 } else if (mode == TEST_TRAV) { 687 struct obj* obj; 688 689 /* don't pick two boulders in a row, unless there's a way thru */ 690 if (sobj_at(BOULDER,ux,uy) && !In_sokoban(&u.uz)) { 691 if (!Passes_walls && 692 !(tunnels(youmonst.data) && !needspick(youmonst.data)) && 693 !carrying(PICK_AXE) && !carrying(DWARVISH_MATTOCK) && 694 !((obj = carrying(WAN_DIGGING)) && 695 !objects[obj->otyp].oc_name_known)) 696 return FALSE; 697 } 698 } 699 /* assume you'll be able to push it when you get there... */ 700 } 701 702 /* OK, it is a legal place to move. */ 703 return TRUE; 704} 705 706/* 707 * Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy). 708 * A shortest path is returned. If guess is TRUE, consider various 709 * inaccessible locations as valid intermediate path points. 710 * Returns TRUE if a path was found. 711 */ 712static boolean 713findtravelpath(guess) 714boolean guess; 715{ 716 /* if travel to adjacent, reachable location, use normal movement rules */ 717 if (!guess && iflags.travel1 && distmin(u.ux, u.uy, u.tx, u.ty) == 1) { 718 flags.run = 0; 719 if (test_move(u.ux, u.uy, u.tx-u.ux, u.ty-u.uy, TEST_MOVE)) { 720 u.dx = u.tx-u.ux; 721 u.dy = u.ty-u.uy; 722 nomul(0); 723 iflags.travelcc.x = iflags.travelcc.y = -1; 724 return TRUE; 725 } 726 flags.run = 8; 727 } 728 if (u.tx != u.ux || u.ty != u.uy) { 729 xchar travel[COLNO][ROWNO]; 730 xchar travelstepx[2][COLNO*ROWNO]; 731 xchar travelstepy[2][COLNO*ROWNO]; 732 xchar tx, ty, ux, uy; 733 int n = 1; /* max offset in travelsteps */ 734 int set = 0; /* two sets current and previous */ 735 int radius = 1; /* search radius */ 736 int i; 737 738 /* If guessing, first find an "obvious" goal location. The obvious 739 * goal is the position the player knows of, or might figure out 740 * (couldsee) that is closest to the target on a straight path. 741 */ 742 if (guess) { 743 tx = u.ux; ty = u.uy; ux = u.tx; uy = u.ty; 744 } else { 745 tx = u.tx; ty = u.ty; ux = u.ux; uy = u.uy; 746 } 747 748 noguess: 749 (void) memset((genericptr_t)travel, 0, sizeof(travel)); 750 travelstepx[0][0] = tx; 751 travelstepy[0][0] = ty; 752 753 while (n != 0) { 754 int nn = 0; 755 756 for (i = 0; i < n; i++) { 757 int dir; 758 int x = travelstepx[set][i]; 759 int y = travelstepy[set][i]; 760 static int ordered[] = { 0, 2, 4, 6, 1, 3, 5, 7 }; 761 /* no diagonal movement for grid bugs */ 762 int dirmax = u.umonnum == PM_GRID_BUG ? 4 : 8; 763 764 for (dir = 0; dir < dirmax; ++dir) { 765 int nx = x+xdir[ordered[dir]]; 766 int ny = y+ydir[ordered[dir]]; 767 768 if (!isok(nx, ny)) continue; 769 if ((!Passes_walls && !can_ooze(&youmonst) && 770 closed_door(x, y)) || sobj_at(BOULDER, x, y)) { 771 /* closed doors and boulders usually 772 * cause a delay, so prefer another path */ 773 if (travel[x][y] > radius-3) { 774 travelstepx[1-set][nn] = x; 775 travelstepy[1-set][nn] = y; 776 /* don't change travel matrix! */ 777 nn++; 778 continue; 779 } 780 } 781 if (test_move(x, y, nx-x, ny-y, TEST_TRAV) && 782 (levl[nx][ny].seenv || (!Blind && couldsee(nx, ny)))) { 783 if (nx == ux && ny == uy) { 784 if (!guess) { 785 u.dx = x-ux; 786 u.dy = y-uy; 787 if (x == u.tx && y == u.ty) { 788 nomul(0); 789 /* reset run so domove run checks work */ 790 flags.run = 8; 791 iflags.travelcc.x = iflags.travelcc.y = -1; 792 } 793 return TRUE; 794 } 795 } else if (!travel[nx][ny]) { 796 travelstepx[1-set][nn] = nx; 797 travelstepy[1-set][nn] = ny; 798 travel[nx][ny] = radius; 799 nn++; 800 } 801 } 802 } 803 } 804 805 n = nn; 806 set = 1-set; 807 radius++; 808 } 809 810 /* if guessing, find best location in travel matrix and go there */ 811 if (guess) { 812 int px = tx, py = ty; /* pick location */ 813 int dist, nxtdist, d2, nd2; 814 815 dist = distmin(ux, uy, tx, ty); 816 d2 = dist2(ux, uy, tx, ty); 817 for (tx = 1; tx < COLNO; ++tx) 818 for (ty = 0; ty < ROWNO; ++ty) 819 if (travel[tx][ty]) { 820 nxtdist = distmin(ux, uy, tx, ty); 821 if (nxtdist == dist && couldsee(tx, ty)) { 822 nd2 = dist2(ux, uy, tx, ty); 823 if (nd2 < d2) { 824 /* prefer non-zigzag path */ 825 px = tx; py = ty; 826 d2 = nd2; 827 } 828 } else if (nxtdist < dist && couldsee(tx, ty)) { 829 px = tx; py = ty; 830 dist = nxtdist; 831 d2 = dist2(ux, uy, tx, ty); 832 } 833 } 834 835 if (px == u.ux && py == u.uy) { 836 /* no guesses, just go in the general direction */ 837 u.dx = sgn(u.tx - u.ux); 838 u.dy = sgn(u.ty - u.uy); 839 if (test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE)) 840 return TRUE; 841 goto found; 842 } 843 tx = px; 844 ty = py; 845 ux = u.ux; 846 uy = u.uy; 847 set = 0; 848 n = radius = 1; 849 guess = FALSE; 850 goto noguess; 851 } 852 return FALSE; 853 } 854 855found: 856 u.dx = 0; 857 u.dy = 0; 858 nomul(0); 859 return FALSE; 860} 861 862void 863domove() 864{ 865 register struct monst *mtmp; 866 register struct rm *tmpr; 867 register xchar x,y; 868 struct trap *trap; 869 int wtcap; 870 boolean on_ice; 871 xchar chainx, chainy, ballx, bally; /* ball&chain new positions */ 872 int bc_control; /* control for ball&chain */ 873 boolean cause_delay = FALSE; /* dragging ball will skip a move */ 874 const char *predicament; 875 876 u_wipe_engr(rnd(5)); 877 878 if (flags.travel) { 879 if (!findtravelpath(FALSE)) 880 (void) findtravelpath(TRUE); 881 iflags.travel1 = 0; 882 } 883 884 if(((wtcap = near_capacity()) >= OVERLOADED 885 || (wtcap > SLT_ENCUMBER && 886 (Upolyd ? (u.mh < 5 && u.mh != u.mhmax) 887 : (u.uhp < 10 && u.uhp != u.uhpmax)))) 888 && !Is_airlevel(&u.uz)) { 889 if(wtcap < OVERLOADED) { 890 You("don't have enough stamina to move."); 891 exercise(A_CON, FALSE); 892 } else 893 You("collapse under your load."); 894 nomul(0); 895 return; 896 } 897 if(u.uswallow) { 898 u.dx = u.dy = 0; 899 u.ux = x = u.ustuck->mx; 900 u.uy = y = u.ustuck->my; 901 mtmp = u.ustuck; 902 } else { 903 if (Is_airlevel(&u.uz) && rn2(4) && 904 !Levitation && !Flying) { 905 switch(rn2(3)) { 906 case 0: 907 You("tumble in place."); 908 exercise(A_DEX, FALSE); 909 break; 910 case 1: 911 You_cant("control your movements very well."); break; 912 case 2: 913 pline("It's hard to walk in thin air."); 914 exercise(A_DEX, TRUE); 915 break; 916 } 917 return; 918 } 919 920 /* check slippery ice */ 921 on_ice = !Levitation && is_ice(u.ux, u.uy); 922 if (on_ice) { 923 static int skates = 0; 924 if (!skates) skates = find_skates(); 925 if ((uarmf && uarmf->otyp == skates) 926 || resists_cold(&youmonst) || Flying 927 || is_floater(youmonst.data) || is_clinger(youmonst.data) 928 || is_whirly(youmonst.data)) 929 on_ice = FALSE; 930 else if (!rn2(Cold_resistance ? 3 : 2)) { 931 HFumbling |= FROMOUTSIDE; 932 HFumbling &= ~TIMEOUT; 933 HFumbling += 1; /* slip on next move */ 934 } 935 } 936 if (!on_ice && (HFumbling & FROMOUTSIDE)) 937 HFumbling &= ~FROMOUTSIDE; 938 939 x = u.ux + u.dx; 940 y = u.uy + u.dy; 941 if(Stunned || (Confusion && !rn2(5))) { 942 register int tries = 0; 943 944 do { 945 if(tries++ > 50) { 946 nomul(0); 947 return; 948 } 949 confdir(); 950 x = u.ux + u.dx; 951 y = u.uy + u.dy; 952 } while(!isok(x, y) || bad_rock(youmonst.data, x, y)); 953 } 954 /* turbulence might alter your actual destination */ 955 if (u.uinwater) { 956 water_friction(); 957 if (!u.dx && !u.dy) { 958 nomul(0); 959 return; 960 } 961 x = u.ux + u.dx; 962 y = u.uy + u.dy; 963 } 964 if(!isok(x, y)) { 965 nomul(0); 966 return; 967 } 968 if (((trap = t_at(x, y)) && trap->tseen) || 969 (Blind && !Levitation && !Flying && 970 !is_clinger(youmonst.data) && 971 (is_pool(x, y) || is_lava(x, y)) && levl[x][y].seenv)) { 972 if(flags.run >= 2) { 973 nomul(0); 974 flags.move = 0; 975 return; 976 } else 977 nomul(0); 978 } 979 980 if (u.ustuck && (x != u.ustuck->mx || y != u.ustuck->my)) { 981 if (distu(u.ustuck->mx, u.ustuck->my) > 2) { 982 /* perhaps it fled (or was teleported or ... ) */ 983 u.ustuck = 0; 984 } else if (sticks(youmonst.data)) { 985 /* When polymorphed into a sticking monster, 986 * u.ustuck means it's stuck to you, not you to it. 987 */ 988 You("release %s.", mon_nam(u.ustuck)); 989 u.ustuck = 0; 990 } else { 991 /* If holder is asleep or paralyzed: 992 * 37.5% chance of getting away, 993 * 12.5% chance of waking/releasing it; 994 * otherwise: 995 * 7.5% chance of getting away. 996 * [strength ought to be a factor] 997 * If holder is tame and there is no conflict, 998 * guaranteed escape. 999 */ 1000 switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) { 1001 case 0: case 1: case 2: 1002 pull_free: 1003 You("pull free from %s.", mon_nam(u.ustuck)); 1004 u.ustuck = 0; 1005 break; 1006 case 3: 1007 if (!u.ustuck->mcanmove) { 1008 /* it's free to move on next turn */ 1009 u.ustuck->mfrozen = 1; 1010 u.ustuck->msleeping = 0; 1011 } 1012 /*FALLTHRU*/ 1013 default: 1014 if (u.ustuck->mtame && 1015 !Conflict && !u.ustuck->mconf) 1016 goto pull_free; 1017 You("cannot escape from %s!", mon_nam(u.ustuck)); 1018 nomul(0); 1019 return; 1020 } 1021 } 1022 } 1023 1024 mtmp = m_at(x,y); 1025 if (mtmp) { 1026 /* Don't attack if you're running, and can see it */ 1027 /* We should never get here if forcefight */ 1028 if (flags.run && 1029 ((!Blind && mon_visible(mtmp) && 1030 ((mtmp->m_ap_type != M_AP_FURNITURE && 1031 mtmp->m_ap_type != M_AP_OBJECT) || 1032 Protection_from_shape_changers)) || 1033 sensemon(mtmp))) { 1034 nomul(0); 1035 flags.move = 0; 1036 return; 1037 } 1038 } 1039 } 1040 1041 u.ux0 = u.ux; 1042 u.uy0 = u.uy; 1043 bhitpos.x = x; 1044 bhitpos.y = y; 1045 tmpr = &levl[x][y]; 1046 1047 /* attack monster */ 1048 if(mtmp) { 1049 nomul(0); 1050 /* only attack if we know it's there */ 1051 /* or if we used the 'F' command to fight blindly */ 1052 /* or if it hides_under, in which case we call attack() to print 1053 * the Wait! message. 1054 * This is different from ceiling hiders, who aren't handled in 1055 * attack(). 1056 */ 1057 1058 /* If they used a 'm' command, trying to move onto a monster 1059 * prints the below message and wastes a turn. The exception is 1060 * if the monster is unseen and the player doesn't remember an 1061 * invisible monster--then, we fall through to attack() and 1062 * attack_check(), which still wastes a turn, but prints a 1063 * different message and makes the player remember the monster. */ 1064 if(flags.nopick && 1065 (canspotmon(mtmp) || glyph_is_invisible(levl[x][y].glyph))){ 1066 if(mtmp->m_ap_type && !Protection_from_shape_changers 1067 && !sensemon(mtmp)) 1068 stumble_onto_mimic(mtmp); 1069 else if (mtmp->mpeaceful && !Hallucination) 1070 pline("Pardon me, %s.", m_monnam(mtmp)); 1071 else 1072 You("move right into %s.", mon_nam(mtmp)); 1073 return; 1074 } 1075 if(flags.forcefight || !mtmp->mundetected || sensemon(mtmp) || 1076 ((hides_under(mtmp->data) || mtmp->data->mlet == S_EEL) && 1077 !is_safepet(mtmp))){ 1078 gethungry(); 1079 if(wtcap >= HVY_ENCUMBER && moves%3) { 1080 if (Upolyd && u.mh > 1) { 1081 u.mh--; 1082 } else if (!Upolyd && u.uhp > 1) { 1083 u.uhp--; 1084 } else { 1085 You("pass out from exertion!"); 1086 exercise(A_CON, FALSE); 1087 fall_asleep(-10, FALSE); 1088 } 1089 } 1090 if(multi < 0) return; /* we just fainted */ 1091 1092 /* try to attack; note that it might evade */ 1093 /* also, we don't attack tame when _safepet_ */ 1094 if(attack(mtmp)) return; 1095 } 1096 } 1097 1098 /* specifying 'F' with no monster wastes a turn */ 1099 if (flags.forcefight || 1100 /* remembered an 'I' && didn't use a move command */ 1101 (glyph_is_invisible(levl[x][y].glyph) && !flags.nopick)) { 1102 boolean expl = (Upolyd && attacktype(youmonst.data, AT_EXPL)); 1103 char buf[BUFSZ]; 1104 Sprintf(buf,"a vacant spot on the %s", surface(x,y)); 1105 You("%s %s.", 1106 expl ? "explode at" : "attack", 1107 !Underwater ? "thin air" : 1108 is_pool(x,y) ? "empty water" : buf); 1109 unmap_object(x, y); /* known empty -- remove 'I' if present */ 1110 newsym(x, y); 1111 nomul(0); 1112 if (expl) { 1113 u.mh = -1; /* dead in the current form */ 1114 rehumanize(); 1115 } 1116 return; 1117 } 1118 if (glyph_is_invisible(levl[x][y].glyph)) { 1119 unmap_object(x, y); 1120 newsym(x, y); 1121 } 1122 /* not attacking an animal, so we try to move */ 1123#ifdef STEED 1124 if (u.usteed && !u.usteed->mcanmove && (u.dx || u.dy)) { 1125 pline("%s won't move!", upstart(y_monnam(u.usteed))); 1126 nomul(0); 1127 return; 1128 } else 1129#endif 1130 if(!youmonst.data->mmove) { 1131 You("are rooted %s.", 1132 Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) ? 1133 "in place" : "to the ground"); 1134 nomul(0); 1135 return; 1136 } 1137 if(u.utrap) { 1138 if(u.utraptype == TT_PIT) { 1139 if (!rn2(2) && sobj_at(BOULDER, u.ux, u.uy)) { 1140 Your("%s gets stuck in a crevice.", body_part(LEG)); 1141 display_nhwindow(WIN_MESSAGE, FALSE); 1142 clear_nhwindow(WIN_MESSAGE); 1143 You("free your %s.", body_part(LEG)); 1144 } else if (!(--u.utrap)) { 1145 You("%s to the edge of the pit.", 1146 (In_sokoban(&u.uz) && Levitation) ? 1147 "struggle against the air currents and float" : 1148#ifdef STEED 1149 u.usteed ? "ride" : 1150#endif 1151 "crawl"); 1152 fill_pit(u.ux, u.uy); 1153 vision_full_recalc = 1; /* vision limits change */ 1154 } else if (flags.verbose) { 1155#ifdef STEED 1156 if (u.usteed) 1157 Norep("%s is still in a pit.", 1158 upstart(y_monnam(u.usteed))); 1159 else 1160#endif 1161 Norep( (Hallucination && !rn2(5)) ? 1162 "You've fallen, and you can't get up." : 1163 "You are still in a pit." ); 1164 } 1165 } else if (u.utraptype == TT_LAVA) { 1166 if(flags.verbose) { 1167 predicament = "stuck in the lava"; 1168#ifdef STEED 1169 if (u.usteed) 1170 Norep("%s is %s.", upstart(y_monnam(u.usteed)), 1171 predicament); 1172 else 1173#endif 1174 Norep("You are %s.", predicament); 1175 } 1176 if(!is_lava(x,y)) { 1177 u.utrap--; 1178 if((u.utrap & 0xff) == 0) { 1179#ifdef STEED 1180 if (u.usteed) 1181 You("lead %s to the edge of the lava.", 1182 y_monnam(u.usteed)); 1183 else 1184#endif 1185 You("pull yourself to the edge of the lava."); 1186 u.utrap = 0; 1187 } 1188 } 1189 u.umoved = TRUE; 1190 } else if (u.utraptype == TT_WEB) { 1191 if(uwep && uwep->oartifact == ART_STING) { 1192 u.utrap = 0; 1193 pline("Sting cuts through the web!"); 1194 return; 1195 } 1196 if(--u.utrap) { 1197 if(flags.verbose) { 1198 predicament = "stuck to the web"; 1199#ifdef STEED 1200 if (u.usteed) 1201 Norep("%s is %s.", upstart(y_monnam(u.usteed)), 1202 predicament); 1203 else 1204#endif 1205 Norep("You are %s.", predicament); 1206 } 1207 } else { 1208#ifdef STEED 1209 if (u.usteed) 1210 pline("%s breaks out of the web.", 1211 upstart(y_monnam(u.usteed))); 1212 else 1213#endif 1214 You("disentangle yourself."); 1215 } 1216 } else if (u.utraptype == TT_INFLOOR) { 1217 if(--u.utrap) { 1218 if(flags.verbose) { 1219 predicament = "stuck in the"; 1220#ifdef STEED 1221 if (u.usteed) 1222 Norep("%s is %s %s.", 1223 upstart(y_monnam(u.usteed)), 1224 predicament, surface(u.ux, u.uy)); 1225 else 1226#endif 1227 Norep("You are %s %s.", predicament, 1228 surface(u.ux, u.uy)); 1229 } 1230 } else { 1231#ifdef STEED 1232 if (u.usteed) 1233 pline("%s finally wiggles free.", 1234 upstart(y_monnam(u.usteed))); 1235 else 1236#endif 1237 You("finally wiggle free."); 1238 } 1239 } else { 1240 if(flags.verbose) { 1241 predicament = "caught in a bear trap"; 1242#ifdef STEED 1243 if (u.usteed) 1244 Norep("%s is %s.", upstart(y_monnam(u.usteed)), 1245 predicament); 1246 else 1247#endif 1248 Norep("You are %s.", predicament); 1249 } 1250 if((u.dx && u.dy) || !rn2(5)) u.utrap--; 1251 } 1252 return; 1253 } 1254 1255 if (!test_move(u.ux, u.uy, x-u.ux, y-u.uy, DO_MOVE)) { 1256 flags.move = 0; 1257 nomul(0); 1258 return; 1259 } 1260 1261 /* Move ball and chain. */ 1262 if (Punished) 1263 if (!drag_ball(x,y, &bc_control, &ballx, &bally, &chainx, &chainy, 1264 &cause_delay, TRUE)) 1265 return; 1266 1267 /* Check regions entering/leaving */ 1268 if (!in_out_region(x,y)) 1269 return; 1270 1271 /* now move the hero */ 1272 mtmp = m_at(x, y); 1273 u.ux += u.dx; 1274 u.uy += u.dy; 1275#ifdef STEED 1276 /* Move your steed, too */ 1277 if (u.usteed) { 1278 u.usteed->mx = u.ux; 1279 u.usteed->my = u.uy; 1280 exercise_steed(); 1281 } 1282#endif 1283 1284 /* 1285 * If safepet at destination then move the pet to the hero's 1286 * previous location using the same conditions as in attack(). 1287 * there are special extenuating circumstances: 1288 * (1) if the pet dies then your god angers, 1289 * (2) if the pet gets trapped then your god may disapprove, 1290 * (3) if the pet was already trapped and you attempt to free it 1291 * not only do you encounter the trap but you may frighten your 1292 * pet causing it to go wild! moral: don't abuse this privilege. 1293 * 1294 * Ceiling-hiding pets are skipped by this section of code, to 1295 * be caught by the normal falling-monster code. 1296 */ 1297 if (is_safepet(mtmp) && !(is_hider(mtmp->data) && mtmp->mundetected)) { 1298 /* if trapped, there's a chance the pet goes wild */ 1299 if (mtmp->mtrapped) { 1300 if (!rn2(mtmp->mtame)) { 1301 mtmp->mtame = mtmp->mpeaceful = mtmp->msleeping = 0; 1302 if (mtmp->mleashed) m_unleash(mtmp, TRUE); 1303 growl(mtmp); 1304 } else { 1305 yelp(mtmp); 1306 } 1307 } 1308 mtmp->mundetected = 0; 1309 if (mtmp->m_ap_type) seemimic(mtmp); 1310 else if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my); 1311 1312 if (mtmp->mtrapped && 1313 (trap = t_at(mtmp->mx, mtmp->my)) != 0 && 1314 (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT) && 1315 sobj_at(BOULDER, trap->tx, trap->ty)) { 1316 /* can't swap places with pet pinned in a pit by a boulder */ 1317 u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */ 1318 } else if (u.ux0 != x && u.uy0 != y && 1319 bad_rock(mtmp->data, x, u.uy0) && 1320 bad_rock(mtmp->data, u.ux0, y) && 1321 (bigmonst(mtmp->data) || (curr_mon_load(mtmp) > 600))) { 1322 /* can't swap places when pet won't fit thru the opening */ 1323 u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */ 1324 You("stop. %s won't fit through.", upstart(y_monnam(mtmp))); 1325 } else { 1326 char pnambuf[BUFSZ]; 1327 1328 /* save its current description in case of polymorph */ 1329 Strcpy(pnambuf, y_monnam(mtmp)); 1330 mtmp->mtrapped = 0; 1331 remove_monster(x, y); 1332 place_monster(mtmp, u.ux0, u.uy0); 1333 1334 /* check for displacing it into pools and traps */ 1335 switch (minliquid(mtmp) ? 2 : mintrap(mtmp)) { 1336 case 0: 1337 You("%s %s.", mtmp->mtame ? "displaced" : "frightened", 1338 pnambuf); 1339 break; 1340 case 1: /* trapped */ 1341 case 3: /* changed levels */ 1342 /* there's already been a trap message, reinforce it */ 1343 abuse_dog(mtmp); 1344 adjalign(-3); 1345 break; 1346 case 2: 1347 /* it may have drowned or died. that's no way to 1348 * treat a pet! your god gets angry. 1349 */ 1350 if (rn2(4)) { 1351 You_feel("guilty about losing your pet like this."); 1352 u.ugangr++; 1353 adjalign(-15); 1354 } 1355 1356 /* you killed your pet by direct action. 1357 * minliquid and mintrap don't know to do this 1358 */ 1359 u.uconduct.killer++; 1360 break; 1361 default: 1362 pline("that's strange, unknown mintrap result!"); 1363 break; 1364 } 1365 } 1366 } 1367 1368 reset_occupations(); 1369 if (flags.run) { 1370 if ( flags.run < 8 ) 1371 if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ) || 1372 IS_FURNITURE(tmpr->typ)) 1373 nomul(0); 1374 } 1375 1376 if (hides_under(youmonst.data)) 1377 u.uundetected = OBJ_AT(u.ux, u.uy); 1378 else if (youmonst.data->mlet == S_EEL) 1379 u.uundetected = is_pool(u.ux, u.uy) && !Is_waterlevel(&u.uz); 1380 else if (u.dx || u.dy) 1381 u.uundetected = 0; 1382 1383 /* 1384 * Mimics (or whatever) become noticeable if they move and are 1385 * imitating something that doesn't move. We could extend this 1386 * to non-moving monsters... 1387 */ 1388 if ((u.dx || u.dy) && (youmonst.m_ap_type == M_AP_OBJECT 1389 || youmonst.m_ap_type == M_AP_FURNITURE)) 1390 youmonst.m_ap_type = M_AP_NOTHING; 1391 1392 check_leash(u.ux0,u.uy0); 1393 1394 if(u.ux0 != u.ux || u.uy0 != u.uy) { 1395 u.umoved = TRUE; 1396 /* Clean old position -- vision_recalc() will print our new one. */ 1397 newsym(u.ux0,u.uy0); 1398 /* Since the hero has moved, adjust what can be seen/unseen. */ 1399 vision_recalc(1); /* Do the work now in the recover time. */ 1400 invocation_message(); 1401 } 1402 1403 if (Punished) /* put back ball and chain */ 1404 move_bc(0,bc_control,ballx,bally,chainx,chainy); 1405 1406 spoteffects(TRUE); 1407 1408 /* delay next move because of ball dragging */ 1409 /* must come after we finished picking up, in spoteffects() */ 1410 if (cause_delay) { 1411 nomul(-2); 1412 nomovemsg = ""; 1413 } 1414 1415 if (flags.run && iflags.runmode != RUN_TPORT) { 1416 /* display every step or every 7th step depending upon mode */ 1417 if (iflags.runmode != RUN_LEAP || !(moves % 7L)) { 1418 if (flags.time) flags.botl = 1; 1419 curs_on_u(); 1420 delay_output(); 1421 if (iflags.runmode == RUN_CRAWL) { 1422 delay_output(); 1423 delay_output(); 1424 delay_output(); 1425 delay_output(); 1426 } 1427 } 1428 } 1429} 1430 1431void 1432invocation_message() 1433{ 1434 /* a special clue-msg when on the Invocation position */ 1435 if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) { 1436 char buf[BUFSZ]; 1437 struct obj *otmp = carrying(CANDELABRUM_OF_INVOCATION); 1438 1439 nomul(0); /* stop running or travelling */ 1440#ifdef STEED 1441 if (u.usteed) Sprintf(buf, "beneath %s", y_monnam(u.usteed)); 1442 else 1443#endif 1444 if (Levitation || Flying) Strcpy(buf, "beneath you"); 1445 else Sprintf(buf, "under your %s", makeplural(body_part(FOOT))); 1446 1447 You_feel("a strange vibration %s.", buf); 1448 if (otmp && otmp->spe == 7 && otmp->lamplit) 1449 pline("%s %s!", The(xname(otmp)), 1450 Blind ? "throbs palpably" : "glows with a strange light"); 1451 } 1452} 1453 1454#endif /* OVL3 */ 1455#ifdef OVL2 1456 1457void 1458spoteffects(pick) 1459boolean pick; 1460{ 1461 register struct monst *mtmp; 1462 1463 if(u.uinwater) { 1464 int was_underwater; 1465 1466 if (!is_pool(u.ux,u.uy)) { 1467 if (Is_waterlevel(&u.uz)) 1468 You("pop into an air bubble."); 1469 else if (is_lava(u.ux, u.uy)) 1470 You("leave the water..."); /* oops! */ 1471 else 1472 You("are on solid %s again.", 1473 is_ice(u.ux, u.uy) ? "ice" : "land"); 1474 } 1475 else if (Is_waterlevel(&u.uz)) 1476 goto stillinwater; 1477 else if (Levitation) 1478 You("pop out of the water like a cork!"); 1479 else if (Flying) 1480 You("fly out of the water."); 1481 else if (Wwalking) 1482 You("slowly rise above the surface."); 1483 else 1484 goto stillinwater; 1485 was_underwater = Underwater && !Is_waterlevel(&u.uz); 1486 u.uinwater = 0; /* leave the water */ 1487 if (was_underwater) { /* restore vision */ 1488 docrt(); 1489 vision_full_recalc = 1; 1490 } 1491 } 1492stillinwater:; 1493 if (!Levitation && !u.ustuck && !Flying) { 1494 /* limit recursive calls through teleds() */ 1495 if (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) { 1496#ifdef STEED 1497 if (u.usteed && !is_flyer(u.usteed->data) && 1498 !is_floater(u.usteed->data) && 1499 !is_clinger(u.usteed->data)) { 1500 dismount_steed(Underwater ? 1501 DISMOUNT_FELL : DISMOUNT_GENERIC); 1502 /* dismount_steed() -> float_down() -> pickup() */ 1503 if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) 1504 pick = FALSE; 1505 } else 1506#endif 1507 if (is_lava(u.ux, u.uy)) { 1508 if (lava_effects()) return; 1509 } else if (!Wwalking && drown()) 1510 return; 1511 } 1512 } 1513 check_special_room(FALSE); 1514#ifdef SINKS 1515 if(IS_SINK(levl[u.ux][u.uy].typ) && Levitation) 1516 dosinkfall(); 1517#endif 1518 if (!in_steed_dismounting) { /* if dismounting, we'll check again later */ 1519 struct trap *trap = t_at(u.ux, u.uy); 1520 boolean pit; 1521 pit = (trap && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)); 1522 if (trap && pit) 1523 dotrap(trap, 0); /* fall into pit */ 1524 if (pick) (void) pickup(1); 1525 if (trap && !pit) 1526 dotrap(trap, 0); /* fall into arrow trap, etc. */ 1527 } 1528 if((mtmp = m_at(u.ux, u.uy)) && !u.uswallow) { 1529 mtmp->mundetected = mtmp->msleeping = 0; 1530 switch(mtmp->data->mlet) { 1531 case S_PIERCER: 1532 pline("%s suddenly drops from the %s!", 1533 Amonnam(mtmp), ceiling(u.ux,u.uy)); 1534 if(mtmp->mtame) /* jumps to greet you, not attack */ 1535 ; 1536 else if(uarmh && is_metallic(uarmh)) 1537 pline("Its blow glances off your helmet."); 1538 else if (u.uac + 3 <= rnd(20)) 1539 You("are almost hit by %s!", 1540 x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE)); 1541 else { 1542 int dmg; 1543 You("are hit by %s!", 1544 x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE)); 1545 dmg = d(4,6); 1546 if(Half_physical_damage) dmg = (dmg+1) / 2; 1547 mdamageu(mtmp, dmg); 1548 } 1549 break; 1550 default: /* monster surprises you. */ 1551 if(mtmp->mtame) 1552 pline("%s jumps near you from the %s.", 1553 Amonnam(mtmp), ceiling(u.ux,u.uy)); 1554 else if(mtmp->mpeaceful) { 1555 You("surprise %s!", 1556 Blind && !sensemon(mtmp) ? 1557 something : a_monnam(mtmp)); 1558 mtmp->mpeaceful = 0; 1559 } else 1560 pline("%s attacks you by surprise!", 1561 Amonnam(mtmp)); 1562 break; 1563 } 1564 mnexto(mtmp); /* have to move the monster */ 1565 } 1566} 1567 1568STATIC_OVL boolean 1569monstinroom(mdat,roomno) 1570struct permonst *mdat; 1571int roomno; 1572{ 1573 register struct monst *mtmp; 1574 1575 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) 1576 if(!DEADMONSTER(mtmp) && mtmp->data == mdat && 1577 index(in_rooms(mtmp->mx, mtmp->my, 0), roomno + ROOMOFFSET)) 1578 return(TRUE); 1579 return(FALSE); 1580} 1581 1582char * 1583in_rooms(x, y, typewanted) 1584register xchar x, y; 1585register int typewanted; 1586{ 1587 static char buf[5]; 1588 char rno, *ptr = &buf[4]; 1589 int typefound, min_x, min_y, max_x, max_y_offset, step; 1590 register struct rm *lev; 1591 1592#define goodtype(rno) (!typewanted || \ 1593 ((typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted) || \ 1594 ((typewanted == SHOPBASE) && (typefound > SHOPBASE))) \ 1595 1596 switch (rno = levl[x][y].roomno) { 1597 case NO_ROOM: 1598 return(ptr); 1599 case SHARED: 1600 step = 2; 1601 break; 1602 case SHARED_PLUS: 1603 step = 1; 1604 break; 1605 default: /* i.e. a regular room # */ 1606 if (goodtype(rno)) 1607 *(--ptr) = rno; 1608 return(ptr); 1609 } 1610 1611 min_x = x - 1; 1612 max_x = x + 1; 1613 if (x < 1) 1614 min_x += step; 1615 else 1616 if (x >= COLNO) 1617 max_x -= step; 1618 1619 min_y = y - 1; 1620 max_y_offset = 2; 1621 if (min_y < 0) { 1622 min_y += step; 1623 max_y_offset -= step; 1624 } else 1625 if ((min_y + max_y_offset) >= ROWNO) 1626 max_y_offset -= step; 1627 1628 for (x = min_x; x <= max_x; x += step) { 1629 lev = &levl[x][min_y]; 1630 y = 0; 1631 if (((rno = lev[y].roomno) >= ROOMOFFSET) && 1632 !index(ptr, rno) && goodtype(rno)) 1633 *(--ptr) = rno; 1634 y += step; 1635 if (y > max_y_offset) 1636 continue; 1637 if (((rno = lev[y].roomno) >= ROOMOFFSET) && 1638 !index(ptr, rno) && goodtype(rno)) 1639 *(--ptr) = rno; 1640 y += step; 1641 if (y > max_y_offset) 1642 continue; 1643 if (((rno = lev[y].roomno) >= ROOMOFFSET) && 1644 !index(ptr, rno) && goodtype(rno)) 1645 *(--ptr) = rno; 1646 } 1647 return(ptr); 1648} 1649 1650/* is (x,y) in a town? */ 1651boolean 1652in_town(x, y) 1653register int x, y; 1654{ 1655 s_level *slev = Is_special(&u.uz); 1656 register struct mkroom *sroom; 1657 boolean has_subrooms = FALSE; 1658 1659 if (!slev || !slev->flags.town) return FALSE; 1660 1661 /* 1662 * See if (x,y) is in a room with subrooms, if so, assume it's the 1663 * town. If there are no subrooms, the whole level is in town. 1664 */ 1665 for (sroom = &rooms[0]; sroom->hx > 0; sroom++) { 1666 if (sroom->nsubrooms > 0) { 1667 has_subrooms = TRUE; 1668 if (inside_room(sroom, x, y)) return TRUE; 1669 } 1670 } 1671 1672 return !has_subrooms; 1673} 1674 1675STATIC_OVL void 1676move_update(newlev) 1677register boolean newlev; 1678{ 1679 char *ptr1, *ptr2, *ptr3, *ptr4; 1680 1681 Strcpy(u.urooms0, u.urooms); 1682 Strcpy(u.ushops0, u.ushops); 1683 if (newlev) { 1684 u.urooms[0] = '\0'; 1685 u.uentered[0] = '\0'; 1686 u.ushops[0] = '\0'; 1687 u.ushops_entered[0] = '\0'; 1688 Strcpy(u.ushops_left, u.ushops0); 1689 return; 1690 } 1691 Strcpy(u.urooms, in_rooms(u.ux, u.uy, 0)); 1692 1693 for (ptr1 = &u.urooms[0], 1694 ptr2 = &u.uentered[0], 1695 ptr3 = &u.ushops[0], 1696 ptr4 = &u.ushops_entered[0]; 1697 *ptr1; ptr1++) { 1698 if (!index(u.urooms0, *ptr1)) 1699 *(ptr2++) = *ptr1; 1700 if (IS_SHOP(*ptr1 - ROOMOFFSET)) { 1701 *(ptr3++) = *ptr1; 1702 if (!index(u.ushops0, *ptr1)) 1703 *(ptr4++) = *ptr1; 1704 } 1705 } 1706 *ptr2 = '\0'; 1707 *ptr3 = '\0'; 1708 *ptr4 = '\0'; 1709 1710 /* filter u.ushops0 -> u.ushops_left */ 1711 for (ptr1 = &u.ushops0[0], ptr2 = &u.ushops_left[0]; *ptr1; ptr1++) 1712 if (!index(u.ushops, *ptr1)) 1713 *(ptr2++) = *ptr1; 1714 *ptr2 = '\0'; 1715} 1716 1717void 1718check_special_room(newlev) 1719register boolean newlev; 1720{ 1721 register struct monst *mtmp; 1722 char *ptr; 1723 1724 move_update(newlev); 1725 1726 if (*u.ushops0) 1727 u_left_shop(u.ushops_left, newlev); 1728 1729 if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */ 1730 return; /* no entrance messages necessary */ 1731 1732 /* Did we just enter a shop? */ 1733 if (*u.ushops_entered) 1734 u_entered_shop(u.ushops_entered); 1735 1736 for (ptr = &u.uentered[0]; *ptr; ptr++) { 1737 register int roomno = *ptr - ROOMOFFSET, rt = rooms[roomno].rtype; 1738 1739 /* Did we just enter some other special room? */ 1740 /* vault.c insists that a vault remain a VAULT, 1741 * and temples should remain TEMPLEs, 1742 * but everything else gives a message only the first time */ 1743 switch (rt) { 1744 case ZOO: 1745 pline("Welcome to David's treasure zoo!"); 1746 break; 1747 case SWAMP: 1748 pline("It %s rather %s down here.", 1749 Blind ? "feels" : "looks", 1750 Blind ? "humid" : "muddy"); 1751 break; 1752 case COURT: 1753 You("enter an opulent throne room!"); 1754 break; 1755 case LEPREHALL: 1756 You("enter a leprechaun hall!"); 1757 break; 1758 case MORGUE: 1759 if(midnight()) { 1760 const char *run = locomotion(youmonst.data, "Run"); 1761 pline("%s away! %s away!", run, run); 1762 } else 1763 You("have an uncanny feeling..."); 1764 break; 1765 case BEEHIVE: 1766 You("enter a giant beehive!"); 1767 break; 1768 case COCKNEST: 1769 You("enter a disgusting nest!"); 1770 break; 1771 case ANTHOLE: 1772 You("enter an anthole!"); 1773 break; 1774 case BARRACKS: 1775 if(monstinroom(&mons[PM_SOLDIER], roomno) || 1776 monstinroom(&mons[PM_SERGEANT], roomno) || 1777 monstinroom(&mons[PM_LIEUTENANT], roomno) || 1778 monstinroom(&mons[PM_CAPTAIN], roomno)) 1779 You("enter a military barracks!"); 1780 else 1781 You("enter an abandoned barracks."); 1782 break; 1783 case DELPHI: 1784 if(monstinroom(&mons[PM_ORACLE], roomno)) 1785 verbalize("%s, %s, welcome to Delphi!", 1786 Hello((struct monst *) 0), plname); 1787 break; 1788 case TEMPLE: 1789 intemple(roomno + ROOMOFFSET); 1790 /* fall through */ 1791 default: 1792 rt = 0; 1793 } 1794 1795 if (rt != 0) { 1796 rooms[roomno].rtype = OROOM; 1797 if (!search_special(rt)) { 1798 /* No more room of that type */ 1799 switch(rt) { 1800 case COURT: 1801 level.flags.has_court = 0; 1802 break; 1803 case SWAMP: 1804 level.flags.has_swamp = 0; 1805 break; 1806 case MORGUE: 1807 level.flags.has_morgue = 0; 1808 break; 1809 case ZOO: 1810 level.flags.has_zoo = 0; 1811 break; 1812 case BARRACKS: 1813 level.flags.has_barracks = 0; 1814 break; 1815 case TEMPLE: 1816 level.flags.has_temple = 0; 1817 break; 1818 case BEEHIVE: 1819 level.flags.has_beehive = 0; 1820 break; 1821 } 1822 } 1823 if (rt == COURT || rt == SWAMP || rt == MORGUE || rt == ZOO) 1824 for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) 1825 if (!DEADMONSTER(mtmp) && !Stealth && !rn2(3)) mtmp->msleeping = 0; 1826 } 1827 } 1828 1829 return; 1830} 1831 1832#endif /* OVL2 */ 1833#ifdef OVLB 1834 1835int 1836dopickup() 1837{ 1838 int count; 1839 struct trap *traphere = t_at(u.ux, u.uy); 1840 /* awful kludge to work around parse()'s pre-decrement */ 1841 count = (multi || (save_cm && *save_cm == ',')) ? multi + 1 : 0; 1842 multi = 0; /* always reset */ 1843 /* uswallow case added by GAN 01/29/87 */ 1844 if(u.uswallow) { 1845 if (!u.ustuck->minvent) { 1846 if (is_animal(u.ustuck->data)) { 1847 You("pick up %s tongue.", s_suffix(mon_nam(u.ustuck))); 1848 pline("But it's kind of slimy, so you drop it."); 1849 } else 1850 You("don't %s anything in here to pick up.", 1851 Blind ? "feel" : "see"); 1852 return(1); 1853 } else { 1854 int tmpcount = -count; 1855 return loot_mon(u.ustuck, &tmpcount, (boolean *)0); 1856 } 1857 } 1858 if(is_pool(u.ux, u.uy)) { 1859 if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data) 1860 || (Flying && !Breathless)) { 1861 You("cannot dive into the water to pick things up."); 1862 return(0); 1863 } else if (!Underwater) { 1864 You_cant("even see the bottom, let alone pick up %s.", 1865 something); 1866 return(0); 1867 } 1868 } 1869 if (is_lava(u.ux, u.uy)) { 1870 if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data) 1871 || (Flying && !Breathless)) { 1872 You_cant("reach the bottom to pick things up."); 1873 return(0); 1874 } else if (!likes_lava(youmonst.data)) { 1875 You("would burn to a crisp trying to pick things up."); 1876 return(0); 1877 } 1878 } 1879 if(!OBJ_AT(u.ux, u.uy)) { 1880 There("is nothing here to pick up."); 1881 return(0); 1882 } 1883 if (!can_reach_floor()) { 1884#ifdef STEED 1885 if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) 1886 You("aren't skilled enough to reach from %s.", 1887 y_monnam(u.usteed)); 1888 else 1889#endif 1890 You("cannot reach the %s.", surface(u.ux,u.uy)); 1891 return(0); 1892 } 1893 1894 if (traphere && traphere->tseen) { 1895 /* Allow pickup from holes and trap doors that you escaped from 1896 * because that stuff is teetering on the edge just like you, but 1897 * not pits, because there is an elevation discrepancy with stuff 1898 * in pits. 1899 */ 1900 if ((traphere->ttyp == PIT || traphere->ttyp == SPIKED_PIT) && 1901 (!u.utrap || (u.utrap && u.utraptype != TT_PIT))) { 1902 You("cannot reach the bottom of the pit."); 1903 return(0); 1904 } 1905 } 1906 1907 return (pickup(-count)); 1908} 1909 1910#endif /* OVLB */ 1911#ifdef OVL2 1912 1913/* stop running if we see something interesting */ 1914/* turn around a corner if that is the only way we can proceed */ 1915/* do not turn left or right twice */ 1916void 1917lookaround() 1918{ 1919 register int x, y, i, x0 = 0, y0 = 0, m0 = 1, i0 = 9; 1920 register int corrct = 0, noturn = 0; 1921 register struct monst *mtmp; 1922 register struct trap *trap; 1923 1924 /* Grid bugs stop if trying to move diagonal, even if blind. Maybe */ 1925 /* they polymorphed while in the middle of a long move. */ 1926 if (u.umonnum == PM_GRID_BUG && u.dx && u.dy) { 1927 nomul(0); 1928 return; 1929 } 1930 1931 if(Blind || flags.run == 0) return; 1932 for(x = u.ux-1; x <= u.ux+1; x++) for(y = u.uy-1; y <= u.uy+1; y++) { 1933 if(!isok(x,y)) continue; 1934 1935 if(u.umonnum == PM_GRID_BUG && x != u.ux && y != u.uy) continue; 1936 1937 if(x == u.ux && y == u.uy) continue; 1938 1939 if((mtmp = m_at(x,y)) && 1940 mtmp->m_ap_type != M_AP_FURNITURE && 1941 mtmp->m_ap_type != M_AP_OBJECT && 1942 (!mtmp->minvis || See_invisible) && !mtmp->mundetected) { 1943 if((flags.run != 1 && !mtmp->mtame) 1944 || (x == u.ux+u.dx && y == u.uy+u.dy)) 1945 goto stop; 1946 } 1947 1948 if (levl[x][y].typ == STONE) continue; 1949 if (x == u.ux-u.dx && y == u.uy-u.dy) continue; 1950 1951 if (IS_ROCK(levl[x][y].typ) || (levl[x][y].typ == ROOM) || 1952 IS_AIR(levl[x][y].typ)) 1953 continue; 1954 else if (closed_door(x,y) || 1955 (mtmp && mtmp->m_ap_type == M_AP_FURNITURE && 1956 (mtmp->mappearance == S_hcdoor || 1957 mtmp->mappearance == S_vcdoor))) { 1958 if(x != u.ux && y != u.uy) continue; 1959 if(flags.run != 1) goto stop; 1960 goto bcorr; 1961 } else if (levl[x][y].typ == CORR) { 1962bcorr: 1963 if(levl[u.ux][u.uy].typ != ROOM) { 1964 if(flags.run == 1 || flags.run == 3 || flags.run == 8) { 1965 i = dist2(x,y,u.ux+u.dx,u.uy+u.dy); 1966 if(i > 2) continue; 1967 if(corrct == 1 && dist2(x,y,x0,y0) != 1) 1968 noturn = 1; 1969 if(i < i0) { 1970 i0 = i; 1971 x0 = x; 1972 y0 = y; 1973 m0 = mtmp ? 1 : 0; 1974 } 1975 } 1976 corrct++; 1977 } 1978 continue; 1979 } else if ((trap = t_at(x,y)) && trap->tseen) { 1980 if(flags.run == 1) goto bcorr; /* if you must */ 1981 if(x == u.ux+u.dx && y == u.uy+u.dy) goto stop; 1982 continue; 1983 } else if (is_pool(x,y) || is_lava(x,y)) { 1984 /* water and lava only stop you if directly in front, and stop 1985 * you even if you are running 1986 */ 1987 if(!Levitation && !Flying && !is_clinger(youmonst.data) && 1988 x == u.ux+u.dx && y == u.uy+u.dy) 1989 /* No Wwalking check; otherwise they'd be able 1990 * to test boots by trying to SHIFT-direction 1991 * into a pool and seeing if the game allowed it 1992 */ 1993 goto stop; 1994 continue; 1995 } else { /* e.g. objects or trap or stairs */ 1996 if(flags.run == 1) goto bcorr; 1997 if(flags.run == 8) continue; 1998 if(mtmp) continue; /* d */ 1999 if(((x == u.ux - u.dx) && (y != u.uy + u.dy)) || 2000 ((y == u.uy - u.dy) && (x != u.ux + u.dx))) 2001 continue; 2002 } 2003stop: 2004 nomul(0); 2005 return; 2006 } /* end for loops */ 2007 2008 if(corrct > 1 && flags.run == 2) goto stop; 2009 if((flags.run == 1 || flags.run == 3 || flags.run == 8) && 2010 !noturn && !m0 && i0 && (corrct == 1 || (corrct == 2 && i0 == 1))) 2011 { 2012 /* make sure that we do not turn too far */ 2013 if(i0 == 2) { 2014 if(u.dx == y0-u.uy && u.dy == u.ux-x0) 2015 i = 2; /* straight turn right */ 2016 else 2017 i = -2; /* straight turn left */ 2018 } else if(u.dx && u.dy) { 2019 if((u.dx == u.dy && y0 == u.uy) || (u.dx != u.dy && y0 != u.uy)) 2020 i = -1; /* half turn left */ 2021 else 2022 i = 1; /* half turn right */ 2023 } else { 2024 if((x0-u.ux == y0-u.uy && !u.dy) || (x0-u.ux != y0-u.uy && u.dy)) 2025 i = 1; /* half turn right */ 2026 else 2027 i = -1; /* half turn left */ 2028 } 2029 2030 i += u.last_str_turn; 2031 if(i <= 2 && i >= -2) { 2032 u.last_str_turn = i; 2033 u.dx = x0-u.ux; 2034 u.dy = y0-u.uy; 2035 } 2036 } 2037} 2038 2039/* something like lookaround, but we are not running */ 2040/* react only to monsters that might hit us */ 2041int 2042monster_nearby() 2043{ 2044 register int x,y; 2045 register struct monst *mtmp; 2046 2047 /* Also see the similar check in dochugw() in monmove.c */ 2048 for(x = u.ux-1; x <= u.ux+1; x++) 2049 for(y = u.uy-1; y <= u.uy+1; y++) { 2050 if(!isok(x,y)) continue; 2051 if(x == u.ux && y == u.uy) continue; 2052 if((mtmp = m_at(x,y)) && 2053 mtmp->m_ap_type != M_AP_FURNITURE && 2054 mtmp->m_ap_type != M_AP_OBJECT && 2055 (!mtmp->mpeaceful || Hallucination) && 2056 (!is_hider(mtmp->data) || !mtmp->mundetected) && 2057 !noattacks(mtmp->data) && 2058 mtmp->mcanmove && !mtmp->msleeping && /* aplvax!jcn */ 2059 !onscary(u.ux, u.uy, mtmp) && 2060 canspotmon(mtmp)) 2061 return(1); 2062 } 2063 return(0); 2064} 2065 2066void 2067nomul(nval) 2068 register int nval; 2069{ 2070 if(multi < nval) return; /* This is a bug fix by ab@unido */ 2071 u.uinvulnerable = FALSE; /* Kludge to avoid ctrl-C bug -dlc */ 2072 u.usleep = 0; 2073 multi = nval; 2074 flags.travel = iflags.travel1 = flags.mv = flags.run = 0; 2075} 2076 2077/* called when a non-movement, multi-turn action has completed */ 2078void 2079unmul(msg_override) 2080const char *msg_override; 2081{ 2082 multi = 0; /* caller will usually have done this already */ 2083 if (msg_override) nomovemsg = msg_override; 2084 else if (!nomovemsg) nomovemsg = You_can_move_again; 2085 if (*nomovemsg) pline(nomovemsg); 2086 nomovemsg = 0; 2087 u.usleep = 0; 2088 if (afternmv) (*afternmv)(); 2089 afternmv = 0; 2090} 2091 2092#endif /* OVL2 */ 2093#ifdef OVL1 2094 2095STATIC_OVL void 2096maybe_wail() 2097{ 2098 static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES, 2099 SHOCK_RES, FIRE_RES, SLEEP_RES, DISINT_RES, 2100 TELEPORT_CONTROL, STEALTH, FAST, INVIS }; 2101 2102 if (moves <= wailmsg + 50) return; 2103 2104 wailmsg = moves; 2105 if (Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)) { 2106 const char *who; 2107 int i, powercnt; 2108 2109 who = (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ? 2110 urole.name.m : "Elf"; 2111 if (u.uhp == 1) { 2112 pline("%s is about to die.", who); 2113 } else { 2114 for (i = 0, powercnt = 0; i < SIZE(powers); ++i) 2115 if (u.uprops[powers[i]].intrinsic & INTRINSIC) ++powercnt; 2116 2117 pline(powercnt >= 4 ? "%s, all your powers will be lost..." 2118 : "%s, your life force is running out.", who); 2119 } 2120 } else { 2121 You_hear(u.uhp == 1 ? "the wailing of the Banshee..." 2122 : "the howling of the CwnAnnwn..."); 2123 } 2124} 2125 2126void 2127losehp(n, knam, k_format) 2128register int n; 2129register const char *knam; 2130boolean k_format; 2131{ 2132 if (Upolyd) { 2133 u.mh -= n; 2134 if (u.mhmax < u.mh) u.mhmax = u.mh; 2135 flags.botl = 1; 2136 if (u.mh < 1) 2137 rehumanize(); 2138 else if (n > 0 && u.mh*10 < u.mhmax && Unchanging) 2139 maybe_wail(); 2140 return; 2141 } 2142 2143 u.uhp -= n; 2144 if(u.uhp > u.uhpmax) 2145 u.uhpmax = u.uhp; /* perhaps n was negative */ 2146 flags.botl = 1; 2147 if(u.uhp < 1) { 2148 killer_format = k_format; 2149 killer = knam; /* the thing that killed you */ 2150 You("die..."); 2151 done(DIED); 2152 } else if (n > 0 && u.uhp*10 < u.uhpmax) { 2153 maybe_wail(); 2154 } 2155} 2156 2157int 2158weight_cap() 2159{ 2160 register long carrcap; 2161 2162 carrcap = 25*(ACURRSTR + ACURR(A_CON)) + 50; 2163 if (Upolyd) { 2164 /* consistent with can_carry() in mon.c */ 2165 if (youmonst.data->mlet == S_NYMPH) 2166 carrcap = MAX_CARR_CAP; 2167 else if (!youmonst.data->cwt) 2168 carrcap = (carrcap * (long)youmonst.data->msize) / MZ_HUMAN; 2169 else if (!strongmonst(youmonst.data) 2170 || (strongmonst(youmonst.data) && (youmonst.data->cwt > WT_HUMAN))) 2171 carrcap = (carrcap * (long)youmonst.data->cwt / WT_HUMAN); 2172 } 2173 2174 if (Levitation || Is_airlevel(&u.uz) /* pugh@cornell */ 2175#ifdef STEED 2176 || (u.usteed && strongmonst(u.usteed->data)) 2177#endif 2178 ) 2179 carrcap = MAX_CARR_CAP; 2180 else { 2181 if(carrcap > MAX_CARR_CAP) carrcap = MAX_CARR_CAP; 2182 if (!Flying) { 2183 if(EWounded_legs & LEFT_SIDE) carrcap -= 100; 2184 if(EWounded_legs & RIGHT_SIDE) carrcap -= 100; 2185 } 2186 if (carrcap < 0) carrcap = 0; 2187 } 2188 return((int) carrcap); 2189} 2190 2191static int wc; /* current weight_cap(); valid after call to inv_weight() */ 2192 2193/* returns how far beyond the normal capacity the player is currently. */ 2194/* inv_weight() is negative if the player is below normal capacity. */ 2195int 2196inv_weight() 2197{ 2198 register struct obj *otmp = invent; 2199 register int wt = 0; 2200 2201#ifndef GOLDOBJ 2202 /* when putting stuff into containers, gold is inserted at the head 2203 of invent for easier manipulation by askchain & co, but it's also 2204 retained in u.ugold in order to keep the status line accurate; we 2205 mustn't add its weight in twice under that circumstance */ 2206 wt = (otmp && otmp->oclass == COIN_CLASS) ? 0 : 2207 (int)((u.ugold + 50L) / 100L); 2208#endif 2209 while (otmp) { 2210#ifndef GOLDOBJ 2211 if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data)) 2212#else 2213 if (otmp->oclass == COIN_CLASS) 2214 wt += (int)(((long)otmp->quan + 50L) / 100L); 2215 else if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data)) 2216#endif 2217 wt += otmp->owt; 2218 otmp = otmp->nobj; 2219 } 2220 wc = weight_cap(); 2221 return (wt - wc); 2222} 2223 2224/* 2225 * Returns 0 if below normal capacity, or the number of "capacity units" 2226 * over the normal capacity the player is loaded. Max is 5. 2227 */ 2228int 2229calc_capacity(xtra_wt) 2230int xtra_wt; 2231{ 2232 int cap, wt = inv_weight() + xtra_wt; 2233 2234 if (wt <= 0) return UNENCUMBERED; 2235 if (wc <= 1) return OVERLOADED; 2236 cap = (wt*2 / wc) + 1; 2237 return min(cap, OVERLOADED); 2238} 2239 2240int 2241near_capacity() 2242{ 2243 return calc_capacity(0); 2244} 2245 2246int 2247max_capacity() 2248{ 2249 int wt = inv_weight(); 2250 2251 return (wt - (2 * wc)); 2252} 2253 2254boolean 2255check_capacity(str) 2256const char *str; 2257{ 2258 if(near_capacity() >= EXT_ENCUMBER) { 2259 if(str) 2260 pline(str); 2261 else 2262 You_cant("do that while carrying so much stuff."); 2263 return 1; 2264 } 2265 return 0; 2266} 2267 2268#endif /* OVL1 */ 2269#ifdef OVLB 2270 2271int 2272inv_cnt() 2273{ 2274 register struct obj *otmp = invent; 2275 register int ct = 0; 2276 2277 while(otmp){ 2278 ct++; 2279 otmp = otmp->nobj; 2280 } 2281 return(ct); 2282} 2283 2284#ifdef GOLDOBJ 2285/* Counts the money in an object chain. */ 2286/* Intended use is for your or some monsters inventory, */ 2287/* now that u.gold/m.gold is gone.*/ 2288/* Counting money in a container might be possible too. */ 2289long 2290money_cnt(otmp) 2291struct obj *otmp; 2292{ 2293 while(otmp) { 2294 /* Must change when silver & copper is implemented: */ 2295 if (otmp->oclass == COIN_CLASS) return otmp->quan; 2296 otmp = otmp->nobj; 2297 } 2298 return 0; 2299} 2300#endif 2301#endif /* OVLB */ 2302 2303/*hack.c*/ 2304