1 | /* SCCS Id: @(#)lock.c 3.3 2000/02/06 */ 2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3 | /* NetHack may be freely redistributed. See license for details. */ 4 | 5 | #include "hack.h" 6 | 7 | STATIC_PTR int NDECL(picklock); 8 | STATIC_PTR int NDECL(forcelock); 9 | 10 | /* at most one of `door' and `box' should be non-null at any given time */ 11 | STATIC_VAR NEARDATA struct xlock_s { 12 | struct rm *door; 13 | struct obj *box; 14 | int picktyp, chance, usedtime; 15 | } xlock; 16 | 17 | #ifdef OVLB 18 | 19 | STATIC_DCL const char *NDECL(lock_action); 20 | STATIC_DCL boolean FDECL(obstructed,(int,int)); 21 | STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *)); 22 | 23 | boolean 24 | picking_lock(x, y) 25 | int *x, *y; 26 | { 27 | if (occupation == picklock) { 28 | *x = u.ux + u.dx; 29 | *y = u.uy + u.dy; 30 | return TRUE; 31 | } else { 32 | *x = *y = 0; 33 | return FALSE; 34 | } 35 | } 36 | 37 | boolean 38 | picking_at(x, y) 39 | int x, y; 40 | { 41 | return (boolean)(occupation == picklock && xlock.door == &levl[x][y]); 42 | } 43 | 44 | /* produce an occupation string appropriate for the current activity */ 45 | STATIC_OVL const char * 46 | lock_action() 47 | { 48 | /* "unlocking"+2 == "locking" */ 49 | static const char *actions[] = { 50 | /* [0] */ "unlocking the door", 51 | /* [1] */ "unlocking the chest", 52 | /* [2] */ "unlocking the box", 53 | /* [3] */ "picking the lock" 54 | }; 55 | 56 | /* if the target is currently unlocked, we're trying to lock it now */ 57 | if (xlock.door && !(xlock.door->doormask & D_LOCKED)) 58 | return actions[0]+2; /* "locking the door" */ 59 | else if (xlock.box && !xlock.box->olocked) 60 | return xlock.box->otyp == CHEST ? actions[1]+2 : actions[2]+2; 61 | /* otherwise we're trying to unlock it */ 62 | else if (xlock.picktyp == LOCK_PICK) 63 | return actions[3]; /* "picking the lock" */ 64 | #ifdef TOURIST 65 | else if (xlock.picktyp == CREDIT_CARD) 66 | return actions[3]; /* same as lock_pick */ 67 | #endif 68 | else if (xlock.door) 69 | return actions[0]; /* "unlocking the door" */ 70 | else 71 | return xlock.box->otyp == CHEST ? actions[1] : actions[2]; 72 | } 73 | 74 | STATIC_PTR 75 | int 76 | picklock() /* try to open/close a lock */ 77 | { 78 | 79 | if (xlock.box) { 80 | if((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) { 81 | return((xlock.usedtime = 0)); /* you or it moved */ 82 | } 83 | } else { /* door */ 84 | if(xlock.door != &(levl[u.ux+u.dx][u.uy+u.dy])) { 85 | return((xlock.usedtime = 0)); /* you moved */ 86 | } 87 | switch (xlock.door->doormask) { 88 | case D_NODOOR: 89 | pline("This doorway has no door."); 90 | return((xlock.usedtime = 0)); 91 | case D_ISOPEN: 92 | You("cannot lock an open door."); 93 | return((xlock.usedtime = 0)); 94 | case D_BROKEN: 95 | pline("This door is broken."); 96 | return((xlock.usedtime = 0)); 97 | } 98 | } 99 | 100 | if (xlock.usedtime++ >= 50 || nohands(youmonst.data)) { 101 | You("give up your attempt at %s.", lock_action()); 102 | exercise(A_DEX, TRUE); /* even if you don't succeed */ 103 | return((xlock.usedtime = 0)); 104 | } 105 | 106 | if(rn2(100) > xlock.chance) return(1); /* still busy */ 107 | 108 | You("succeed in %s.", lock_action()); 109 | if (xlock.door) { 110 | if(xlock.door->doormask & D_TRAPPED) { 111 | b_trapped("door", FINGER); 112 | xlock.door->doormask = D_NODOOR; 113 | unblock_point(u.ux+u.dx, u.uy+u.dy); 114 | if (*in_rooms(u.ux+u.dx, u.uy+u.dy, SHOPBASE)) 115 | add_damage(u.ux+u.dx, u.uy+u.dy, 0L); 116 | newsym(u.ux+u.dx, u.uy+u.dy); 117 | } else if (xlock.door->doormask & D_LOCKED) 118 | xlock.door->doormask = D_CLOSED; 119 | else xlock.door->doormask = D_LOCKED; 120 | } else { 121 | xlock.box->olocked = !xlock.box->olocked; 122 | if(xlock.box->otrapped) 123 | (void) chest_trap(xlock.box, FINGER, FALSE); 124 | } 125 | exercise(A_DEX, TRUE); 126 | return((xlock.usedtime = 0)); 127 | } 128 | 129 | STATIC_PTR 130 | int 131 | forcelock() /* try to force a locked chest */ 132 | { 133 | 134 | register struct obj *otmp; 135 | 136 | if((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) 137 | return((xlock.usedtime = 0)); /* you or it moved */ 138 | 139 | if (xlock.usedtime++ >= 50 || !uwep || nohands(youmonst.data)) { 140 | You("give up your attempt to force the lock."); 141 | if(xlock.usedtime >= 50) /* you made the effort */ 142 | exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE); 143 | return((xlock.usedtime = 0)); 144 | } 145 | 146 | if(xlock.picktyp) { /* blade */ 147 | 148 | if(rn2(1000-(int)uwep->spe) > (992-greatest_erosion(uwep)*10) && 149 | !uwep->cursed && !obj_resists(uwep, 0, 99)) { 150 | /* for a +0 weapon, probability that it survives an unsuccessful 151 | * attempt to force the lock is (.992)^50 = .67 152 | */ 153 | pline("%sour %s broke!", 154 | (uwep->quan > 1L) ? "One of y" : "Y", xname(uwep)); 155 | useup(uwep); 156 | You("give up your attempt to force the lock."); 157 | exercise(A_DEX, TRUE); 158 | return((xlock.usedtime = 0)); 159 | } 160 | } else /* blunt */ 161 | wake_nearby(); /* due to hammering on the container */ 162 | 163 | if(rn2(100) > xlock.chance) return(1); /* still busy */ 164 | 165 | You("succeed in forcing the lock."); 166 | xlock.box->olocked = 0; 167 | xlock.box->obroken = 1; 168 | if(!xlock.picktyp && !rn2(3)) { 169 | struct monst *shkp; 170 | boolean costly; 171 | long loss = 0L; 172 | 173 | costly = (*u.ushops && costly_spot(u.ux, u.uy)); 174 | shkp = costly ? shop_keeper(*u.ushops) : 0; 175 | 176 | pline("In fact, you've totally destroyed %s.", 177 | the(xname(xlock.box))); 178 | 179 | /* Put the contents on ground at the hero's feet. */ 180 | while ((otmp = xlock.box->cobj) != 0) { 181 | obj_extract_self(otmp); 182 | if(!rn2(3) || otmp->oclass == POTION_CLASS) { 183 | chest_shatter_msg(otmp); 184 | if (costly) 185 | loss += stolen_value(otmp, u.ux, u.uy, 186 | (boolean)shkp->mpeaceful, TRUE); 187 | if (otmp->quan == 1L) { 188 | obfree(otmp, (struct obj *) 0); 189 | continue; 190 | } 191 | useup(otmp); 192 | } 193 | if (xlock.box->otyp == ICE_BOX && otmp->otyp == CORPSE) { 194 | otmp->age = monstermoves - otmp->age; /* actual age */ 195 | start_corpse_timeout(otmp); 196 | } 197 | place_object(otmp, u.ux, u.uy); 198 | stackobj(otmp); 199 | } 200 | 201 | if (costly) 202 | loss += stolen_value(xlock.box, u.ux, u.uy, 203 | (boolean)shkp->mpeaceful, TRUE); 204 | if(loss) You("owe %ld zorkmids for objects destroyed.", loss); 205 | delobj(xlock.box); 206 | } 207 | exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE); 208 | return((xlock.usedtime = 0)); 209 | } 210 | 211 | #endif /* OVLB */ 212 | #ifdef OVL0 213 | 214 | void 215 | reset_pick() 216 | { 217 | xlock.usedtime = xlock.chance = xlock.picktyp = 0; 218 | xlock.door = 0; 219 | xlock.box = 0; 220 | } 221 | 222 | #endif /* OVL0 */ 223 | #ifdef OVLB 224 | 225 | int 226 | pick_lock(pick) /* pick a lock with a given object */ 227 | register struct obj *pick; 228 | { 229 | int x, y, picktyp, c, ch; 230 | struct rm *door; 231 | struct obj *otmp; 232 | char qbuf[QBUFSZ]; 233 | 234 | picktyp = pick->otyp; 235 | 236 | /* check whether we're resuming an interrupted previous attempt */ 237 | if (xlock.usedtime && picktyp == xlock.picktyp) { 238 | static char no_longer[] = "Unfortunately, you can no longer %s %s."; 239 | 240 | if (nohands(youmonst.data)) { 241 | const char *what = (picktyp == LOCK_PICK) ? "pick" : "key"; 242 | #ifdef TOURIST 243 | if (picktyp == CREDIT_CARD) what = "card"; 244 | #endif 245 | pline(no_longer, "hold the", what); 246 | reset_pick(); 247 | return 0; 248 | } else if (xlock.box && !can_reach_floor()) { 249 | pline(no_longer, "reach the", "lock"); 250 | reset_pick(); 251 | return 0; 252 | } else { 253 | const char *action = lock_action(); 254 | You("resume your attempt at %s.", action); 255 | set_occupation(picklock, action, 0); 256 | return(1); 257 | } 258 | } 259 | 260 | if(nohands(youmonst.data)) { 261 | You_cant("hold %s -- you have no hands!", doname(pick)); 262 | return(0); 263 | } 264 | 265 | if((picktyp != LOCK_PICK && 266 | #ifdef TOURIST 267 | picktyp != CREDIT_CARD && 268 | #endif 269 | picktyp != SKELETON_KEY)) { 270 | impossible("picking lock with object %d?", picktyp); 271 | return(0); 272 | } 273 | if(!getdir((char *)0)) return(0); 274 | 275 | ch = 0; /* lint suppression */ 276 | x = u.ux + u.dx; 277 | y = u.uy + u.dy; 278 | if (x == u.ux && y == u.uy) { /* pick lock on a container */ 279 | const char *verb; 280 | boolean it; 281 | int count; 282 | 283 | if (u.dz < 0) { 284 | There("isn't any sort of lock up %s.", 285 | Levitation ? "here" : "there"); 286 | return 0; 287 | } else if (is_lava(u.ux, u.uy)) { 288 | pline("Doing that would probably melt your %s.", 289 | xname(pick)); 290 | return 0; 291 | } else if (is_pool(u.ux, u.uy) && !Underwater) { 292 | pline_The("water has no lock."); 293 | return 0; 294 | } 295 | 296 | count = 0; 297 | c = 'n'; /* in case there are no boxes here */ 298 | for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) 299 | if (Is_box(otmp)) { 300 | ++count; 301 | if (!can_reach_floor()) { 302 | You_cant("reach %s from up here.", the(xname(otmp))); 303 | return 0; 304 | } 305 | it = 0; 306 | if (otmp->obroken) verb = "fix"; 307 | else if (!otmp->olocked) verb = "lock", it = 1; 308 | else if (picktyp != LOCK_PICK) verb = "unlock", it = 1; 309 | else verb = "pick"; 310 | Sprintf(qbuf, "There is %s here, %s %s?", 311 | doname(otmp), verb, it ? "it" : "its lock"); 312 | 313 | c = ynq(qbuf); 314 | if(c == 'q') return(0); 315 | if(c == 'n') continue; 316 | 317 | if (otmp->obroken) { 318 | You_cant("fix its broken lock with %s.", doname(pick)); 319 | return 0; 320 | } 321 | #ifdef TOURIST 322 | else if (picktyp == CREDIT_CARD && !otmp->olocked) { 323 | /* credit cards are only good for unlocking */ 324 | You_cant("do that with %s.", doname(pick)); 325 | return 0; 326 | } 327 | #endif 328 | switch(picktyp) { 329 | #ifdef TOURIST 330 | case CREDIT_CARD: 331 | ch = ACURR(A_DEX) + 20*Role_if(PM_ROGUE); 332 | break; 333 | #endif 334 | case LOCK_PICK: 335 | ch = 4*ACURR(A_DEX) + 25*Role_if(PM_ROGUE); 336 | break; 337 | case SKELETON_KEY: 338 | ch = 75 + ACURR(A_DEX); 339 | break; 340 | default: ch = 0; 341 | } 342 | if(otmp->cursed) ch /= 2; 343 | 344 | xlock.picktyp = picktyp; 345 | xlock.box = otmp; 346 | xlock.door = 0; 347 | break; 348 | } 349 | if (c != 'y') { 350 | if (!count) 351 | There("doesn't seem to be any sort of lock here."); 352 | return(0); /* decided against all boxes */ 353 | } 354 | } else { /* pick the lock in a door */ 355 | struct monst *mtmp; 356 | 357 | door = &levl[x][y]; 358 | if ((mtmp = m_at(x, y)) && canseemon(mtmp) 359 | && mtmp->m_ap_type != M_AP_FURNITURE 360 | && mtmp->m_ap_type != M_AP_OBJECT) { 361 | #ifdef TOURIST 362 | if (picktyp == CREDIT_CARD && 363 | (mtmp->isshk || mtmp->data == &mons[PM_ORACLE])) 364 | verbalize("No checks, no credit, no problem."); 365 | else 366 | #endif 367 | pline("I don't think %s would appreciate that.", mon_nam(mtmp)); 368 | return(0); 369 | } 370 | if(!IS_DOOR(door->typ)) { 371 | if (is_drawbridge_wall(x,y) >= 0) 372 | You("%s no lock on the drawbridge.", 373 | Blind ? "feel" : "see"); 374 | else 375 | You("%s no door there.", 376 | Blind ? "feel" : "see"); 377 | return(0); 378 | } 379 | switch (door->doormask) { 380 | case D_NODOOR: 381 | pline("This doorway has no door."); 382 | return(0); 383 | case D_ISOPEN: 384 | You("cannot lock an open door."); 385 | return(0); 386 | case D_BROKEN: 387 | pline("This door is broken."); 388 | return(0); 389 | default: 390 | #ifdef TOURIST 391 | /* credit cards are only good for unlocking */ 392 | if(picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) { 393 | You_cant("lock a door with a credit card."); 394 | return(0); 395 | } 396 | #endif 397 | 398 | Sprintf(qbuf,"%sock it?", 399 | (door->doormask & D_LOCKED) ? "Unl" : "L" ); 400 | 401 | c = yn(qbuf); 402 | if(c == 'n') return(0); 403 | 404 | switch(picktyp) { 405 | #ifdef TOURIST 406 | case CREDIT_CARD: 407 | ch = 2*ACURR(A_DEX) + 20*Role_if(PM_ROGUE); 408 | break; 409 | #endif 410 | case LOCK_PICK: 411 | ch = 3*ACURR(A_DEX) + 30*Role_if(PM_ROGUE); 412 | break; 413 | case SKELETON_KEY: 414 | ch = 70 + ACURR(A_DEX); 415 | break; 416 | default: ch = 0; 417 | } 418 | xlock.door = door; 419 | xlock.box = 0; 420 | } 421 | } 422 | flags.move = 0; 423 | xlock.chance = ch; 424 | xlock.picktyp = picktyp; 425 | xlock.usedtime = 0; 426 | set_occupation(picklock, lock_action(), 0); 427 | return(1); 428 | } 429 | 430 | int 431 | doforce() /* try to force a chest with your weapon */ 432 | { 433 | register struct obj *otmp; 434 | register int c, picktyp; 435 | char qbuf[QBUFSZ]; 436 | 437 | if(!uwep || /* proper type test */ 438 | (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep) && 439 | uwep->oclass != ROCK_CLASS) || 440 | (objects[uwep->otyp].oc_skill < P_DAGGER) || 441 | (objects[uwep->otyp].oc_skill > P_LANCE) || 442 | uwep->otyp == FLAIL || uwep->otyp == AKLYS 443 | #ifdef KOPS 444 | || uwep->otyp == RUBBER_HOSE 445 | #endif 446 | ) { 447 | You_cant("force anything without a %sweapon.", 448 | (uwep) ? "proper " : ""); 449 | return(0); 450 | } 451 | 452 | picktyp = is_blade(uwep); 453 | if(xlock.usedtime && xlock.box && picktyp == xlock.picktyp) { 454 | You("resume your attempt to force the lock."); 455 | set_occupation(forcelock, "forcing the lock", 0); 456 | return(1); 457 | } 458 | 459 | /* A lock is made only for the honest man, the thief will break it. */ 460 | xlock.box = (struct obj *)0; 461 | for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) 462 | if(Is_box(otmp)) { 463 | if (otmp->obroken || !otmp->olocked) { 464 | There("is %s here, but its lock is already %s.", 465 | doname(otmp), otmp->obroken ? "broken" : "unlocked"); 466 | continue; 467 | } 468 | Sprintf(qbuf,"There is %s here, force its lock?", doname(otmp)); 469 | 470 | c = ynq(qbuf); 471 | if(c == 'q') return(0); 472 | if(c == 'n') continue; 473 | 474 | if(picktyp) 475 | You("force your %s into a crack and pry.", xname(uwep)); 476 | else 477 | You("start bashing it with your %s.", xname(uwep)); 478 | xlock.box = otmp; 479 | xlock.chance = objects[otmp->otyp].oc_wldam * 2; 480 | xlock.picktyp = picktyp; 481 | xlock.usedtime = 0; 482 | break; 483 | } 484 | 485 | if(xlock.box) set_occupation(forcelock, "forcing the lock", 0); 486 | else You("decide not to force the issue."); 487 | return(1); 488 | } 489 | 490 | int 491 | doopen() /* try to open a door */ 492 | { 493 | register int x, y; 494 | register struct rm *door; 495 | struct monst *mtmp; 496 | 497 | if (nohands(youmonst.data)) { 498 | You_cant("open anything -- you have no hands!"); 499 | return 0; 500 | } 501 | 502 | if (u.utrap && u.utraptype == TT_PIT) { 503 | You_cant("reach over the edge of the pit."); 504 | return 0; 505 | } 506 | 507 | if(!getdir((char *)0)) return(0); 508 | 509 | x = u.ux + u.dx; 510 | y = u.uy + u.dy; 511 | if((x == u.ux) && (y == u.uy)) return(0); 512 | 513 | if ((mtmp = m_at(x,y)) && 514 | mtmp->m_ap_type == M_AP_FURNITURE && 515 | (mtmp->mappearance == S_hcdoor || 516 | mtmp->mappearance == S_vcdoor) && 517 | !Protection_from_shape_changers) { 518 | 519 | stumble_onto_mimic(mtmp); 520 | return(1); 521 | } 522 | 523 | door = &levl[x][y]; 524 | 525 | if(!IS_DOOR(door->typ)) { 526 | if (is_db_wall(x,y)) { 527 | There("is no obvious way to open the drawbridge."); 528 | return(0); 529 | } 530 | You("%s no door there.", 531 | Blind ? "feel" : "see"); 532 | return(0); 533 | } 534 | 535 | if (!(door->doormask & D_CLOSED)) { 536 | const char *mesg; 537 | 538 | switch (door->doormask) { 539 | case D_BROKEN: mesg = " is broken"; break; 540 | case D_NODOOR: mesg = "way has no door"; break; 541 | case D_ISOPEN: mesg = " is already open"; break; 542 | default: mesg = " is locked"; break; 543 | } 544 | pline("This door%s.", mesg); 545 | if (Blind) feel_location(x,y); 546 | return(0); 547 | } 548 | 549 | if(verysmall(youmonst.data)) { 550 | pline("You're too small to pull the door open."); 551 | return(0); 552 | } 553 | 554 | /* door is known to be CLOSED */ 555 | if (rnl(20) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) { 556 | pline_The("door opens."); 557 | if(door->doormask & D_TRAPPED) { 558 | b_trapped("door", FINGER); 559 | door->doormask = D_NODOOR; 560 | if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L); 561 | } else 562 | door->doormask = D_ISOPEN; 563 | if (Blind) 564 | feel_location(x,y); /* the hero knows she opened it */ 565 | else 566 | newsym(x,y); 567 | unblock_point(x,y); /* vision: new see through there */ 568 | } else { 569 | exercise(A_STR, TRUE); 570 | pline_The("door resists!"); 571 | } 572 | 573 | return(1); 574 | } 575 | 576 | STATIC_OVL 577 | boolean 578 | obstructed(x,y) 579 | register int x, y; 580 | { 581 | register struct monst *mtmp = m_at(x, y); 582 | 583 | if(mtmp && mtmp->m_ap_type != M_AP_FURNITURE) { 584 | if (mtmp->m_ap_type == M_AP_OBJECT) goto objhere; 585 | pline("%s stands in the way!", !canspotmon(mtmp) ? 586 | "Some creature" : Monnam(mtmp)); 587 | if (!canspotmon(mtmp)) 588 | map_invisible(mtmp->mx, mtmp->my); 589 | return(TRUE); 590 | } 591 | if (OBJ_AT(x, y)) { 592 | objhere: pline("%s's in the way.", Something); 593 | return(TRUE); 594 | } 595 | return(FALSE); 596 | } 597 | 598 | int 599 | doclose() /* try to close a door */ 600 | { 601 | register int x, y; 602 | register struct rm *door; 603 | struct monst *mtmp; 604 | 605 | if (nohands(youmonst.data)) { 606 | You_cant("close anything -- you have no hands!"); 607 | return 0; 608 | } 609 | 610 | if (u.utrap && u.utraptype == TT_PIT) { 611 | You_cant("reach over the edge of the pit."); 612 | return 0; 613 | } 614 | 615 | if(!getdir((char *)0)) return(0); 616 | 617 | x = u.ux + u.dx; 618 | y = u.uy + u.dy; 619 | if((x == u.ux) && (y == u.uy)) { 620 | You("are in the way!"); 621 | return(1); 622 | } 623 | 624 | if ((mtmp = m_at(x,y)) && 625 | mtmp->m_ap_type == M_AP_FURNITURE && 626 | (mtmp->mappearance == S_hcdoor || 627 | mtmp->mappearance == S_vcdoor) && 628 | !Protection_from_shape_changers) { 629 | 630 | stumble_onto_mimic(mtmp); 631 | return(1); 632 | } 633 | 634 | door = &levl[x][y]; 635 | 636 | if(!IS_DOOR(door->typ)) { 637 | if (door->typ == DRAWBRIDGE_DOWN) 638 | There("is no obvious way to close the drawbridge."); 639 | else 640 | You("%s no door there.", 641 | Blind ? "feel" : "see"); 642 | return(0); 643 | } 644 | 645 | if(door->doormask == D_NODOOR) { 646 | pline("This doorway has no door."); 647 | return(0); 648 | } 649 | 650 | if(obstructed(x, y)) return(0); 651 | 652 | if(door->doormask == D_BROKEN) { 653 | pline("This door is broken."); 654 | return(0); 655 | } 656 | 657 | if(door->doormask & (D_CLOSED | D_LOCKED)) { 658 | pline("This door is already closed."); 659 | return(0); 660 | } 661 | 662 | if(door->doormask == D_ISOPEN) { 663 | if(verysmall(youmonst.data) 664 | #ifdef STEED 665 | && !u.usteed 666 | #endif 667 | ) { 668 | pline("You're too small to push the door closed."); 669 | return(0); 670 | } 671 | if ( 672 | #ifdef STEED 673 | u.usteed || 674 | #endif 675 | rn2(25) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) { 676 | pline_The("door closes."); 677 | door->doormask = D_CLOSED; 678 | if (Blind) 679 | feel_location(x,y); /* the hero knows she closed it */ 680 | else 681 | newsym(x,y); 682 | block_point(x,y); /* vision: no longer see there */ 683 | } 684 | else { 685 | exercise(A_STR, TRUE); 686 | pline_The("door resists!"); 687 | } 688 | } 689 | 690 | return(1); 691 | } 692 | 693 | boolean /* box obj was hit with spell effect otmp */ 694 | boxlock(obj, otmp) /* returns true if something happened */ 695 | register struct obj *obj, *otmp; /* obj *is* a box */ 696 | { 697 | register boolean res = 0; 698 | 699 | switch(otmp->otyp) { 700 | case WAN_LOCKING: 701 | case SPE_WIZARD_LOCK: 702 | if (!obj->olocked) { /* lock it; fix if broken */ 703 | pline("Klunk!"); 704 | obj->olocked = 1; 705 | obj->obroken = 0; 706 | res = 1; 707 | } /* else already closed and locked */ 708 | break; 709 | case WAN_OPENING: 710 | case SPE_KNOCK: 711 | if (obj->olocked) { /* unlock; couldn't be broken */ 712 | pline("Klick!"); 713 | obj->olocked = 0; 714 | res = 1; 715 | } else /* silently fix if broken */ 716 | obj->obroken = 0; 717 | break; 718 | case WAN_POLYMORPH: 719 | case SPE_POLYMORPH: 720 | /* maybe start unlocking chest, get interrupted, then zap it; 721 | we must avoid any attempt to resume unlocking it */ 722 | if (xlock.box == obj) 723 | reset_pick(); 724 | break; 725 | } 726 | return res; 727 | } 728 | 729 | boolean /* Door/secret door was hit with spell effect otmp */ 730 | doorlock(otmp,x,y) /* returns true if something happened */ 731 | struct obj *otmp; 732 | int x, y; 733 | { 734 | register struct rm *door = &levl[x][y]; 735 | boolean res = TRUE; 736 | int loudness = 0; 737 | const char *msg = (const char *)0; 738 | const char *dustcloud = "A cloud of dust"; 739 | const char *quickly_dissipates = "quickly dissipates"; 740 | 741 | if (door->typ == SDOOR) { 742 | switch (otmp->otyp) { 743 | case WAN_OPENING: 744 | case SPE_KNOCK: 745 | case WAN_STRIKING: 746 | case SPE_FORCE_BOLT: 747 | door->typ = DOOR; 748 | door->doormask = D_CLOSED | (door->doormask & D_TRAPPED); 749 | newsym(x,y); 750 | if (cansee(x,y)) pline("A door appears in the wall!"); 751 | if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) 752 | return TRUE; 753 | break; /* striking: continue door handling below */ 754 | case WAN_LOCKING: 755 | case SPE_WIZARD_LOCK: 756 | default: 757 | return FALSE; 758 | } 759 | } 760 | 761 | switch(otmp->otyp) { 762 | case WAN_LOCKING: 763 | case SPE_WIZARD_LOCK: 764 | #ifdef REINCARNATION 765 | if (Is_rogue_level(&u.uz)) { 766 | boolean vis = cansee(x,y); 767 | /* Can't have real locking in Rogue, so just hide doorway */ 768 | if (vis) pline("%s springs up in the older, more primitive doorway.", 769 | dustcloud); 770 | else 771 | You_hear("a swoosh."); 772 | if (obstructed(x,y)) { 773 | if (vis) pline_The("cloud %s.",quickly_dissipates); 774 | return FALSE; 775 | } 776 | block_point(x, y); 777 | door->typ = SDOOR; 778 | if (vis) pline_The("doorway vanishes!"); 779 | newsym(x,y); 780 | return TRUE; 781 | } 782 | #endif 783 | if (obstructed(x,y)) return FALSE; 784 | /* Don't allow doors to close over traps. This is for pits */ 785 | /* & trap doors, but is it ever OK for anything else? */ 786 | if (t_at(x,y)) { 787 | /* maketrap() clears doormask, so it should be NODOOR */ 788 | pline( 789 | "%s springs up in the doorway, but %s.", 790 | dustcloud, quickly_dissipates); 791 | return FALSE; 792 | } 793 | 794 | switch (door->doormask & ~D_TRAPPED) { 795 | case D_CLOSED: 796 | msg = "The door locks!"; 797 | break; 798 | case D_ISOPEN: 799 | msg = "The door swings shut, and locks!"; 800 | break; 801 | case D_BROKEN: 802 | msg = "The broken door reassembles and locks!"; 803 | break; 804 | case D_NODOOR: 805 | msg = 806 | "A cloud of dust springs up and assembles itself into a door!"; 807 | break; 808 | default: 809 | res = FALSE; 810 | break; 811 | } 812 | block_point(x, y); 813 | door->doormask = D_LOCKED | (door->doormask & D_TRAPPED); 814 | newsym(x,y); 815 | break; 816 | case WAN_OPENING: 817 | case SPE_KNOCK: 818 | if (door->doormask & D_LOCKED) { 819 | msg = "The door unlocks!"; 820 | door->doormask = D_CLOSED | (door->doormask & D_TRAPPED); 821 | } else res = FALSE; 822 | break; 823 | case WAN_STRIKING: 824 | case SPE_FORCE_BOLT: 825 | if (door->doormask & (D_LOCKED | D_CLOSED)) { 826 | if (door->doormask & D_TRAPPED) { 827 | if (MON_AT(x, y)) 828 | (void) mb_trapped(m_at(x,y)); 829 | else if (flags.verbose) { 830 | if (cansee(x,y)) 831 | pline("KABOOM!! You see a door explode."); 832 | else if (flags.soundok) 833 | You_hear("a distant explosion."); 834 | } 835 | door->doormask = D_NODOOR; 836 | unblock_point(x,y); 837 | newsym(x,y); 838 | loudness = 40; 839 | break; 840 | } 841 | door->doormask = D_BROKEN; 842 | if (flags.verbose) { 843 | if (cansee(x,y)) 844 | pline_The("door crashes open!"); 845 | else if (flags.soundok) 846 | You_hear("a crashing sound."); 847 | } 848 | unblock_point(x,y); 849 | newsym(x,y); 850 | loudness = 20; 851 | } else res = FALSE; 852 | break; 853 | default: impossible("magic (%d) attempted on door.", otmp->otyp); 854 | break; 855 | } 856 | if (msg && cansee(x,y)) pline(msg); 857 | if (loudness > 0) { 858 | /* door was destroyed */ 859 | wake_nearto(x, y, loudness); 860 | if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L); 861 | } 862 | 863 | if (res && picking_at(x, y)) { 864 | /* maybe unseen monster zaps door you're unlocking */ 865 | stop_occupation(); 866 | reset_pick(); 867 | } 868 | return res; 869 | } 870 | 871 | STATIC_OVL void 872 | chest_shatter_msg(otmp) 873 | struct obj *otmp; 874 | { 875 | const char *disposition, *article = (otmp->quan > 1L) ? "A" : "The"; 876 | const char *thing; 877 | long save_Blinded; 878 | 879 | if (otmp->oclass == POTION_CLASS) { 880 | You("%s a flask shatter!", Blind ? "hear" : "see"); 881 | potionbreathe(otmp); 882 | return; 883 | } 884 | /* We have functions for distant and singular names, but not one */ 885 | /* which does _both_... */ 886 | save_Blinded = Blinded; 887 | Blinded = 1; 888 | thing = singular(otmp, xname); 889 | Blinded = save_Blinded; 890 | switch (objects[otmp->otyp].oc_material) { 891 | case PAPER: disposition = "is torn to shreds"; 892 | break; 893 | case WAX: disposition = "is crushed"; 894 | break; 895 | case VEGGY: disposition = "is pulped"; 896 | break; 897 | case FLESH: disposition = "is mashed"; 898 | break; 899 | case GLASS: disposition = "shatters"; 900 | break; 901 | case WOOD: disposition = "splinters to fragments"; 902 | break; 903 | default: disposition = "is destroyed"; 904 | break; 905 | } 906 | pline("%s %s %s!", article, thing, disposition); 907 | } 908 | 909 | #endif /* OVLB */ 910 | 911 | /*lock.c*/