diff --git a/src/lock.c b/src/lock.c new file mode 100644 index 000000000..a79848d7a --- /dev/null +++ b/src/lock.c @@ -0,0 +1,912 @@ +/* SCCS Id: @(#)lock.c 3.3 2000/02/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_PTR int NDECL(picklock); +STATIC_PTR int NDECL(forcelock); + +/* at most one of `door' and `box' should be non-null at any given time */ +STATIC_VAR NEARDATA struct xlock_s { + struct rm *door; + struct obj *box; + int picktyp, chance, usedtime; +} xlock; + +#ifdef OVLB + +STATIC_DCL const char *NDECL(lock_action); +STATIC_DCL boolean FDECL(obstructed,(int,int)); +STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *)); + +boolean +picking_lock(x, y) + int *x, *y; +{ + if (occupation == picklock) { + *x = u.ux + u.dx; + *y = u.uy + u.dy; + return TRUE; + } else { + *x = *y = 0; + return FALSE; + } +} + +boolean +picking_at(x, y) +int x, y; +{ + return (boolean)(occupation == picklock && xlock.door == &levl[x][y]); +} + +/* produce an occupation string appropriate for the current activity */ +STATIC_OVL const char * +lock_action() +{ + /* "unlocking"+2 == "locking" */ + static const char *actions[] = { + /* [0] */ "unlocking the door", + /* [1] */ "unlocking the chest", + /* [2] */ "unlocking the box", + /* [3] */ "picking the lock" + }; + + /* if the target is currently unlocked, we're trying to lock it now */ + if (xlock.door && !(xlock.door->doormask & D_LOCKED)) + return actions[0]+2; /* "locking the door" */ + else if (xlock.box && !xlock.box->olocked) + return xlock.box->otyp == CHEST ? actions[1]+2 : actions[2]+2; + /* otherwise we're trying to unlock it */ + else if (xlock.picktyp == LOCK_PICK) + return actions[3]; /* "picking the lock" */ +#ifdef TOURIST + else if (xlock.picktyp == CREDIT_CARD) + return actions[3]; /* same as lock_pick */ +#endif + else if (xlock.door) + return actions[0]; /* "unlocking the door" */ + else + return xlock.box->otyp == CHEST ? actions[1] : actions[2]; +} + +STATIC_PTR +int +picklock() /* try to open/close a lock */ +{ + + if (xlock.box) { + if((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) { + return((xlock.usedtime = 0)); /* you or it moved */ + } + } else { /* door */ + if(xlock.door != &(levl[u.ux+u.dx][u.uy+u.dy])) { + return((xlock.usedtime = 0)); /* you moved */ + } + switch (xlock.door->doormask) { + case D_NODOOR: + pline("This doorway has no door."); + return((xlock.usedtime = 0)); + case D_ISOPEN: + You("cannot lock an open door."); + return((xlock.usedtime = 0)); + case D_BROKEN: + pline("This door is broken."); + return((xlock.usedtime = 0)); + } + } + + if (xlock.usedtime++ >= 50 || nohands(youmonst.data)) { + You("give up your attempt at %s.", lock_action()); + exercise(A_DEX, TRUE); /* even if you don't succeed */ + return((xlock.usedtime = 0)); + } + + if(rn2(100) >= xlock.chance) return(1); /* still busy */ + + You("succeed in %s.", lock_action()); + if (xlock.door) { + if(xlock.door->doormask & D_TRAPPED) { + b_trapped("door", FINGER); + xlock.door->doormask = D_NODOOR; + unblock_point(u.ux+u.dx, u.uy+u.dy); + if (*in_rooms(u.ux+u.dx, u.uy+u.dy, SHOPBASE)) + add_damage(u.ux+u.dx, u.uy+u.dy, 0L); + newsym(u.ux+u.dx, u.uy+u.dy); + } else if (xlock.door->doormask & D_LOCKED) + xlock.door->doormask = D_CLOSED; + else xlock.door->doormask = D_LOCKED; + } else { + xlock.box->olocked = !xlock.box->olocked; + if(xlock.box->otrapped) + (void) chest_trap(xlock.box, FINGER, FALSE); + } + exercise(A_DEX, TRUE); + return((xlock.usedtime = 0)); +} + +STATIC_PTR +int +forcelock() /* try to force a locked chest */ +{ + + register struct obj *otmp; + + if((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) + return((xlock.usedtime = 0)); /* you or it moved */ + + if (xlock.usedtime++ >= 50 || !uwep || nohands(youmonst.data)) { + You("give up your attempt to force the lock."); + if(xlock.usedtime >= 50) /* you made the effort */ + exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE); + return((xlock.usedtime = 0)); + } + + if(xlock.picktyp) { /* blade */ + + if(rn2(1000-(int)uwep->spe) > (992-greatest_erosion(uwep)*10) && + !uwep->cursed && !obj_resists(uwep, 0, 99)) { + /* for a +0 weapon, probability that it survives an unsuccessful + * attempt to force the lock is (.992)^50 = .67 + */ + pline("%sour %s broke!", + (uwep->quan > 1L) ? "One of y" : "Y", xname(uwep)); + useup(uwep); + You("give up your attempt to force the lock."); + exercise(A_DEX, TRUE); + return((xlock.usedtime = 0)); + } + } else /* blunt */ + wake_nearby(); /* due to hammering on the container */ + + if(rn2(100) >= xlock.chance) return(1); /* still busy */ + + You("succeed in forcing the lock."); + xlock.box->olocked = 0; + xlock.box->obroken = 1; + if(!xlock.picktyp && !rn2(3)) { + struct monst *shkp; + boolean costly; + long loss = 0L; + + costly = (*u.ushops && costly_spot(u.ux, u.uy)); + shkp = costly ? shop_keeper(*u.ushops) : 0; + + pline("In fact, you've totally destroyed %s.", + the(xname(xlock.box))); + + /* Put the contents on ground at the hero's feet. */ + while ((otmp = xlock.box->cobj) != 0) { + obj_extract_self(otmp); + if(!rn2(3) || otmp->oclass == POTION_CLASS) { + chest_shatter_msg(otmp); + if (costly) + loss += stolen_value(otmp, u.ux, u.uy, + (boolean)shkp->mpeaceful, TRUE); + if (otmp->quan == 1L) { + obfree(otmp, (struct obj *) 0); + continue; + } + useup(otmp); + } + if (xlock.box->otyp == ICE_BOX && otmp->otyp == CORPSE) { + otmp->age = monstermoves - otmp->age; /* actual age */ + start_corpse_timeout(otmp); + } + place_object(otmp, u.ux, u.uy); + stackobj(otmp); + } + + if (costly) + loss += stolen_value(xlock.box, u.ux, u.uy, + (boolean)shkp->mpeaceful, TRUE); + if(loss) You("owe %ld zorkmids for objects destroyed.", loss); + delobj(xlock.box); + } + exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE); + return((xlock.usedtime = 0)); +} + +#endif /* OVLB */ +#ifdef OVL0 + +void +reset_pick() +{ + xlock.usedtime = xlock.chance = xlock.picktyp = 0; + xlock.door = 0; + xlock.box = 0; +} + +#endif /* OVL0 */ +#ifdef OVLB + +int +pick_lock(pick) /* pick a lock with a given object */ + register struct obj *pick; +{ + int x, y, picktyp, c, ch; + struct rm *door; + struct obj *otmp; + char qbuf[QBUFSZ]; + + picktyp = pick->otyp; + + /* check whether we're resuming an interrupted previous attempt */ + if (xlock.usedtime && picktyp == xlock.picktyp) { + static char no_longer[] = "Unfortunately, you can no longer %s %s."; + + if (nohands(youmonst.data)) { + const char *what = (picktyp == LOCK_PICK) ? "pick" : "key"; +#ifdef TOURIST + if (picktyp == CREDIT_CARD) what = "card"; +#endif + pline(no_longer, "hold the", what); + reset_pick(); + return 0; + } else if (xlock.box && !can_reach_floor()) { + pline(no_longer, "reach the", "lock"); + reset_pick(); + return 0; + } else { + const char *action = lock_action(); + You("resume your attempt at %s.", action); + set_occupation(picklock, action, 0); + return(1); + } + } + + if(nohands(youmonst.data)) { + You_cant("hold %s -- you have no hands!", doname(pick)); + return(0); + } + + if((picktyp != LOCK_PICK && +#ifdef TOURIST + picktyp != CREDIT_CARD && +#endif + picktyp != SKELETON_KEY)) { + impossible("picking lock with object %d?", picktyp); + return(0); + } + if(!getdir((char *)0)) return(0); + + ch = 0; /* lint suppression */ + x = u.ux + u.dx; + y = u.uy + u.dy; + if (x == u.ux && y == u.uy) { /* pick lock on a container */ + const char *verb; + boolean it; + int count; + + if (u.dz < 0) { + There("isn't any sort of lock up %s.", + Levitation ? "here" : "there"); + return 0; + } else if (is_lava(u.ux, u.uy)) { + pline("Doing that would probably melt your %s.", + xname(pick)); + return 0; + } else if (is_pool(u.ux, u.uy) && !Underwater) { + pline_The("water has no lock."); + return 0; + } + + count = 0; + c = 'n'; /* in case there are no boxes here */ + for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if (Is_box(otmp)) { + ++count; + if (!can_reach_floor()) { + You_cant("reach %s from up here.", the(xname(otmp))); + return 0; + } + it = 0; + if (otmp->obroken) verb = "fix"; + else if (!otmp->olocked) verb = "lock", it = 1; + else if (picktyp != LOCK_PICK) verb = "unlock", it = 1; + else verb = "pick"; + Sprintf(qbuf, "There is %s here, %s %s?", + doname(otmp), verb, it ? "it" : "its lock"); + + c = ynq(qbuf); + if(c == 'q') return(0); + if(c == 'n') continue; + + if (otmp->obroken) { + You_cant("fix its broken lock with %s.", doname(pick)); + return 0; + } +#ifdef TOURIST + else if (picktyp == CREDIT_CARD && !otmp->olocked) { + /* credit cards are only good for unlocking */ + You_cant("do that with %s.", doname(pick)); + return 0; + } +#endif + switch(picktyp) { +#ifdef TOURIST + case CREDIT_CARD: + ch = ACURR(A_DEX) + 20*Role_if(PM_ROGUE); + break; +#endif + case LOCK_PICK: + ch = 4*ACURR(A_DEX) + 25*Role_if(PM_ROGUE); + break; + case SKELETON_KEY: + ch = 75 + ACURR(A_DEX); + break; + default: ch = 0; + } + if(otmp->cursed) ch /= 2; + + xlock.picktyp = picktyp; + xlock.box = otmp; + xlock.door = 0; + break; + } + if (c != 'y') { + if (!count) + There("doesn't seem to be any sort of lock here."); + return(0); /* decided against all boxes */ + } + } else { /* pick the lock in a door */ + struct monst *mtmp; + + door = &levl[x][y]; + if ((mtmp = m_at(x, y)) && canseemon(mtmp) + && mtmp->m_ap_type != M_AP_FURNITURE + && mtmp->m_ap_type != M_AP_OBJECT) { +#ifdef TOURIST + if (picktyp == CREDIT_CARD && + (mtmp->isshk || mtmp->data == &mons[PM_ORACLE])) + verbalize("No checks, no credit, no problem."); + else +#endif + pline("I don't think %s would appreciate that.", mon_nam(mtmp)); + return(0); + } + if(!IS_DOOR(door->typ)) { + if (is_drawbridge_wall(x,y) >= 0) + You("%s no lock on the drawbridge.", + Blind ? "feel" : "see"); + else + You("%s no door there.", + Blind ? "feel" : "see"); + return(0); + } + switch (door->doormask) { + case D_NODOOR: + pline("This doorway has no door."); + return(0); + case D_ISOPEN: + You("cannot lock an open door."); + return(0); + case D_BROKEN: + pline("This door is broken."); + return(0); + default: +#ifdef TOURIST + /* credit cards are only good for unlocking */ + if(picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) { + You_cant("lock a door with a credit card."); + return(0); + } +#endif + + Sprintf(qbuf,"%sock it?", + (door->doormask & D_LOCKED) ? "Unl" : "L" ); + + c = yn(qbuf); + if(c == 'n') return(0); + + switch(picktyp) { +#ifdef TOURIST + case CREDIT_CARD: + ch = 2*ACURR(A_DEX) + 20*Role_if(PM_ROGUE); + break; +#endif + case LOCK_PICK: + ch = 3*ACURR(A_DEX) + 30*Role_if(PM_ROGUE); + break; + case SKELETON_KEY: + ch = 70 + ACURR(A_DEX); + break; + default: ch = 0; + } + xlock.door = door; + xlock.box = 0; + } + } + flags.move = 0; + xlock.chance = ch; + xlock.picktyp = picktyp; + xlock.usedtime = 0; + set_occupation(picklock, lock_action(), 0); + return(1); +} + +int +doforce() /* try to force a chest with your weapon */ +{ + register struct obj *otmp; + register int c, picktyp; + char qbuf[QBUFSZ]; + + if(!uwep || /* proper type test */ + (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep) && + uwep->oclass != ROCK_CLASS) || + (objects[uwep->otyp].oc_skill < P_DAGGER) || + (objects[uwep->otyp].oc_skill > P_LANCE) || + uwep->otyp == FLAIL || uwep->otyp == AKLYS +#ifdef KOPS + || uwep->otyp == RUBBER_HOSE +#endif + ) { + You_cant("force anything without a %sweapon.", + (uwep) ? "proper " : ""); + return(0); + } + + picktyp = is_blade(uwep); + if(xlock.usedtime && xlock.box && picktyp == xlock.picktyp) { + You("resume your attempt to force the lock."); + set_occupation(forcelock, "forcing the lock", 0); + return(1); + } + + /* A lock is made only for the honest man, the thief will break it. */ + xlock.box = (struct obj *)0; + for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) + if(Is_box(otmp)) { + if (otmp->obroken || !otmp->olocked) { + There("is %s here, but its lock is already %s.", + doname(otmp), otmp->obroken ? "broken" : "unlocked"); + continue; + } + Sprintf(qbuf,"There is %s here, force its lock?", doname(otmp)); + + c = ynq(qbuf); + if(c == 'q') return(0); + if(c == 'n') continue; + + if(picktyp) + You("force your %s into a crack and pry.", xname(uwep)); + else + You("start bashing it with your %s.", xname(uwep)); + xlock.box = otmp; + xlock.chance = objects[otmp->otyp].oc_wldam * 2; + xlock.picktyp = picktyp; + xlock.usedtime = 0; + break; + } + + if(xlock.box) set_occupation(forcelock, "forcing the lock", 0); + else You("decide not to force the issue."); + return(1); +} + +int +doopen() /* try to open a door */ +{ + register int x, y; + register struct rm *door; + struct monst *mtmp; + + if (nohands(youmonst.data)) { + You_cant("open anything -- you have no hands!"); + return 0; + } + + if (u.utrap && u.utraptype == TT_PIT) { + You_cant("reach over the edge of the pit."); + return 0; + } + + if(!getdir((char *)0)) return(0); + + x = u.ux + u.dx; + y = u.uy + u.dy; + if((x == u.ux) && (y == u.uy)) return(0); + + if ((mtmp = m_at(x,y)) && + mtmp->m_ap_type == M_AP_FURNITURE && + (mtmp->mappearance == S_hcdoor || + mtmp->mappearance == S_vcdoor) && + !Protection_from_shape_changers) { + + stumble_onto_mimic(mtmp); + return(1); + } + + door = &levl[x][y]; + + if(!IS_DOOR(door->typ)) { + if (is_db_wall(x,y)) { + There("is no obvious way to open the drawbridge."); + return(0); + } + You("%s no door there.", + Blind ? "feel" : "see"); + return(0); + } + + if (!(door->doormask & D_CLOSED)) { + const char *mesg; + + switch (door->doormask) { + case D_BROKEN: mesg = " is broken"; break; + case D_NODOOR: mesg = "way has no door"; break; + case D_ISOPEN: mesg = " is already open"; break; + default: mesg = " is locked"; break; + } + pline("This door%s.", mesg); + if (Blind) feel_location(x,y); + return(0); + } + + if(verysmall(youmonst.data)) { + pline("You're too small to pull the door open."); + return(0); + } + + /* door is known to be CLOSED */ + if (rnl(20) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) { + pline_The("door opens."); + if(door->doormask & D_TRAPPED) { + b_trapped("door", FINGER); + door->doormask = D_NODOOR; + if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L); + } else + door->doormask = D_ISOPEN; + if (Blind) + feel_location(x,y); /* the hero knows she opened it */ + else + newsym(x,y); + unblock_point(x,y); /* vision: new see through there */ + } else { + exercise(A_STR, TRUE); + pline_The("door resists!"); + } + + return(1); +} + +STATIC_OVL +boolean +obstructed(x,y) +register int x, y; +{ + register struct monst *mtmp = m_at(x, y); + + if(mtmp && mtmp->m_ap_type != M_AP_FURNITURE) { + if (mtmp->m_ap_type == M_AP_OBJECT) goto objhere; + pline("%s stands in the way!", !canspotmon(mtmp) ? + "Some creature" : Monnam(mtmp)); + if (!canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + return(TRUE); + } + if (OBJ_AT(x, y)) { +objhere: pline("%s's in the way.", Something); + return(TRUE); + } + return(FALSE); +} + +int +doclose() /* try to close a door */ +{ + register int x, y; + register struct rm *door; + struct monst *mtmp; + + if (nohands(youmonst.data)) { + You_cant("close anything -- you have no hands!"); + return 0; + } + + if (u.utrap && u.utraptype == TT_PIT) { + You_cant("reach over the edge of the pit."); + return 0; + } + + if(!getdir((char *)0)) return(0); + + x = u.ux + u.dx; + y = u.uy + u.dy; + if((x == u.ux) && (y == u.uy)) { + You("are in the way!"); + return(1); + } + + if ((mtmp = m_at(x,y)) && + mtmp->m_ap_type == M_AP_FURNITURE && + (mtmp->mappearance == S_hcdoor || + mtmp->mappearance == S_vcdoor) && + !Protection_from_shape_changers) { + + stumble_onto_mimic(mtmp); + return(1); + } + + door = &levl[x][y]; + + if(!IS_DOOR(door->typ)) { + if (door->typ == DRAWBRIDGE_DOWN) + There("is no obvious way to close the drawbridge."); + else + You("%s no door there.", + Blind ? "feel" : "see"); + return(0); + } + + if(door->doormask == D_NODOOR) { + pline("This doorway has no door."); + return(0); + } + + if(obstructed(x, y)) return(0); + + if(door->doormask == D_BROKEN) { + pline("This door is broken."); + return(0); + } + + if(door->doormask & (D_CLOSED | D_LOCKED)) { + pline("This door is already closed."); + return(0); + } + + if(door->doormask == D_ISOPEN) { + if(verysmall(youmonst.data) +#ifdef STEED + && !u.usteed +#endif + ) { + pline("You're too small to push the door closed."); + return(0); + } + if ( +#ifdef STEED + u.usteed || +#endif + rn2(25) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) { + pline_The("door closes."); + door->doormask = D_CLOSED; + if (Blind) + feel_location(x,y); /* the hero knows she closed it */ + else + newsym(x,y); + block_point(x,y); /* vision: no longer see there */ + } + else { + exercise(A_STR, TRUE); + pline_The("door resists!"); + } + } + + return(1); +} + +boolean /* box obj was hit with spell effect otmp */ +boxlock(obj, otmp) /* returns true if something happened */ +register struct obj *obj, *otmp; /* obj *is* a box */ +{ + register boolean res = 0; + + switch(otmp->otyp) { + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + if (!obj->olocked) { /* lock it; fix if broken */ + pline("Klunk!"); + obj->olocked = 1; + obj->obroken = 0; + res = 1; + } /* else already closed and locked */ + break; + case WAN_OPENING: + case SPE_KNOCK: + if (obj->olocked) { /* unlock; couldn't be broken */ + pline("Klick!"); + obj->olocked = 0; + res = 1; + } else /* silently fix if broken */ + obj->obroken = 0; + break; + case WAN_POLYMORPH: + case SPE_POLYMORPH: + /* maybe start unlocking chest, get interrupted, then zap it; + we must avoid any attempt to resume unlocking it */ + if (xlock.box == obj) + reset_pick(); + break; + } + return res; +} + +boolean /* Door/secret door was hit with spell effect otmp */ +doorlock(otmp,x,y) /* returns true if something happened */ +struct obj *otmp; +int x, y; +{ + register struct rm *door = &levl[x][y]; + boolean res = TRUE; + int loudness = 0; + const char *msg = (const char *)0; + const char *dustcloud = "A cloud of dust"; + const char *quickly_dissipates = "quickly dissipates"; + + if (door->typ == SDOOR) { + switch (otmp->otyp) { + case WAN_OPENING: + case SPE_KNOCK: + case WAN_STRIKING: + case SPE_FORCE_BOLT: + door->typ = DOOR; + door->doormask = D_CLOSED | (door->doormask & D_TRAPPED); + newsym(x,y); + if (cansee(x,y)) pline("A door appears in the wall!"); + if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) + return TRUE; + break; /* striking: continue door handling below */ + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + default: + return FALSE; + } + } + + switch(otmp->otyp) { + case WAN_LOCKING: + case SPE_WIZARD_LOCK: +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) { + boolean vis = cansee(x,y); + /* Can't have real locking in Rogue, so just hide doorway */ + if (vis) pline("%s springs up in the older, more primitive doorway.", + dustcloud); + else + You_hear("a swoosh."); + if (obstructed(x,y)) { + if (vis) pline_The("cloud %s.",quickly_dissipates); + return FALSE; + } + block_point(x, y); + door->typ = SDOOR; + if (vis) pline_The("doorway vanishes!"); + newsym(x,y); + return TRUE; + } +#endif + if (obstructed(x,y)) return FALSE; + /* Don't allow doors to close over traps. This is for pits */ + /* & trap doors, but is it ever OK for anything else? */ + if (t_at(x,y)) { + /* maketrap() clears doormask, so it should be NODOOR */ + pline( + "%s springs up in the doorway, but %s.", + dustcloud, quickly_dissipates); + return FALSE; + } + + switch (door->doormask & ~D_TRAPPED) { + case D_CLOSED: + msg = "The door locks!"; + break; + case D_ISOPEN: + msg = "The door swings shut, and locks!"; + break; + case D_BROKEN: + msg = "The broken door reassembles and locks!"; + break; + case D_NODOOR: + msg = + "A cloud of dust springs up and assembles itself into a door!"; + break; + default: + res = FALSE; + break; + } + block_point(x, y); + door->doormask = D_LOCKED | (door->doormask & D_TRAPPED); + newsym(x,y); + break; + case WAN_OPENING: + case SPE_KNOCK: + if (door->doormask & D_LOCKED) { + msg = "The door unlocks!"; + door->doormask = D_CLOSED | (door->doormask & D_TRAPPED); + } else res = FALSE; + break; + case WAN_STRIKING: + case SPE_FORCE_BOLT: + if (door->doormask & (D_LOCKED | D_CLOSED)) { + if (door->doormask & D_TRAPPED) { + if (MON_AT(x, y)) + (void) mb_trapped(m_at(x,y)); + else if (flags.verbose) { + if (cansee(x,y)) + pline("KABOOM!! You see a door explode."); + else if (flags.soundok) + You_hear("a distant explosion."); + } + door->doormask = D_NODOOR; + unblock_point(x,y); + newsym(x,y); + loudness = 40; + break; + } + door->doormask = D_BROKEN; + if (flags.verbose) { + if (cansee(x,y)) + pline_The("door crashes open!"); + else if (flags.soundok) + You_hear("a crashing sound."); + } + unblock_point(x,y); + newsym(x,y); + loudness = 20; + } else res = FALSE; + break; + default: impossible("magic (%d) attempted on door.", otmp->otyp); + break; + } + if (msg && cansee(x,y)) pline(msg); + if (loudness > 0) { + /* door was destroyed */ + wake_nearto(x, y, loudness); + if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L); + } + + if (res && picking_at(x, y)) { + /* maybe unseen monster zaps door you're unlocking */ + stop_occupation(); + reset_pick(); + } + return res; +} + +STATIC_OVL void +chest_shatter_msg(otmp) +struct obj *otmp; +{ + const char *disposition, *article = (otmp->quan > 1L) ? "A" : "The"; + const char *thing; + long save_Blinded; + + if (otmp->oclass == POTION_CLASS) { + You("%s a %s shatter!", Blind ? "hear" : "see", bottlename()); + if (!breathless(youmonst.data) || haseyes(youmonst.data)) + potionbreathe(otmp); + return; + } + /* We have functions for distant and singular names, but not one */ + /* which does _both_... */ + save_Blinded = Blinded; + Blinded = 1; + thing = singular(otmp, xname); + Blinded = save_Blinded; + switch (objects[otmp->otyp].oc_material) { + case PAPER: disposition = "is torn to shreds"; + break; + case WAX: disposition = "is crushed"; + break; + case VEGGY: disposition = "is pulped"; + break; + case FLESH: disposition = "is mashed"; + break; + case GLASS: disposition = "shatters"; + break; + case WOOD: disposition = "splinters to fragments"; + break; + default: disposition = "is destroyed"; + break; + } + pline("%s %s %s!", article, thing, disposition); +} + +#endif /* OVLB */ + +/*lock.c*/