1 /* SCCS Id: @(#)lock.c 3.4 2000/02/06 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
7 STATIC_PTR int NDECL(picklock);
8 STATIC_PTR int NDECL(forcelock);
10 /* at most one of `door' and `box' should be non-null at any given time */
11 STATIC_VAR NEARDATA struct xlock_s {
14 int picktyp, chance, usedtime;
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 *));
27 if (occupation == picklock) {
41 return (boolean)(occupation == picklock && xlock.door == &levl[x][y]);
44 /* produce an occupation string appropriate for the current activity */
45 STATIC_OVL const char *
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"
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" */
65 else if (xlock.picktyp == CREDIT_CARD)
66 return actions[3]; /* same as lock_pick */
69 return actions[0]; /* "unlocking the door" */
71 return xlock.box->otyp == CHEST ? actions[1] : actions[2];
76 picklock() /* try to open/close a lock */
80 if((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) {
81 return((xlock.usedtime = 0)); /* you or it moved */
84 if(xlock.door != &(levl[u.ux+u.dx][u.uy+u.dy])) {
85 return((xlock.usedtime = 0)); /* you moved */
87 switch (xlock.door->doormask) {
89 pline("This doorway has no door.");
90 return((xlock.usedtime = 0));
92 You("cannot lock an open door.");
93 return((xlock.usedtime = 0));
95 pline("This door is broken.");
96 return((xlock.usedtime = 0));
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));
106 if(rn2(100) >= xlock.chance) return(1); /* still busy */
108 You("succeed in %s.", lock_action());
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;
121 xlock.box->olocked = !xlock.box->olocked;
122 if(xlock.box->otrapped)
123 (void) chest_trap(xlock.box, FINGER, FALSE);
125 exercise(A_DEX, TRUE);
126 return((xlock.usedtime = 0));
131 forcelock() /* try to force a locked chest */
134 register struct obj *otmp;
136 if((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy))
137 return((xlock.usedtime = 0)); /* you or it moved */
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));
146 if(xlock.picktyp) { /* blade */
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
153 pline("%sour %s broke!",
154 (uwep->quan > 1L) ? "One of y" : "Y", xname(uwep));
156 You("give up your attempt to force the lock.");
157 exercise(A_DEX, TRUE);
158 return((xlock.usedtime = 0));
161 wake_nearby(); /* due to hammering on the container */
163 if(rn2(100) >= xlock.chance) return(1); /* still busy */
165 You("succeed in forcing the lock.");
166 xlock.box->olocked = 0;
167 xlock.box->obroken = 1;
168 if(!xlock.picktyp && !rn2(3)) {
173 costly = (*u.ushops && costly_spot(u.ux, u.uy));
174 shkp = costly ? shop_keeper(*u.ushops) : 0;
176 pline("In fact, you've totally destroyed %s.",
177 the(xname(xlock.box)));
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);
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);
193 if (xlock.box->otyp == ICE_BOX && otmp->otyp == CORPSE) {
194 otmp->age = monstermoves - otmp->age; /* actual age */
195 start_corpse_timeout(otmp);
197 place_object(otmp, u.ux, u.uy);
202 loss += stolen_value(xlock.box, u.ux, u.uy,
203 (boolean)shkp->mpeaceful, TRUE);
204 if(loss) You("owe %ld %s for objects destroyed.", loss, currency(loss));
207 exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
208 return((xlock.usedtime = 0));
217 xlock.usedtime = xlock.chance = xlock.picktyp = 0;
226 pick_lock(pick) /* pick a lock with a given object */
227 register struct obj *pick;
235 picktyp = pick->otyp;
237 /* check whether we're resuming an interrupted previous attempt */
238 if (xlock.usedtime && picktyp == xlock.picktyp) {
239 static char no_longer[] = "Unfortunately, you can no longer %s %s.";
241 if (nohands(youmonst.data)) {
242 const char *what = (picktyp == LOCK_PICK) ? "pick" : "key";
244 if (picktyp == CREDIT_CARD) what = "card";
246 pline(no_longer, "hold the", what);
249 } else if (xlock.box && !can_reach_floor()) {
250 pline(no_longer, "reach the", "lock");
254 const char *action = lock_action();
255 You("resume your attempt at %s.", action);
256 set_occupation(picklock, action, 0);
261 if(nohands(youmonst.data)) {
262 You_cant("hold %s -- you have no hands!", doname(pick));
266 if((picktyp != LOCK_PICK &&
268 picktyp != CREDIT_CARD &&
270 picktyp != SKELETON_KEY)) {
271 impossible("picking lock with object %d?", picktyp);
274 ch = 0; /* lint suppression */
276 if(!get_adjacent_loc((char *)0, "Invalid location!", u.ux, u.uy, &cc)) return 0;
277 if (cc.x == u.ux && cc.y == u.uy) { /* pick lock on a container */
283 There("isn't any sort of lock up %s.",
284 Levitation ? "here" : "there");
286 } else if (is_lava(u.ux, u.uy)) {
287 pline("Doing that would probably melt your %s.",
290 } else if (is_pool(u.ux, u.uy) && !Underwater) {
291 pline_The("water has no lock.");
296 c = 'n'; /* in case there are no boxes here */
297 for(otmp = level.objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere)
300 if (!can_reach_floor()) {
301 You_cant("reach %s from up here.", the(xname(otmp)));
305 if (otmp->obroken) verb = "fix";
306 else if (!otmp->olocked) verb = "lock", it = 1;
307 else if (picktyp != LOCK_PICK) verb = "unlock", it = 1;
309 Sprintf(qbuf, "There is %s here, %s %s?",
310 safe_qbuf("", sizeof("There is here, unlock its lock?"),
311 doname(otmp), an(simple_typename(otmp->otyp)), "a box"),
312 verb, it ? "it" : "its lock");
315 if(c == 'q') return(0);
316 if(c == 'n') continue;
319 You_cant("fix its broken lock with %s.", doname(pick));
323 else if (picktyp == CREDIT_CARD && !otmp->olocked) {
324 /* credit cards are only good for unlocking */
325 You_cant("do that with %s.", doname(pick));
332 ch = ACURR(A_DEX) + 20*Role_if(PM_ROGUE);
336 ch = 4*ACURR(A_DEX) + 25*Role_if(PM_ROGUE);
339 ch = 75 + ACURR(A_DEX);
343 if(otmp->cursed) ch /= 2;
345 xlock.picktyp = picktyp;
352 There("doesn't seem to be any sort of lock here.");
353 return(0); /* decided against all boxes */
355 } else { /* pick the lock in a door */
358 if (u.utrap && u.utraptype == TT_PIT) {
359 You_cant("reach over the edge of the pit.");
363 door = &levl[cc.x][cc.y];
364 if ((mtmp = m_at(cc.x, cc.y)) && canseemon(mtmp)
365 && mtmp->m_ap_type != M_AP_FURNITURE
366 && mtmp->m_ap_type != M_AP_OBJECT) {
368 if (picktyp == CREDIT_CARD &&
369 (mtmp->isshk || mtmp->data == &mons[PM_ORACLE]))
370 verbalize("No checks, no credit, no problem.");
373 pline("I don't think %s would appreciate that.", mon_nam(mtmp));
376 if(!IS_DOOR(door->typ)) {
377 if (is_drawbridge_wall(cc.x,cc.y) >= 0)
378 You("%s no lock on the drawbridge.",
379 Blind ? "feel" : "see");
381 You("%s no door there.",
382 Blind ? "feel" : "see");
385 switch (door->doormask) {
387 pline("This doorway has no door.");
390 You("cannot lock an open door.");
393 pline("This door is broken.");
397 /* credit cards are only good for unlocking */
398 if(picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) {
399 You_cant("lock a door with a credit card.");
404 Sprintf(qbuf,"%sock it?",
405 (door->doormask & D_LOCKED) ? "Unl" : "L" );
408 if(c == 'n') return(0);
413 ch = 2*ACURR(A_DEX) + 20*Role_if(PM_ROGUE);
417 ch = 3*ACURR(A_DEX) + 30*Role_if(PM_ROGUE);
420 ch = 70 + ACURR(A_DEX);
430 xlock.picktyp = picktyp;
432 set_occupation(picklock, lock_action(), 0);
437 doforce() /* try to force a chest with your weapon */
439 register struct obj *otmp;
440 register int c, picktyp;
443 if(!uwep || /* proper type test */
444 (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep) &&
445 uwep->oclass != ROCK_CLASS) ||
446 (objects[uwep->otyp].oc_skill < P_DAGGER) ||
447 (objects[uwep->otyp].oc_skill > P_LANCE) ||
448 uwep->otyp == FLAIL || uwep->otyp == AKLYS
450 || uwep->otyp == RUBBER_HOSE
453 You_cant("force anything without a %sweapon.",
454 (uwep) ? "proper " : "");
458 picktyp = is_blade(uwep);
459 if(xlock.usedtime && xlock.box && picktyp == xlock.picktyp) {
460 You("resume your attempt to force the lock.");
461 set_occupation(forcelock, "forcing the lock", 0);
465 /* A lock is made only for the honest man, the thief will break it. */
466 xlock.box = (struct obj *)0;
467 for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere)
469 if (otmp->obroken || !otmp->olocked) {
470 There("is %s here, but its lock is already %s.",
471 doname(otmp), otmp->obroken ? "broken" : "unlocked");
474 Sprintf(qbuf,"There is %s here, force its lock?",
475 safe_qbuf("", sizeof("There is here, force its lock?"),
476 doname(otmp), an(simple_typename(otmp->otyp)),
480 if(c == 'q') return(0);
481 if(c == 'n') continue;
484 You("force your %s into a crack and pry.", xname(uwep));
486 You("start bashing it with your %s.", xname(uwep));
488 xlock.chance = objects[uwep->otyp].oc_wldam * 2;
489 xlock.picktyp = picktyp;
494 if(xlock.box) set_occupation(forcelock, "forcing the lock", 0);
495 else You("decide not to force the issue.");
500 doopen() /* try to open a door */
503 register struct rm *door;
506 if (nohands(youmonst.data)) {
507 You_cant("open anything -- you have no hands!");
511 if (u.utrap && u.utraptype == TT_PIT) {
512 You_cant("reach over the edge of the pit.");
516 if(!get_adjacent_loc((char *)0, (char *)0, u.ux, u.uy, &cc)) return(0);
518 if((cc.x == u.ux) && (cc.y == u.uy)) return(0);
520 if ((mtmp = m_at(cc.x,cc.y)) &&
521 mtmp->m_ap_type == M_AP_FURNITURE &&
522 (mtmp->mappearance == S_hcdoor ||
523 mtmp->mappearance == S_vcdoor) &&
524 !Protection_from_shape_changers) {
526 stumble_onto_mimic(mtmp);
530 door = &levl[cc.x][cc.y];
532 if(!IS_DOOR(door->typ)) {
533 if (is_db_wall(cc.x,cc.y)) {
534 There("is no obvious way to open the drawbridge.");
537 You("%s no door there.",
538 Blind ? "feel" : "see");
542 if (!(door->doormask & D_CLOSED)) {
545 switch (door->doormask) {
546 case D_BROKEN: mesg = " is broken"; break;
547 case D_NODOOR: mesg = "way has no door"; break;
548 case D_ISOPEN: mesg = " is already open"; break;
549 default: mesg = " is locked"; break;
551 pline("This door%s.", mesg);
552 if (Blind) feel_location(cc.x,cc.y);
556 if(verysmall(youmonst.data)) {
557 pline("You're too small to pull the door open.");
561 /* door is known to be CLOSED */
562 if (rnl(20) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) {
563 pline_The("door opens.");
564 if(door->doormask & D_TRAPPED) {
565 b_trapped("door", FINGER);
566 door->doormask = D_NODOOR;
567 if (*in_rooms(cc.x, cc.y, SHOPBASE)) add_damage(cc.x, cc.y, 0L);
569 door->doormask = D_ISOPEN;
571 feel_location(cc.x,cc.y); /* the hero knows she opened it */
574 unblock_point(cc.x,cc.y); /* vision: new see through there */
576 exercise(A_STR, TRUE);
577 pline_The("door resists!");
588 register struct monst *mtmp = m_at(x, y);
590 if(mtmp && mtmp->m_ap_type != M_AP_FURNITURE) {
591 if (mtmp->m_ap_type == M_AP_OBJECT) goto objhere;
592 pline("%s stands in the way!", !canspotmon(mtmp) ?
593 "Some creature" : Monnam(mtmp));
594 if (!canspotmon(mtmp))
595 map_invisible(mtmp->mx, mtmp->my);
599 objhere: pline("%s's in the way.", Something);
606 doclose() /* try to close a door */
609 register struct rm *door;
612 if (nohands(youmonst.data)) {
613 You_cant("close anything -- you have no hands!");
617 if (u.utrap && u.utraptype == TT_PIT) {
618 You_cant("reach over the edge of the pit.");
622 if(!getdir((char *)0)) return(0);
626 if((x == u.ux) && (y == u.uy)) {
627 You("are in the way!");
631 if ((mtmp = m_at(x,y)) &&
632 mtmp->m_ap_type == M_AP_FURNITURE &&
633 (mtmp->mappearance == S_hcdoor ||
634 mtmp->mappearance == S_vcdoor) &&
635 !Protection_from_shape_changers) {
637 stumble_onto_mimic(mtmp);
643 if(!IS_DOOR(door->typ)) {
644 if (door->typ == DRAWBRIDGE_DOWN)
645 There("is no obvious way to close the drawbridge.");
647 You("%s no door there.",
648 Blind ? "feel" : "see");
652 if(door->doormask == D_NODOOR) {
653 pline("This doorway has no door.");
657 if(obstructed(x, y)) return(0);
659 if(door->doormask == D_BROKEN) {
660 pline("This door is broken.");
664 if(door->doormask & (D_CLOSED | D_LOCKED)) {
665 pline("This door is already closed.");
669 if(door->doormask == D_ISOPEN) {
670 if(verysmall(youmonst.data)
675 pline("You're too small to push the door closed.");
682 rn2(25) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) {
683 pline_The("door closes.");
684 door->doormask = D_CLOSED;
686 feel_location(x,y); /* the hero knows she closed it */
689 block_point(x,y); /* vision: no longer see there */
692 exercise(A_STR, TRUE);
693 pline_The("door resists!");
700 boolean /* box obj was hit with spell effect otmp */
701 boxlock(obj, otmp) /* returns true if something happened */
702 register struct obj *obj, *otmp; /* obj *is* a box */
704 register boolean res = 0;
708 case SPE_WIZARD_LOCK:
709 if (!obj->olocked) { /* lock it; fix if broken */
714 } /* else already closed and locked */
718 if (obj->olocked) { /* unlock; couldn't be broken */
722 } else /* silently fix if broken */
727 /* maybe start unlocking chest, get interrupted, then zap it;
728 we must avoid any attempt to resume unlocking it */
729 if (xlock.box == obj)
736 boolean /* Door/secret door was hit with spell effect otmp */
737 doorlock(otmp,x,y) /* returns true if something happened */
741 register struct rm *door = &levl[x][y];
744 const char *msg = (const char *)0;
745 const char *dustcloud = "A cloud of dust";
746 const char *quickly_dissipates = "quickly dissipates";
748 if (door->typ == SDOOR) {
749 switch (otmp->otyp) {
755 door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
757 if (cansee(x,y)) pline("A door appears in the wall!");
758 if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK)
760 break; /* striking: continue door handling below */
762 case SPE_WIZARD_LOCK:
770 case SPE_WIZARD_LOCK:
772 if (Is_rogue_level(&u.uz)) {
773 boolean vis = cansee(x,y);
774 /* Can't have real locking in Rogue, so just hide doorway */
775 if (vis) pline("%s springs up in the older, more primitive doorway.",
778 You_hear("a swoosh.");
779 if (obstructed(x,y)) {
780 if (vis) pline_The("cloud %s.",quickly_dissipates);
785 if (vis) pline_The("doorway vanishes!");
790 if (obstructed(x,y)) return FALSE;
791 /* Don't allow doors to close over traps. This is for pits */
792 /* & trap doors, but is it ever OK for anything else? */
794 /* maketrap() clears doormask, so it should be NODOOR */
796 "%s springs up in the doorway, but %s.",
797 dustcloud, quickly_dissipates);
801 switch (door->doormask & ~D_TRAPPED) {
803 msg = "The door locks!";
806 msg = "The door swings shut, and locks!";
809 msg = "The broken door reassembles and locks!";
813 "A cloud of dust springs up and assembles itself into a door!";
820 door->doormask = D_LOCKED | (door->doormask & D_TRAPPED);
825 if (door->doormask & D_LOCKED) {
826 msg = "The door unlocks!";
827 door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
832 if (door->doormask & (D_LOCKED | D_CLOSED)) {
833 if (door->doormask & D_TRAPPED) {
835 (void) mb_trapped(m_at(x,y));
836 else if (flags.verbose) {
838 pline("KABOOM!! You see a door explode.");
839 else if (flags.soundok)
840 You_hear("a distant explosion.");
842 door->doormask = D_NODOOR;
848 door->doormask = D_BROKEN;
851 pline_The("door crashes open!");
852 else if (flags.soundok)
853 You_hear("a crashing sound.");
857 /* force vision recalc before printing more messages */
858 if (vision_full_recalc) vision_recalc(0);
862 default: impossible("magic (%d) attempted on door.", otmp->otyp);
865 if (msg && cansee(x,y)) pline(msg);
867 /* door was destroyed */
868 wake_nearto(x, y, loudness);
869 if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L);
872 if (res && picking_at(x, y)) {
873 /* maybe unseen monster zaps door you're unlocking */
881 chest_shatter_msg(otmp)
884 const char *disposition;
888 if (otmp->oclass == POTION_CLASS) {
889 You("%s %s shatter!", Blind ? "hear" : "see", an(bottlename()));
890 if (!breathless(youmonst.data) || haseyes(youmonst.data))
894 /* We have functions for distant and singular names, but not one */
895 /* which does _both_... */
896 save_Blinded = Blinded;
898 thing = singular(otmp, xname);
899 Blinded = save_Blinded;
900 switch (objects[otmp->otyp].oc_material) {
901 case PAPER: disposition = "is torn to shreds";
903 case WAX: disposition = "is crushed";
905 case VEGGY: disposition = "is pulped";
907 case FLESH: disposition = "is mashed";
909 case GLASS: disposition = "shatters";
911 case WOOD: disposition = "splinters to fragments";
913 default: disposition = "is destroyed";
916 pline("%s %s!", An(thing), disposition);