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