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