tuning: succubi
[aNetHack.git] / src / lock.c
blob16f1c63b1412e379b4098ea4879380bfb4eb181e
1 /* NetHack 3.6 lock.c $NHDT-Date: 1446955300 2015/11/08 04:01:40 $ $NHDT-Branch: master $:$NHDT-Revision: 1.67 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
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 {
12 struct rm *door;
13 struct obj *box;
14 int picktyp, /* key|pick|card for unlock, sharp vs blunt for #force */
15 chance, usedtime;
16 } xlock;
18 STATIC_DCL const char *NDECL(lock_action);
19 STATIC_DCL boolean FDECL(obstructed, (int, int, BOOLEAN_P));
20 STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *));
22 boolean
23 picking_lock(x, y)
24 int *x, *y;
26 if (occupation == picklock) {
27 *x = u.ux + u.dx;
28 *y = u.uy + u.dy;
29 return TRUE;
30 } else {
31 *x = *y = 0;
32 return FALSE;
36 boolean
37 picking_at(x, y)
38 int x, y;
40 return (boolean) (occupation == picklock && xlock.door == &levl[x][y]);
43 /* produce an occupation string appropriate for the current activity */
44 STATIC_OVL const char *
45 lock_action()
47 /* "unlocking"+2 == "locking" */
48 static const char *actions[] = {
49 "unlocking the door", /* [0] */
50 "unlocking the chest", /* [1] */
51 "unlocking the box", /* [2] */
52 "picking the lock" /* [3] */
55 /* if the target is currently unlocked, we're trying to lock it now */
56 if (xlock.door && !(xlock.door->doormask & D_LOCKED))
57 return actions[0] + 2; /* "locking the door" */
58 else if (xlock.box && !xlock.box->olocked)
59 return xlock.box->otyp == CHEST ? actions[1] + 2 : actions[2] + 2;
60 /* otherwise we're trying to unlock it */
61 else if (xlock.picktyp == LOCK_PICK)
62 return actions[3]; /* "picking the lock" */
63 else if (xlock.picktyp == CREDIT_CARD)
64 return actions[3]; /* same as lock_pick */
65 else if (xlock.door)
66 return actions[0]; /* "unlocking the door" */
67 else if (xlock.box)
68 return xlock.box->otyp == CHEST ? actions[1] : actions[2];
69 else
70 return actions[3];
73 /* try to open/close a lock */
74 STATIC_PTR int
75 picklock(VOID_ARGS)
77 if (xlock.box) {
78 if ((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) {
79 return ((xlock.usedtime = 0)); /* you or it moved */
81 } else { /* door */
82 if (xlock.door != &(levl[u.ux + u.dx][u.uy + u.dy])) {
83 return ((xlock.usedtime = 0)); /* you moved */
85 switch (xlock.door->doormask) {
86 case D_NODOOR:
87 pline("This doorway has no door.");
88 return ((xlock.usedtime = 0));
89 case D_ISOPEN:
90 You("cannot lock an open door.");
91 return ((xlock.usedtime = 0));
92 case D_BROKEN:
93 pline("This door is broken.");
94 return ((xlock.usedtime = 0));
98 if (xlock.usedtime++ >= 50 || nohands(youmonst.data)) {
99 You("give up your attempt at %s.", lock_action());
100 exercise(A_DEX, TRUE); /* even if you don't succeed */
101 return ((xlock.usedtime = 0));
104 if (rn2(100) >= xlock.chance)
105 return 1; /* still busy */
107 You("succeed in %s.", lock_action());
108 if (xlock.door) {
109 if (xlock.door->doormask & D_TRAPPED) {
110 b_trapped("door", FINGER);
111 xlock.door->doormask = D_NODOOR;
112 unblock_point(u.ux + u.dx, u.uy + u.dy);
113 if (*in_rooms(u.ux + u.dx, u.uy + u.dy, SHOPBASE))
114 add_damage(u.ux + u.dx, u.uy + u.dy, 0L);
115 newsym(u.ux + u.dx, u.uy + u.dy);
116 } else if (xlock.door->doormask & D_LOCKED)
117 xlock.door->doormask = D_CLOSED;
118 else
119 xlock.door->doormask = D_LOCKED;
120 } else {
121 xlock.box->olocked = !xlock.box->olocked;
122 xlock.box->lknown = 1;
123 if (xlock.box->otrapped)
124 (void) chest_trap(xlock.box, FINGER, FALSE);
126 exercise(A_DEX, TRUE);
127 return ((xlock.usedtime = 0));
130 void
131 breakchestlock(box, destroyit)
132 struct obj *box;
133 boolean destroyit;
135 if (!destroyit) { /* bill for the box but not for its contents */
136 struct obj *hide_contents = box->cobj;
138 box->cobj = 0;
139 costly_alteration(box, COST_BRKLCK);
140 box->cobj = hide_contents;
141 box->olocked = 0;
142 box->obroken = 1;
143 box->lknown = 1;
144 } else { /* #force has destroyed this box (at <u.ux,u.uy>) */
145 struct obj *otmp;
146 struct monst *shkp = (*u.ushops && costly_spot(u.ux, u.uy))
147 ? shop_keeper(*u.ushops)
148 : 0;
149 boolean costly = (boolean) (shkp != 0),
150 peaceful_shk = costly && (boolean) shkp->mpeaceful;
151 long loss = 0L;
153 pline("In fact, you've totally destroyed %s.", the(xname(box)));
154 /* Put the contents on ground at the hero's feet. */
155 while ((otmp = box->cobj) != 0) {
156 obj_extract_self(otmp);
157 if (!rn2(3) || otmp->oclass == POTION_CLASS) {
158 chest_shatter_msg(otmp);
159 if (costly)
160 loss +=
161 stolen_value(otmp, u.ux, u.uy, peaceful_shk, TRUE);
162 if (otmp->quan == 1L) {
163 obfree(otmp, (struct obj *) 0);
164 continue;
166 useup(otmp);
168 if (box->otyp == ICE_BOX && otmp->otyp == CORPSE) {
169 otmp->age = monstermoves - otmp->age; /* actual age */
170 start_corpse_timeout(otmp);
172 place_object(otmp, u.ux, u.uy);
173 stackobj(otmp);
175 if (costly)
176 loss += stolen_value(box, u.ux, u.uy, peaceful_shk, TRUE);
177 if (loss)
178 You("owe %ld %s for objects destroyed.", loss, currency(loss));
179 delobj(box);
183 /* try to force a locked chest */
184 STATIC_PTR int
185 forcelock(VOID_ARGS)
187 if ((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy))
188 return ((xlock.usedtime = 0)); /* you or it moved */
190 if (xlock.usedtime++ >= 50 || !uwep || nohands(youmonst.data)) {
191 You("give up your attempt to force the lock.");
192 if (xlock.usedtime >= 50) /* you made the effort */
193 exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
194 return ((xlock.usedtime = 0));
197 if (xlock.picktyp) { /* blade */
198 if (rn2(1000 - (int) uwep->spe) > (992 - greatest_erosion(uwep) * 10)
199 && !uwep->cursed && !obj_resists(uwep, 0, 99)) {
200 /* for a +0 weapon, probability that it survives an unsuccessful
201 * attempt to force the lock is (.992)^50 = .67
203 pline("%sour %s broke!", (uwep->quan > 1L) ? "One of y" : "Y",
204 xname(uwep));
205 useup(uwep);
206 You("give up your attempt to force the lock.");
207 exercise(A_DEX, TRUE);
208 return ((xlock.usedtime = 0));
210 } else /* blunt */
211 wake_nearby(); /* due to hammering on the container */
213 if (rn2(100) >= xlock.chance)
214 return 1; /* still busy */
216 You("succeed in forcing the lock.");
217 breakchestlock(xlock.box, (boolean) (!xlock.picktyp && !rn2(3)));
219 exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
220 return ((xlock.usedtime = 0));
223 void
224 reset_pick()
226 xlock.usedtime = xlock.chance = xlock.picktyp = 0;
227 xlock.door = 0;
228 xlock.box = 0;
231 /* for doapply(); if player gives a direction or resumes an interrupted
232 previous attempt then it costs hero a move even if nothing ultimately
233 happens; when told "can't do that" before being asked for direction
234 or player cancels with ESC while giving direction, it doesn't */
235 #define PICKLOCK_LEARNED_SOMETHING (-1) /* time passes */
236 #define PICKLOCK_DID_NOTHING 0 /* no time passes */
237 #define PICKLOCK_DID_SOMETHING 1
239 /* player is applying a key, lock pick, or credit card */
241 pick_lock(pick)
242 struct obj *pick;
244 int picktyp, c, ch;
245 coord cc;
246 struct rm *door;
247 struct obj *otmp;
248 char qbuf[QBUFSZ];
250 picktyp = pick->otyp;
252 /* check whether we're resuming an interrupted previous attempt */
253 if (xlock.usedtime && picktyp == xlock.picktyp) {
254 static char no_longer[] = "Unfortunately, you can no longer %s %s.";
256 if (nohands(youmonst.data)) {
257 const char *what = (picktyp == LOCK_PICK) ? "pick" : "key";
258 if (picktyp == CREDIT_CARD)
259 what = "card";
260 pline(no_longer, "hold the", what);
261 reset_pick();
262 return PICKLOCK_LEARNED_SOMETHING;
263 } else if (u.uswallow || (xlock.box && !can_reach_floor(TRUE))) {
264 pline(no_longer, "reach the", "lock");
265 reset_pick();
266 return PICKLOCK_LEARNED_SOMETHING;
267 } else {
268 const char *action = lock_action();
270 You("resume your attempt at %s.", action);
271 set_occupation(picklock, action, 0);
272 return PICKLOCK_DID_SOMETHING;
276 if (nohands(youmonst.data)) {
277 You_cant("hold %s -- you have no hands!", doname(pick));
278 return PICKLOCK_DID_NOTHING;
279 } else if (u.uswallow) {
280 You_cant("%sunlock %s.", (picktyp == CREDIT_CARD) ? "" : "lock or ",
281 mon_nam(u.ustuck));
282 return PICKLOCK_DID_NOTHING;
285 if ((picktyp != LOCK_PICK && picktyp != CREDIT_CARD
286 && picktyp != SKELETON_KEY)) {
287 impossible("picking lock with object %d?", picktyp);
288 return PICKLOCK_DID_NOTHING;
290 ch = 0; /* lint suppression */
292 if (!get_adjacent_loc((char *) 0, "Invalid location!", u.ux, u.uy, &cc))
293 return PICKLOCK_DID_NOTHING;
295 if (cc.x == u.ux && cc.y == u.uy) { /* pick lock on a container */
296 const char *verb;
297 char qsfx[QBUFSZ];
298 boolean it;
299 int count;
301 if (u.dz < 0) {
302 There("isn't any sort of lock up %s.",
303 Levitation ? "here" : "there");
304 return PICKLOCK_LEARNED_SOMETHING;
305 } else if (is_lava(u.ux, u.uy)) {
306 pline("Doing that would probably melt %s.", yname(pick));
307 return PICKLOCK_LEARNED_SOMETHING;
308 } else if (is_pool(u.ux, u.uy) && !Underwater) {
309 pline_The("water has no lock.");
310 return PICKLOCK_LEARNED_SOMETHING;
313 count = 0;
314 c = 'n'; /* in case there are no boxes here */
315 for (otmp = level.objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere)
316 if (Is_box(otmp)) {
317 ++count;
318 if (!can_reach_floor(TRUE)) {
319 You_cant("reach %s from up here.", the(xname(otmp)));
320 return PICKLOCK_LEARNED_SOMETHING;
322 it = 0;
323 if (otmp->obroken)
324 verb = "fix";
325 else if (!otmp->olocked)
326 verb = "lock", it = 1;
327 else if (picktyp != LOCK_PICK)
328 verb = "unlock", it = 1;
329 else
330 verb = "pick";
332 /* "There is <a box> here; <verb> <it|its lock>?" */
333 Sprintf(qsfx, " here; %s %s?", verb, it ? "it" : "its lock");
334 (void) safe_qbuf(qbuf, "There is ", qsfx, otmp, doname,
335 ansimpleoname, "a box");
336 otmp->lknown = 1;
338 c = ynq(qbuf);
339 if (c == 'q')
340 return 0;
341 if (c == 'n')
342 continue;
344 if (otmp->obroken) {
345 You_cant("fix its broken lock with %s.", doname(pick));
346 return PICKLOCK_LEARNED_SOMETHING;
347 } else if (picktyp == CREDIT_CARD && !otmp->olocked) {
348 /* credit cards are only good for unlocking */
349 You_cant("do that with %s.",
350 an(simple_typename(picktyp)));
351 return PICKLOCK_LEARNED_SOMETHING;
353 switch (picktyp) {
354 case CREDIT_CARD:
355 ch = ACURR(A_DEX) + 20 * Role_if(PM_ROGUE);
356 break;
357 case LOCK_PICK:
358 ch = 4 * ACURR(A_DEX) + 25 * Role_if(PM_ROGUE);
359 break;
360 case SKELETON_KEY:
361 ch = 75 + ACURR(A_DEX);
362 break;
363 default:
364 ch = 0;
366 if (otmp->cursed)
367 ch /= 2;
369 xlock.picktyp = picktyp;
370 xlock.box = otmp;
371 xlock.door = 0;
372 break;
374 if (c != 'y') {
375 if (!count)
376 There("doesn't seem to be any sort of lock here.");
377 return PICKLOCK_LEARNED_SOMETHING; /* decided against all boxes */
379 } else { /* pick the lock in a door */
380 struct monst *mtmp;
382 if (u.utrap && u.utraptype == TT_PIT) {
383 You_cant("reach over the edge of the pit.");
384 return PICKLOCK_LEARNED_SOMETHING;
387 door = &levl[cc.x][cc.y];
388 mtmp = m_at(cc.x, cc.y);
389 if (mtmp && canseemon(mtmp) && mtmp->m_ap_type != M_AP_FURNITURE
390 && mtmp->m_ap_type != M_AP_OBJECT) {
391 if (picktyp == CREDIT_CARD
392 && (mtmp->isshk || mtmp->data == &mons[PM_ORACLE]))
393 verbalize("No checks, no credit, no problem.");
394 else
395 pline("I don't think %s would appreciate that.",
396 mon_nam(mtmp));
397 return PICKLOCK_LEARNED_SOMETHING;
398 } else if (mtmp && is_door_mappear(mtmp)) {
399 /* "The door actually was a <mimic>!" */
400 stumble_onto_mimic(mtmp);
401 /* mimic might keep the key (50% chance, 10% for PYEC) */
402 maybe_absorb_item(mtmp, pick, 50, 10);
403 return PICKLOCK_LEARNED_SOMETHING;
405 if (!IS_DOOR(door->typ)) {
406 if (is_drawbridge_wall(cc.x, cc.y) >= 0)
407 You("%s no lock on the drawbridge.", Blind ? "feel" : "see");
408 else
409 You("%s no door there.", Blind ? "feel" : "see");
410 return PICKLOCK_LEARNED_SOMETHING;
412 switch (door->doormask) {
413 case D_NODOOR:
414 pline("This doorway has no door.");
415 return PICKLOCK_LEARNED_SOMETHING;
416 case D_ISOPEN:
417 You("cannot lock an open door.");
418 return PICKLOCK_LEARNED_SOMETHING;
419 case D_BROKEN:
420 pline("This door is broken.");
421 return PICKLOCK_LEARNED_SOMETHING;
422 default:
423 /* credit cards are only good for unlocking */
424 if (picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) {
425 You_cant("lock a door with a credit card.");
426 return PICKLOCK_LEARNED_SOMETHING;
429 Sprintf(qbuf, "%s it?",
430 (door->doormask & D_LOCKED) ? "Unlock" : "Lock");
432 c = yn(qbuf);
433 if (c == 'n')
434 return 0;
436 switch (picktyp) {
437 case CREDIT_CARD:
438 ch = 2 * ACURR(A_DEX) + 20 * Role_if(PM_ROGUE);
439 break;
440 case LOCK_PICK:
441 ch = 3 * ACURR(A_DEX) + 30 * Role_if(PM_ROGUE);
442 break;
443 case SKELETON_KEY:
444 ch = 70 + ACURR(A_DEX);
445 break;
446 default:
447 ch = 0;
449 xlock.door = door;
450 xlock.box = 0;
453 context.move = 0;
454 xlock.chance = ch;
455 xlock.picktyp = picktyp;
456 xlock.usedtime = 0;
457 set_occupation(picklock, lock_action(), 0);
458 return PICKLOCK_DID_SOMETHING;
461 /* try to force a chest with your weapon */
463 doforce()
465 register struct obj *otmp;
466 register int c, picktyp;
467 char qbuf[QBUFSZ];
469 if (u.uswallow) {
470 You_cant("force anything from inside here.");
471 return 0;
473 if (!uwep /* proper type test */
474 || ((uwep->oclass == WEAPON_CLASS || is_weptool(uwep))
475 ? (objects[uwep->otyp].oc_skill < P_DAGGER
476 || objects[uwep->otyp].oc_skill == P_FLAIL
477 || objects[uwep->otyp].oc_skill > P_LANCE)
478 : uwep->oclass != ROCK_CLASS)) {
479 You_cant("force anything %s weapon.",
480 !uwep ? "when not wielding a"
481 : (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep))
482 ? "without a proper"
483 : "with that");
484 return 0;
486 if (!can_reach_floor(TRUE)) {
487 cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
488 return 0;
491 picktyp = is_blade(uwep) && !is_pick(uwep);
492 if (xlock.usedtime && xlock.box && picktyp == xlock.picktyp) {
493 You("resume your attempt to force the lock.");
494 set_occupation(forcelock, "forcing the lock", 0);
495 return 1;
498 /* A lock is made only for the honest man, the thief will break it. */
499 xlock.box = (struct obj *) 0;
500 for (otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere)
501 if (Is_box(otmp)) {
502 if (otmp->obroken || !otmp->olocked) {
503 There("is %s here, but its lock is already %s.", doname(otmp),
504 otmp->obroken ? "broken" : "unlocked");
505 otmp->lknown = 1;
506 continue;
508 (void) safe_qbuf(qbuf, "There is ", " here; force its lock?",
509 otmp, doname, ansimpleoname, "a box");
510 otmp->lknown = 1;
512 c = ynq(qbuf);
513 if (c == 'q')
514 return 0;
515 if (c == 'n')
516 continue;
518 if (picktyp)
519 You("force %s into a crack and pry.", yname(uwep));
520 else
521 You("start bashing it with %s.", yname(uwep));
522 xlock.box = otmp;
523 xlock.chance = objects[uwep->otyp].oc_wldam * 2;
524 xlock.picktyp = picktyp;
525 xlock.usedtime = 0;
526 break;
529 if (xlock.box)
530 set_occupation(forcelock, "forcing the lock", 0);
531 else
532 You("decide not to force the issue.");
533 return 1;
536 boolean
537 stumble_on_door_mimic(x, y)
538 int x, y;
540 struct monst *mtmp;
542 if ((mtmp = m_at(x, y)) && is_door_mappear(mtmp)
543 && !Protection_from_shape_changers) {
544 stumble_onto_mimic(mtmp);
545 return TRUE;
547 return FALSE;
550 /* the 'O' command - try to open a door */
552 doopen()
554 return doopen_indir(0, 0);
557 /* try to open a door in direction u.dx/u.dy */
559 doopen_indir(x, y)
560 int x, y;
562 coord cc;
563 register struct rm *door;
564 boolean portcullis;
565 int res = 0;
567 if (nohands(youmonst.data)) {
568 You_cant("open anything -- you have no hands!");
569 return 0;
572 if (u.utrap && u.utraptype == TT_PIT) {
573 You_cant("reach over the edge of the pit.");
574 return 0;
577 if (x > 0 && y > 0) {
578 cc.x = x;
579 cc.y = y;
580 } else if (!get_adjacent_loc((char *) 0, (char *) 0, u.ux, u.uy, &cc))
581 return 0;
583 if ((cc.x == u.ux) && (cc.y == u.uy))
584 return 0;
586 if (stumble_on_door_mimic(cc.x, cc.y))
587 return 1;
589 /* when choosing a direction is impaired, use a turn
590 regardless of whether a door is successfully targetted */
591 if (Confusion || Stunned)
592 res = 1;
594 door = &levl[cc.x][cc.y];
595 portcullis = (is_drawbridge_wall(cc.x, cc.y) >= 0);
596 if (Blind) {
597 int oldglyph = door->glyph;
598 schar oldlastseentyp = lastseentyp[cc.x][cc.y];
600 feel_location(cc.x, cc.y);
601 if (door->glyph != oldglyph
602 || lastseentyp[cc.x][cc.y] != oldlastseentyp)
603 res = 1; /* learned something */
606 if (portcullis || !IS_DOOR(door->typ)) {
607 /* closed portcullis or spot that opened bridge would span */
608 if (is_db_wall(cc.x, cc.y) || door->typ == DRAWBRIDGE_UP)
609 There("is no obvious way to open the drawbridge.");
610 else if (portcullis || door->typ == DRAWBRIDGE_DOWN)
611 pline_The("drawbridge is already open.");
612 else
613 You("%s no door there.", Blind ? "feel" : "see");
614 return res;
617 if (!(door->doormask & D_CLOSED)) {
618 const char *mesg;
620 switch (door->doormask) {
621 case D_BROKEN:
622 mesg = " is broken";
623 break;
624 case D_NODOOR:
625 mesg = "way has no door";
626 break;
627 case D_ISOPEN:
628 mesg = " is already open";
629 break;
630 default:
631 mesg = " is locked";
632 break;
634 pline("This door%s.", mesg);
635 return res;
638 if (verysmall(youmonst.data)) {
639 pline("You're too small to pull the door open.");
640 return res;
643 /* door is known to be CLOSED */
644 if (rnl(20) < (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3) {
645 pline_The("door opens.");
646 if (door->doormask & D_TRAPPED) {
647 b_trapped("door", FINGER);
648 door->doormask = D_NODOOR;
649 if (*in_rooms(cc.x, cc.y, SHOPBASE))
650 add_damage(cc.x, cc.y, 0L);
651 } else
652 door->doormask = D_ISOPEN;
653 feel_newsym(cc.x, cc.y); /* the hero knows she opened it */
654 unblock_point(cc.x, cc.y); /* vision: new see through there */
655 } else {
656 exercise(A_STR, TRUE);
657 pline_The("door resists!");
660 return 1;
663 STATIC_OVL boolean
664 obstructed(x, y, quietly)
665 register int x, y;
666 boolean quietly;
668 register struct monst *mtmp = m_at(x, y);
670 if (mtmp && mtmp->m_ap_type != M_AP_FURNITURE) {
671 if (mtmp->m_ap_type == M_AP_OBJECT)
672 goto objhere;
673 if (!quietly) {
674 if ((mtmp->mx != x) || (mtmp->my != y)) {
675 /* worm tail */
676 pline("%s%s blocks the way!",
677 !canspotmon(mtmp) ? Something : s_suffix(Monnam(mtmp)),
678 !canspotmon(mtmp) ? "" : " tail");
679 } else {
680 pline("%s blocks the way!",
681 !canspotmon(mtmp) ? "Some creature" : Monnam(mtmp));
684 if (!canspotmon(mtmp))
685 map_invisible(x, y);
686 return TRUE;
688 if (OBJ_AT(x, y)) {
689 objhere:
690 if (!quietly)
691 pline("%s's in the way.", Something);
692 return TRUE;
694 return FALSE;
697 /* the 'C' command - try to close a door */
699 doclose()
701 register int x, y;
702 register struct rm *door;
703 boolean portcullis;
704 int res = 0;
706 if (nohands(youmonst.data)) {
707 You_cant("close anything -- you have no hands!");
708 return 0;
711 if (u.utrap && u.utraptype == TT_PIT) {
712 You_cant("reach over the edge of the pit.");
713 return 0;
716 if (!getdir((char *) 0))
717 return 0;
719 x = u.ux + u.dx;
720 y = u.uy + u.dy;
721 if ((x == u.ux) && (y == u.uy)) {
722 You("are in the way!");
723 return 1;
726 if (!isok(x, y))
727 goto nodoor;
729 if (stumble_on_door_mimic(x, y))
730 return 1;
732 /* when choosing a direction is impaired, use a turn
733 regardless of whether a door is successfully targetted */
734 if (Confusion || Stunned)
735 res = 1;
737 door = &levl[x][y];
738 portcullis = (is_drawbridge_wall(x, y) >= 0);
739 if (Blind) {
740 int oldglyph = door->glyph;
741 schar oldlastseentyp = lastseentyp[x][y];
743 feel_location(x, y);
744 if (door->glyph != oldglyph || lastseentyp[x][y] != oldlastseentyp)
745 res = 1; /* learned something */
748 if (portcullis || !IS_DOOR(door->typ)) {
749 /* is_db_wall: closed portcullis */
750 if (is_db_wall(x, y) || door->typ == DRAWBRIDGE_UP)
751 pline_The("drawbridge is already closed.");
752 else if (portcullis || door->typ == DRAWBRIDGE_DOWN)
753 There("is no obvious way to close the drawbridge.");
754 else {
755 nodoor:
756 You("%s no door there.", Blind ? "feel" : "see");
758 return res;
761 if (door->doormask == D_NODOOR) {
762 pline("This doorway has no door.");
763 return res;
764 } else if (obstructed(x, y, FALSE)) {
765 return res;
766 } else if (door->doormask == D_BROKEN) {
767 pline("This door is broken.");
768 return res;
769 } else if (door->doormask & (D_CLOSED | D_LOCKED)) {
770 pline("This door is already closed.");
771 return res;
774 if (door->doormask == D_ISOPEN) {
775 if (verysmall(youmonst.data) && !u.usteed) {
776 pline("You're too small to push the door closed.");
777 return res;
779 if (u.usteed
780 || rn2(25) < (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3) {
781 pline_The("door closes.");
782 door->doormask = D_CLOSED;
783 feel_newsym(x, y); /* the hero knows she closed it */
784 block_point(x, y); /* vision: no longer see there */
785 } else {
786 exercise(A_STR, TRUE);
787 pline_The("door resists!");
791 return 1;
794 /* box obj was hit with spell or wand effect otmp;
795 returns true if something happened */
796 boolean
797 boxlock(obj, otmp)
798 struct obj *obj, *otmp; /* obj *is* a box */
800 boolean res = 0;
802 switch (otmp->otyp) {
803 case WAN_LOCKING:
804 case SPE_WIZARD_LOCK:
805 if (!obj->olocked) { /* lock it; fix if broken */
806 pline("Klunk!");
807 obj->olocked = 1;
808 obj->obroken = 0;
809 if (Role_if(PM_WIZARD))
810 obj->lknown = 1;
811 else
812 obj->lknown = 0;
813 res = 1;
814 } /* else already closed and locked */
815 break;
816 case WAN_OPENING:
817 case SPE_KNOCK:
818 if (obj->olocked) { /* unlock; couldn't be broken */
819 pline("Klick!");
820 obj->olocked = 0;
821 res = 1;
822 if (Role_if(PM_WIZARD))
823 obj->lknown = 1;
824 else
825 obj->lknown = 0;
826 } else /* silently fix if broken */
827 obj->obroken = 0;
828 break;
829 case WAN_POLYMORPH:
830 case SPE_POLYMORPH:
831 /* maybe start unlocking chest, get interrupted, then zap it;
832 we must avoid any attempt to resume unlocking it */
833 if (xlock.box == obj)
834 reset_pick();
835 break;
837 return res;
840 /* Door/secret door was hit with spell or wand effect otmp;
841 returns true if something happened */
842 boolean
843 doorlock(otmp, x, y)
844 struct obj *otmp;
845 int x, y;
847 register struct rm *door = &levl[x][y];
848 boolean res = TRUE;
849 int loudness = 0;
850 const char *msg = (const char *) 0;
851 const char *dustcloud = "A cloud of dust";
852 const char *quickly_dissipates = "quickly dissipates";
853 boolean mysterywand = (otmp->oclass == WAND_CLASS && !otmp->dknown);
855 if (door->typ == SDOOR) {
856 switch (otmp->otyp) {
857 case WAN_OPENING:
858 case SPE_KNOCK:
859 case WAN_STRIKING:
860 case SPE_FORCE_BOLT:
861 door->typ = DOOR;
862 door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
863 newsym(x, y);
864 if (cansee(x, y))
865 pline("A door appears in the wall!");
866 if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK)
867 return TRUE;
868 break; /* striking: continue door handling below */
869 case WAN_LOCKING:
870 case SPE_WIZARD_LOCK:
871 default:
872 return FALSE;
876 switch (otmp->otyp) {
877 case WAN_LOCKING:
878 case SPE_WIZARD_LOCK:
879 if (Is_rogue_level(&u.uz)) {
880 boolean vis = cansee(x, y);
881 /* Can't have real locking in Rogue, so just hide doorway */
882 if (vis)
883 pline("%s springs up in the older, more primitive doorway.",
884 dustcloud);
885 else
886 You_hear("a swoosh.");
887 if (obstructed(x, y, mysterywand)) {
888 if (vis)
889 pline_The("cloud %s.", quickly_dissipates);
890 return FALSE;
892 block_point(x, y);
893 door->typ = SDOOR;
894 if (vis)
895 pline_The("doorway vanishes!");
896 newsym(x, y);
897 return TRUE;
899 if (obstructed(x, y, mysterywand))
900 return FALSE;
901 /* Don't allow doors to close over traps. This is for pits */
902 /* & trap doors, but is it ever OK for anything else? */
903 if (t_at(x, y)) {
904 /* maketrap() clears doormask, so it should be NODOOR */
905 pline("%s springs up in the doorway, but %s.", dustcloud,
906 quickly_dissipates);
907 return FALSE;
910 switch (door->doormask & ~D_TRAPPED) {
911 case D_CLOSED:
912 msg = "The door locks!";
913 break;
914 case D_ISOPEN:
915 msg = "The door swings shut, and locks!";
916 break;
917 case D_BROKEN:
918 msg = "The broken door reassembles and locks!";
919 break;
920 case D_NODOOR:
921 msg =
922 "A cloud of dust springs up and assembles itself into a door!";
923 break;
924 default:
925 res = FALSE;
926 break;
928 block_point(x, y);
929 door->doormask = D_LOCKED | (door->doormask & D_TRAPPED);
930 newsym(x, y);
931 break;
932 case WAN_OPENING:
933 case SPE_KNOCK:
934 if (door->doormask & D_LOCKED) {
935 msg = "The door unlocks!";
936 door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
937 } else
938 res = FALSE;
939 break;
940 case WAN_STRIKING:
941 case SPE_FORCE_BOLT:
942 if (door->doormask & (D_LOCKED | D_CLOSED)) {
943 if (door->doormask & D_TRAPPED) {
944 if (MON_AT(x, y))
945 (void) mb_trapped(m_at(x, y));
946 else if (flags.verbose) {
947 if (cansee(x, y))
948 pline("KABOOM!! You see a door explode.");
949 else
950 You_hear("a distant explosion.");
952 door->doormask = D_NODOOR;
953 unblock_point(x, y);
954 newsym(x, y);
955 loudness = 40;
956 break;
958 door->doormask = D_BROKEN;
959 if (flags.verbose) {
960 if (cansee(x, y))
961 pline_The("door crashes open!");
962 else
963 You_hear("a crashing sound.");
965 unblock_point(x, y);
966 newsym(x, y);
967 /* force vision recalc before printing more messages */
968 if (vision_full_recalc)
969 vision_recalc(0);
970 loudness = 20;
971 } else
972 res = FALSE;
973 break;
974 default:
975 impossible("magic (%d) attempted on door.", otmp->otyp);
976 break;
978 if (msg && cansee(x, y))
979 pline1(msg);
980 if (loudness > 0) {
981 /* door was destroyed */
982 wake_nearto(x, y, loudness);
983 if (*in_rooms(x, y, SHOPBASE))
984 add_damage(x, y, 0L);
987 if (res && picking_at(x, y)) {
988 /* maybe unseen monster zaps door you're unlocking */
989 stop_occupation();
990 reset_pick();
992 return res;
995 STATIC_OVL void
996 chest_shatter_msg(otmp)
997 struct obj *otmp;
999 const char *disposition;
1000 const char *thing;
1001 long save_Blinded;
1003 if (otmp->oclass == POTION_CLASS) {
1004 You("%s %s shatter!", Blind ? "hear" : "see", an(bottlename()));
1005 if (!breathless(youmonst.data) || haseyes(youmonst.data))
1006 potionbreathe(otmp);
1007 return;
1009 /* We have functions for distant and singular names, but not one */
1010 /* which does _both_... */
1011 save_Blinded = Blinded;
1012 Blinded = 1;
1013 thing = singular(otmp, xname);
1014 Blinded = save_Blinded;
1015 switch (objects[otmp->otyp].oc_material) {
1016 case PAPER:
1017 disposition = "is torn to shreds";
1018 break;
1019 case WAX:
1020 disposition = "is crushed";
1021 break;
1022 case VEGGY:
1023 disposition = "is pulped";
1024 break;
1025 case FLESH:
1026 disposition = "is mashed";
1027 break;
1028 case GLASS:
1029 disposition = "shatters";
1030 break;
1031 case WOOD:
1032 disposition = "splinters to fragments";
1033 break;
1034 default:
1035 disposition = "is destroyed";
1036 break;
1038 pline("%s %s!", An(thing), disposition);
1041 /*lock.c*/