NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / src / lock.c
blobf11c60eb14e13e97d1232f8b3a7e44acfbc89c6f
1 /* aNetHack 0.0.1 lock.c $ANH-Date: 1446955300 2015/11/08 04:01:40 $ $ANH-Branch: master $:$ANH-Revision: 1.67 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack 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->where != OBJ_FLOOR
79 || xlock.box->ox != u.ux || xlock.box->oy != u.uy) {
80 return ((xlock.usedtime = 0)); /* you or it moved */
82 } else { /* door */
83 if (xlock.door != &(levl[u.ux + u.dx][u.uy + u.dy])) {
84 return ((xlock.usedtime = 0)); /* you moved */
86 switch (xlock.door->doormask) {
87 case D_NODOOR:
88 pline("This doorway has no door.");
89 return ((xlock.usedtime = 0));
90 case D_ISOPEN:
91 You("cannot lock an open door.");
92 return ((xlock.usedtime = 0));
93 case D_BROKEN:
94 pline("This door is broken.");
95 return ((xlock.usedtime = 0));
99 if (xlock.usedtime++ >= 50 || nohands(youmonst.data)) {
100 You("give up your attempt at %s.", lock_action());
101 exercise(A_DEX, TRUE); /* even if you don't succeed */
102 return ((xlock.usedtime = 0));
105 if (rn2(100) >= xlock.chance)
106 return 1; /* still busy */
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
120 xlock.door->doormask = D_LOCKED;
121 } else {
122 xlock.box->olocked = !xlock.box->olocked;
123 xlock.box->lknown = 1;
124 if (xlock.box->otrapped)
125 (void) chest_trap(xlock.box, FINGER, FALSE);
127 exercise(A_DEX, TRUE);
128 return ((xlock.usedtime = 0));
131 void
132 breakchestlock(box, destroyit)
133 struct obj *box;
134 boolean destroyit;
136 if (!destroyit) { /* bill for the box but not for its contents */
137 struct obj *hide_contents = box->cobj;
139 box->cobj = 0;
140 costly_alteration(box, COST_BRKLCK);
141 box->cobj = hide_contents;
142 box->olocked = 0;
143 box->obroken = 1;
144 box->lknown = 1;
145 } else { /* #force has destroyed this box (at <u.ux,u.uy>) */
146 struct obj *otmp;
147 struct monst *shkp = (*u.ushops && costly_spot(u.ux, u.uy))
148 ? shop_keeper(*u.ushops)
149 : 0;
150 boolean costly = (boolean) (shkp != 0),
151 peaceful_shk = costly && (boolean) shkp->mpeaceful;
152 long loss = 0L;
154 pline("In fact, you've totally destroyed %s.", the(xname(box)));
155 /* Put the contents on ground at the hero's feet. */
156 while ((otmp = box->cobj) != 0) {
157 obj_extract_self(otmp);
158 if (!rn2(3) || otmp->oclass == POTION_CLASS) {
159 chest_shatter_msg(otmp);
160 if (costly)
161 loss +=
162 stolen_value(otmp, u.ux, u.uy, peaceful_shk, TRUE);
163 if (otmp->quan == 1L) {
164 obfree(otmp, (struct obj *) 0);
165 continue;
167 useup(otmp);
169 if (box->otyp == ICE_BOX && otmp->otyp == CORPSE) {
170 otmp->age = monstermoves - otmp->age; /* actual age */
171 start_corpse_timeout(otmp);
173 place_object(otmp, u.ux, u.uy);
174 stackobj(otmp);
176 if (costly)
177 loss += stolen_value(box, u.ux, u.uy, peaceful_shk, TRUE);
178 if (loss)
179 You("owe %ld %s for objects destroyed.", loss, currency(loss));
180 delobj(box);
184 /* try to force a locked chest */
185 STATIC_PTR int
186 forcelock(VOID_ARGS)
188 if ((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy))
189 return ((xlock.usedtime = 0)); /* you or it moved */
191 if (xlock.usedtime++ >= 50 || !uwep || nohands(youmonst.data)) {
192 You("give up your attempt to force the lock.");
193 if (xlock.usedtime >= 50) /* you made the effort */
194 exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
195 return ((xlock.usedtime = 0));
198 if (xlock.picktyp) { /* blade */
199 if (rn2(1000 - (int) uwep->spe) > (992 - greatest_erosion(uwep) * 10)
200 && !uwep->cursed && !obj_resists(uwep, 0, 99)) {
201 /* for a +0 weapon, probability that it survives an unsuccessful
202 * attempt to force the lock is (.992)^50 = .67
204 pline("%sour %s broke!", (uwep->quan > 1L) ? "One of y" : "Y",
205 xname(uwep));
206 useup(uwep);
207 You("give up your attempt to force the lock.");
208 exercise(A_DEX, TRUE);
209 return ((xlock.usedtime = 0));
211 } else /* blunt */
212 wake_nearby(); /* due to hammering on the container */
214 if (rn2(100) >= xlock.chance)
215 return 1; /* still busy */
217 You("succeed in forcing the lock.");
218 breakchestlock(xlock.box, (boolean) (!xlock.picktyp && !rn2(3)));
220 exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
221 return ((xlock.usedtime = 0));
224 void
225 reset_pick()
227 xlock.usedtime = xlock.chance = xlock.picktyp = 0;
228 xlock.door = 0;
229 xlock.box = 0;
232 /* level change; don't reset if hero is carrying xlock.box with him/her */
233 void
234 maybe_reset_pick()
236 if (!xlock.box || !carried(xlock.box))
237 reset_pick();
240 /* for doapply(); if player gives a direction or resumes an interrupted
241 previous attempt then it costs hero a move even if nothing ultimately
242 happens; when told "can't do that" before being asked for direction
243 or player cancels with ESC while giving direction, it doesn't */
244 #define PICKLOCK_LEARNED_SOMETHING (-1) /* time passes */
245 #define PICKLOCK_DID_NOTHING 0 /* no time passes */
246 #define PICKLOCK_DID_SOMETHING 1
248 /* player is applying a key, lock pick, or credit card */
250 pick_lock(pick)
251 struct obj *pick;
253 int picktyp, c, ch;
254 coord cc;
255 struct rm *door;
256 struct obj *otmp;
257 char qbuf[QBUFSZ];
259 picktyp = pick->otyp;
261 /* check whether we're resuming an interrupted previous attempt */
262 if (xlock.usedtime && picktyp == xlock.picktyp) {
263 static char no_longer[] = "Unfortunately, you can no longer %s %s.";
265 if (nohands(youmonst.data)) {
266 const char *what = (picktyp == LOCK_PICK) ? "pick" : "key";
267 if (picktyp == CREDIT_CARD)
268 what = "card";
269 pline(no_longer, "hold the", what);
270 reset_pick();
271 return PICKLOCK_LEARNED_SOMETHING;
272 } else if (u.uswallow || (xlock.box && !can_reach_floor(TRUE))) {
273 pline(no_longer, "reach the", "lock");
274 reset_pick();
275 return PICKLOCK_LEARNED_SOMETHING;
276 } else {
277 const char *action = lock_action();
279 You("resume your attempt at %s.", action);
280 set_occupation(picklock, action, 0);
281 return PICKLOCK_DID_SOMETHING;
285 if (nohands(youmonst.data)) {
286 You_cant("hold %s -- you have no hands!", doname(pick));
287 return PICKLOCK_DID_NOTHING;
288 } else if (u.uswallow) {
289 You_cant("%sunlock %s.", (picktyp == CREDIT_CARD) ? "" : "lock or ",
290 mon_nam(u.ustuck));
291 return PICKLOCK_DID_NOTHING;
294 if ((picktyp != LOCK_PICK && picktyp != CREDIT_CARD
295 && picktyp != SKELETON_KEY)) {
296 impossible("picking lock with object %d?", picktyp);
297 return PICKLOCK_DID_NOTHING;
299 ch = 0; /* lint suppression */
301 if (!get_adjacent_loc((char *) 0, "Invalid location!", u.ux, u.uy, &cc))
302 return PICKLOCK_DID_NOTHING;
304 if (cc.x == u.ux && cc.y == u.uy) { /* pick lock on a container */
305 const char *verb;
306 char qsfx[QBUFSZ];
307 boolean it;
308 int count;
310 if (u.dz < 0) {
311 There("isn't any sort of lock up %s.",
312 Levitation ? "here" : "there");
313 return PICKLOCK_LEARNED_SOMETHING;
314 } else if (is_lava(u.ux, u.uy)) {
315 pline("Doing that would probably melt %s.", yname(pick));
316 return PICKLOCK_LEARNED_SOMETHING;
317 } else if (is_pool(u.ux, u.uy) && !Underwater) {
318 pline_The("%s has no lock.", hliquid("water"));
319 return PICKLOCK_LEARNED_SOMETHING;
322 count = 0;
323 c = 'n'; /* in case there are no boxes here */
324 for (otmp = level.objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere)
325 if (Is_box(otmp)) {
326 ++count;
327 if (!can_reach_floor(TRUE)) {
328 You_cant("reach %s from up here.", the(xname(otmp)));
329 return PICKLOCK_LEARNED_SOMETHING;
331 it = 0;
332 if (otmp->obroken)
333 verb = "fix";
334 else if (!otmp->olocked)
335 verb = "lock", it = 1;
336 else if (picktyp != LOCK_PICK)
337 verb = "unlock", it = 1;
338 else
339 verb = "pick";
341 /* "There is <a box> here; <verb> <it|its lock>?" */
342 Sprintf(qsfx, " here; %s %s?", verb, it ? "it" : "its lock");
343 (void) safe_qbuf(qbuf, "There is ", qsfx, otmp, doname,
344 ansimpleoname, "a box");
345 otmp->lknown = 1;
347 c = ynq(qbuf);
348 if (c == 'q')
349 return 0;
350 if (c == 'n')
351 continue;
353 if (otmp->obroken) {
354 You_cant("fix its broken lock with %s.", doname(pick));
355 return PICKLOCK_LEARNED_SOMETHING;
356 } else if (picktyp == CREDIT_CARD && !otmp->olocked) {
357 /* credit cards are only good for unlocking */
358 You_cant("do that with %s.",
359 an(simple_typename(picktyp)));
360 return PICKLOCK_LEARNED_SOMETHING;
362 switch (picktyp) {
363 case CREDIT_CARD:
364 ch = ACURR(A_DEX) + 20 * Role_if(PM_ROGUE);
365 break;
366 case LOCK_PICK:
367 ch = 4 * ACURR(A_DEX) + 25 * Role_if(PM_ROGUE);
368 break;
369 case SKELETON_KEY:
370 ch = 75 + ACURR(A_DEX);
371 break;
372 default:
373 ch = 0;
375 if (otmp->cursed)
376 ch /= 2;
378 xlock.picktyp = picktyp;
379 xlock.box = otmp;
380 xlock.door = 0;
381 break;
383 if (c != 'y') {
384 if (!count)
385 There("doesn't seem to be any sort of lock here.");
386 return PICKLOCK_LEARNED_SOMETHING; /* decided against all boxes */
388 } else { /* pick the lock in a door */
389 struct monst *mtmp;
391 if (u.utrap && u.utraptype == TT_PIT) {
392 You_cant("reach over the edge of the pit.");
393 return PICKLOCK_LEARNED_SOMETHING;
396 door = &levl[cc.x][cc.y];
397 mtmp = m_at(cc.x, cc.y);
398 if (mtmp && canseemon(mtmp) && mtmp->m_ap_type != M_AP_FURNITURE
399 && mtmp->m_ap_type != M_AP_OBJECT) {
400 if (picktyp == CREDIT_CARD
401 && (mtmp->isshk || mtmp->data == &mons[PM_ORACLE]))
402 verbalize("No checks, no credit, no problem.");
403 else
404 pline("I don't think %s would appreciate that.",
405 mon_nam(mtmp));
406 return PICKLOCK_LEARNED_SOMETHING;
407 } else if (mtmp && is_door_mappear(mtmp)) {
408 /* "The door actually was a <mimic>!" */
409 stumble_onto_mimic(mtmp);
410 /* mimic might keep the key (50% chance, 10% for PYEC) */
411 maybe_absorb_item(mtmp, pick, 50, 10);
412 return PICKLOCK_LEARNED_SOMETHING;
414 if (!IS_DOOR(door->typ)) {
415 if (is_drawbridge_wall(cc.x, cc.y) >= 0)
416 You("%s no lock on the drawbridge.", Blind ? "feel" : "see");
417 else
418 You("%s no door there.", Blind ? "feel" : "see");
419 return PICKLOCK_LEARNED_SOMETHING;
421 switch (door->doormask) {
422 case D_NODOOR:
423 pline("This doorway has no door.");
424 return PICKLOCK_LEARNED_SOMETHING;
425 case D_ISOPEN:
426 You("cannot lock an open door.");
427 return PICKLOCK_LEARNED_SOMETHING;
428 case D_BROKEN:
429 pline("This door is broken.");
430 return PICKLOCK_LEARNED_SOMETHING;
431 default:
432 /* credit cards are only good for unlocking */
433 if (picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) {
434 You_cant("lock a door with a credit card.");
435 return PICKLOCK_LEARNED_SOMETHING;
438 Sprintf(qbuf, "%s it?",
439 (door->doormask & D_LOCKED) ? "Unlock" : "Lock");
441 c = yn(qbuf);
442 if (c == 'n')
443 return 0;
445 switch (picktyp) {
446 case CREDIT_CARD:
447 ch = 2 * ACURR(A_DEX) + 20 * Role_if(PM_ROGUE);
448 break;
449 case LOCK_PICK:
450 ch = 3 * ACURR(A_DEX) + 30 * Role_if(PM_ROGUE);
451 break;
452 case SKELETON_KEY:
453 ch = 70 + ACURR(A_DEX);
454 break;
455 default:
456 ch = 0;
458 xlock.door = door;
459 xlock.box = 0;
462 context.move = 0;
463 xlock.chance = ch;
464 xlock.picktyp = picktyp;
465 xlock.usedtime = 0;
466 set_occupation(picklock, lock_action(), 0);
467 return PICKLOCK_DID_SOMETHING;
470 /* try to force a chest with your weapon */
472 doforce()
474 register struct obj *otmp;
475 register int c, picktyp;
476 char qbuf[QBUFSZ];
478 if (u.uswallow) {
479 You_cant("force anything from inside here.");
480 return 0;
482 if (!uwep /* proper type test */
483 || ((uwep->oclass == WEAPON_CLASS || is_weptool(uwep))
484 ? (objects[uwep->otyp].oc_skill < P_DAGGER
485 || objects[uwep->otyp].oc_skill == P_FLAIL
486 || objects[uwep->otyp].oc_skill > P_LANCE)
487 : uwep->oclass != ROCK_CLASS)) {
488 You_cant("force anything %s weapon.",
489 !uwep ? "when not wielding a"
490 : (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep))
491 ? "without a proper"
492 : "with that");
493 return 0;
495 if (!can_reach_floor(TRUE)) {
496 cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
497 return 0;
500 picktyp = is_blade(uwep) && !is_pick(uwep);
501 if (xlock.usedtime && xlock.box && picktyp == xlock.picktyp) {
502 You("resume your attempt to force the lock.");
503 set_occupation(forcelock, "forcing the lock", 0);
504 return 1;
507 /* A lock is made only for the honest man, the thief will break it. */
508 xlock.box = (struct obj *) 0;
509 for (otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere)
510 if (Is_box(otmp)) {
511 if (otmp->obroken || !otmp->olocked) {
512 There("is %s here, but its lock is already %s.", doname(otmp),
513 otmp->obroken ? "broken" : "unlocked");
514 otmp->lknown = 1;
515 continue;
517 (void) safe_qbuf(qbuf, "There is ", " here; force its lock?",
518 otmp, doname, ansimpleoname, "a box");
519 otmp->lknown = 1;
521 c = ynq(qbuf);
522 if (c == 'q')
523 return 0;
524 if (c == 'n')
525 continue;
527 if (picktyp)
528 You("force %s into a crack and pry.", yname(uwep));
529 else
530 You("start bashing it with %s.", yname(uwep));
531 xlock.box = otmp;
532 xlock.chance = objects[uwep->otyp].oc_wldam * 2;
533 xlock.picktyp = picktyp;
534 xlock.usedtime = 0;
535 break;
538 if (xlock.box)
539 set_occupation(forcelock, "forcing the lock", 0);
540 else
541 You("decide not to force the issue.");
542 return 1;
545 boolean
546 stumble_on_door_mimic(x, y)
547 int x, y;
549 struct monst *mtmp;
551 if ((mtmp = m_at(x, y)) && is_door_mappear(mtmp)
552 && !Protection_from_shape_changers) {
553 stumble_onto_mimic(mtmp);
554 return TRUE;
556 return FALSE;
559 /* the 'O' command - try to open a door */
561 doopen()
563 return doopen_indir(0, 0);
566 /* try to open a door in direction u.dx/u.dy */
568 doopen_indir(x, y)
569 int x, y;
571 coord cc;
572 register struct rm *door;
573 boolean portcullis;
574 int res = 0;
576 if (nohands(youmonst.data)) {
577 You_cant("open anything -- you have no hands!");
578 return 0;
581 if (u.utrap && u.utraptype == TT_PIT) {
582 You_cant("reach over the edge of the pit.");
583 return 0;
586 if (x > 0 && y > 0) {
587 cc.x = x;
588 cc.y = y;
589 } else if (!get_adjacent_loc((char *) 0, (char *) 0, u.ux, u.uy, &cc))
590 return 0;
592 if ((cc.x == u.ux) && (cc.y == u.uy))
593 return 0;
595 if (stumble_on_door_mimic(cc.x, cc.y))
596 return 1;
598 /* when choosing a direction is impaired, use a turn
599 regardless of whether a door is successfully targetted */
600 if (Confusion || Stunned)
601 res = 1;
603 door = &levl[cc.x][cc.y];
604 portcullis = (is_drawbridge_wall(cc.x, cc.y) >= 0);
605 if (Blind) {
606 int oldglyph = door->glyph;
607 schar oldlastseentyp = lastseentyp[cc.x][cc.y];
609 feel_location(cc.x, cc.y);
610 if (door->glyph != oldglyph
611 || lastseentyp[cc.x][cc.y] != oldlastseentyp)
612 res = 1; /* learned something */
615 if (portcullis || !IS_DOOR(door->typ)) {
616 /* closed portcullis or spot that opened bridge would span */
617 if (is_db_wall(cc.x, cc.y) || door->typ == DRAWBRIDGE_UP)
618 There("is no obvious way to open the drawbridge.");
619 else if (portcullis || door->typ == DRAWBRIDGE_DOWN)
620 pline_The("drawbridge is already open.");
621 else if (container_at(cc.x, cc.y, TRUE))
622 pline("%s like something lootable over there.",
623 Blind ? "Feels" : "Seems");
624 else
625 You("%s no door there.", Blind ? "feel" : "see");
626 return res;
629 if (!(door->doormask & D_CLOSED)) {
630 const char *mesg;
632 switch (door->doormask) {
633 case D_BROKEN:
634 mesg = " is broken";
635 break;
636 case D_NODOOR:
637 mesg = "way has no door";
638 break;
639 case D_ISOPEN:
640 mesg = " is already open";
641 break;
642 default:
643 mesg = " is locked";
644 break;
646 pline("This door%s.", mesg);
647 return res;
650 if (verysmall(youmonst.data)) {
651 pline("You're too small to pull the door open.");
652 return res;
655 /* door is known to be CLOSED */
656 if (rnl(20) < (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3) {
657 pline_The("door opens.");
658 if (door->doormask & D_TRAPPED) {
659 b_trapped("door", FINGER);
660 door->doormask = D_NODOOR;
661 if (*in_rooms(cc.x, cc.y, SHOPBASE))
662 add_damage(cc.x, cc.y, 0L);
663 } else
664 door->doormask = D_ISOPEN;
665 feel_newsym(cc.x, cc.y); /* the hero knows she opened it */
666 unblock_point(cc.x, cc.y); /* vision: new see through there */
667 } else {
668 exercise(A_STR, TRUE);
669 pline_The("door resists!");
672 return 1;
675 STATIC_OVL boolean
676 obstructed(x, y, quietly)
677 register int x, y;
678 boolean quietly;
680 register struct monst *mtmp = m_at(x, y);
682 if (mtmp && mtmp->m_ap_type != M_AP_FURNITURE) {
683 if (mtmp->m_ap_type == M_AP_OBJECT)
684 goto objhere;
685 if (!quietly) {
686 if ((mtmp->mx != x) || (mtmp->my != y)) {
687 /* worm tail */
688 pline("%s%s blocks the way!",
689 !canspotmon(mtmp) ? Something : s_suffix(Monnam(mtmp)),
690 !canspotmon(mtmp) ? "" : " tail");
691 } else {
692 pline("%s blocks the way!",
693 !canspotmon(mtmp) ? "Some creature" : Monnam(mtmp));
696 if (!canspotmon(mtmp))
697 map_invisible(x, y);
698 return TRUE;
700 if (OBJ_AT(x, y)) {
701 objhere:
702 if (!quietly)
703 pline("%s's in the way.", Something);
704 return TRUE;
706 return FALSE;
709 /* the 'C' command - try to close a door */
711 doclose()
713 register int x, y;
714 register struct rm *door;
715 boolean portcullis;
716 int res = 0;
718 if (nohands(youmonst.data)) {
719 You_cant("close anything -- you have no hands!");
720 return 0;
723 if (u.utrap && u.utraptype == TT_PIT) {
724 You_cant("reach over the edge of the pit.");
725 return 0;
728 if (!getdir((char *) 0))
729 return 0;
731 x = u.ux + u.dx;
732 y = u.uy + u.dy;
733 if ((x == u.ux) && (y == u.uy)) {
734 You("are in the way!");
735 return 1;
738 if (!isok(x, y))
739 goto nodoor;
741 if (stumble_on_door_mimic(x, y))
742 return 1;
744 /* when choosing a direction is impaired, use a turn
745 regardless of whether a door is successfully targetted */
746 if (Confusion || Stunned)
747 res = 1;
749 door = &levl[x][y];
750 portcullis = (is_drawbridge_wall(x, y) >= 0);
751 if (Blind) {
752 int oldglyph = door->glyph;
753 schar oldlastseentyp = lastseentyp[x][y];
755 feel_location(x, y);
756 if (door->glyph != oldglyph || lastseentyp[x][y] != oldlastseentyp)
757 res = 1; /* learned something */
760 if (portcullis || !IS_DOOR(door->typ)) {
761 /* is_db_wall: closed portcullis */
762 if (is_db_wall(x, y) || door->typ == DRAWBRIDGE_UP)
763 pline_The("drawbridge is already closed.");
764 else if (portcullis || door->typ == DRAWBRIDGE_DOWN)
765 There("is no obvious way to close the drawbridge.");
766 else {
767 nodoor:
768 You("%s no door there.", Blind ? "feel" : "see");
770 return res;
773 if (door->doormask == D_NODOOR) {
774 pline("This doorway has no door.");
775 return res;
776 } else if (obstructed(x, y, FALSE)) {
777 return res;
778 } else if (door->doormask == D_BROKEN) {
779 pline("This door is broken.");
780 return res;
781 } else if (door->doormask & (D_CLOSED | D_LOCKED)) {
782 pline("This door is already closed.");
783 return res;
786 if (door->doormask == D_ISOPEN) {
787 if (verysmall(youmonst.data) && !u.usteed) {
788 pline("You're too small to push the door closed.");
789 return res;
791 if (u.usteed
792 || rn2(25) < (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3) {
793 pline_The("door closes.");
794 door->doormask = D_CLOSED;
795 feel_newsym(x, y); /* the hero knows she closed it */
796 block_point(x, y); /* vision: no longer see there */
797 } else {
798 exercise(A_STR, TRUE);
799 pline_The("door resists!");
803 return 1;
806 /* box obj was hit with spell or wand effect otmp;
807 returns true if something happened */
808 boolean
809 boxlock(obj, otmp)
810 struct obj *obj, *otmp; /* obj *is* a box */
812 boolean res = 0;
814 switch (otmp->otyp) {
815 case WAN_LOCKING:
816 case SPE_WIZARD_LOCK:
817 if (!obj->olocked) { /* lock it; fix if broken */
818 pline("Klunk!");
819 obj->olocked = 1;
820 obj->obroken = 0;
821 if (Role_if(PM_WIZARD))
822 obj->lknown = 1;
823 else
824 obj->lknown = 0;
825 res = 1;
826 } /* else already closed and locked */
827 break;
828 case WAN_OPENING:
829 case SPE_KNOCK:
830 if (obj->olocked) { /* unlock; couldn't be broken */
831 pline("Klick!");
832 obj->olocked = 0;
833 res = 1;
834 if (Role_if(PM_WIZARD))
835 obj->lknown = 1;
836 else
837 obj->lknown = 0;
838 } else /* silently fix if broken */
839 obj->obroken = 0;
840 break;
841 case WAN_POLYMORPH:
842 case SPE_POLYMORPH:
843 /* maybe start unlocking chest, get interrupted, then zap it;
844 we must avoid any attempt to resume unlocking it */
845 if (xlock.box == obj)
846 reset_pick();
847 break;
849 return res;
852 /* Door/secret door was hit with spell or wand effect otmp;
853 returns true if something happened */
854 boolean
855 doorlock(otmp, x, y)
856 struct obj *otmp;
857 int x, y;
859 register struct rm *door = &levl[x][y];
860 boolean res = TRUE;
861 int loudness = 0;
862 const char *msg = (const char *) 0;
863 const char *dustcloud = "A cloud of dust";
864 const char *quickly_dissipates = "quickly dissipates";
865 boolean mysterywand = (otmp->oclass == WAND_CLASS && !otmp->dknown);
867 if (door->typ == SDOOR) {
868 switch (otmp->otyp) {
869 case WAN_OPENING:
870 case SPE_KNOCK:
871 case WAN_STRIKING:
872 case SPE_FORCE_BOLT:
873 door->typ = DOOR;
874 door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
875 newsym(x, y);
876 if (cansee(x, y))
877 pline("A door appears in the wall!");
878 if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK)
879 return TRUE;
880 break; /* striking: continue door handling below */
881 case WAN_LOCKING:
882 case SPE_WIZARD_LOCK:
883 default:
884 return FALSE;
888 switch (otmp->otyp) {
889 case WAN_LOCKING:
890 case SPE_WIZARD_LOCK:
891 if (Is_rogue_level(&u.uz)) {
892 boolean vis = cansee(x, y);
893 /* Can't have real locking in Rogue, so just hide doorway */
894 if (vis)
895 pline("%s springs up in the older, more primitive doorway.",
896 dustcloud);
897 else
898 You_hear("a swoosh.");
899 if (obstructed(x, y, mysterywand)) {
900 if (vis)
901 pline_The("cloud %s.", quickly_dissipates);
902 return FALSE;
904 block_point(x, y);
905 door->typ = SDOOR;
906 if (vis)
907 pline_The("doorway vanishes!");
908 newsym(x, y);
909 return TRUE;
911 if (obstructed(x, y, mysterywand))
912 return FALSE;
913 /* Don't allow doors to close over traps. This is for pits */
914 /* & trap doors, but is it ever OK for anything else? */
915 if (t_at(x, y)) {
916 /* maketrap() clears doormask, so it should be NODOOR */
917 pline("%s springs up in the doorway, but %s.", dustcloud,
918 quickly_dissipates);
919 return FALSE;
922 switch (door->doormask & ~D_TRAPPED) {
923 case D_CLOSED:
924 msg = "The door locks!";
925 break;
926 case D_ISOPEN:
927 msg = "The door swings shut, and locks!";
928 break;
929 case D_BROKEN:
930 msg = "The broken door reassembles and locks!";
931 break;
932 case D_NODOOR:
933 msg =
934 "A cloud of dust springs up and assembles itself into a door!";
935 break;
936 default:
937 res = FALSE;
938 break;
940 block_point(x, y);
941 door->doormask = D_LOCKED | (door->doormask & D_TRAPPED);
942 newsym(x, y);
943 break;
944 case WAN_OPENING:
945 case SPE_KNOCK:
946 if (door->doormask & D_LOCKED) {
947 msg = "The door unlocks!";
948 door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
949 } else
950 res = FALSE;
951 break;
952 case WAN_STRIKING:
953 case SPE_FORCE_BOLT:
954 if (door->doormask & (D_LOCKED | D_CLOSED)) {
955 if (door->doormask & D_TRAPPED) {
956 if (MON_AT(x, y))
957 (void) mb_trapped(m_at(x, y));
958 else if (flags.verbose) {
959 if (cansee(x, y))
960 pline("KABOOM!! You see a door explode.");
961 else
962 You_hear("a distant explosion.");
964 door->doormask = D_NODOOR;
965 unblock_point(x, y);
966 newsym(x, y);
967 loudness = 40;
968 break;
970 door->doormask = D_BROKEN;
971 if (flags.verbose) {
972 if (cansee(x, y))
973 pline_The("door crashes open!");
974 else
975 You_hear("a crashing sound.");
977 unblock_point(x, y);
978 newsym(x, y);
979 /* force vision recalc before printing more messages */
980 if (vision_full_recalc)
981 vision_recalc(0);
982 loudness = 20;
983 } else
984 res = FALSE;
985 break;
986 default:
987 impossible("magic (%d) attempted on door.", otmp->otyp);
988 break;
990 if (msg && cansee(x, y))
991 pline1(msg);
992 if (loudness > 0) {
993 /* door was destroyed */
994 wake_nearto(x, y, loudness);
995 if (*in_rooms(x, y, SHOPBASE))
996 add_damage(x, y, 0L);
999 if (res && picking_at(x, y)) {
1000 /* maybe unseen monster zaps door you're unlocking */
1001 stop_occupation();
1002 reset_pick();
1004 return res;
1007 STATIC_OVL void
1008 chest_shatter_msg(otmp)
1009 struct obj *otmp;
1011 const char *disposition;
1012 const char *thing;
1013 long save_Blinded;
1015 if (otmp->oclass == POTION_CLASS) {
1016 You("%s %s shatter!", Blind ? "hear" : "see", an(bottlename()));
1017 if (!breathless(youmonst.data) || haseyes(youmonst.data))
1018 potionbreathe(otmp);
1019 return;
1021 /* We have functions for distant and singular names, but not one */
1022 /* which does _both_... */
1023 save_Blinded = Blinded;
1024 Blinded = 1;
1025 thing = singular(otmp, xname);
1026 Blinded = save_Blinded;
1027 switch (objects[otmp->otyp].oc_material) {
1028 case PAPER:
1029 disposition = "is torn to shreds";
1030 break;
1031 case WAX:
1032 disposition = "is crushed";
1033 break;
1034 case VEGGY:
1035 disposition = "is pulped";
1036 break;
1037 case FLESH:
1038 disposition = "is mashed";
1039 break;
1040 case GLASS:
1041 disposition = "shatters";
1042 break;
1043 case WOOD:
1044 disposition = "splinters to fragments";
1045 break;
1046 default:
1047 disposition = "is destroyed";
1048 break;
1050 pline("%s %s!", An(thing), disposition);
1053 /*lock.c*/