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*/