1/*	SCCS Id: @(#)lock.c	3.4	2000/02/06	*/
2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3/* NetHack may be freely redistributed.  See license for details. */
4
5#include "hack.h"
6
7STATIC_PTR int NDECL(picklock);
8STATIC_PTR int NDECL(forcelock);
9
10/* at most one of `door' and `box' should be non-null at any given time */
11STATIC_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
19STATIC_DCL const char *NDECL(lock_action);
20STATIC_DCL boolean FDECL(obstructed,(int,int));
21STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *));
22
23boolean
24picking_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
37boolean
38picking_at(x, y)
39int 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 */
45STATIC_OVL const char *
46lock_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
74STATIC_PTR
75int
76picklock()	/* 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
129STATIC_PTR
130int
131forcelock()	/* 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 %s for objects destroyed.", loss, currency(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
214void
215reset_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
225int
226pick_lock(pick) /* pick a lock with a given object */
227	register struct	obj	*pick;
228{
229	int picktyp, c, ch;
230	coord cc;
231	struct rm	*door;
232	struct obj	*otmp;
233	char qbuf[QBUFSZ];
234
235	picktyp = pick->otyp;
236
237	/* check whether we're resuming an interrupted previous attempt */
238	if (xlock.usedtime && picktyp == xlock.picktyp) {
239	    static char no_longer[] = "Unfortunately, you can no longer %s %s.";
240
241	    if (nohands(youmonst.data)) {
242		const char *what = (picktyp == LOCK_PICK) ? "pick" : "key";
243#ifdef TOURIST
244		if (picktyp == CREDIT_CARD) what = "card";
245#endif
246		pline(no_longer, "hold the", what);
247		reset_pick();
248		return 0;
249	    } else if (xlock.box && !can_reach_floor()) {
250		pline(no_longer, "reach the", "lock");
251		reset_pick();
252		return 0;
253	    } else {
254		const char *action = lock_action();
255		You("resume your attempt at %s.", action);
256		set_occupation(picklock, action, 0);
257		return(1);
258	    }
259	}
260
261	if(nohands(youmonst.data)) {
262		You_cant("hold %s -- you have no hands!", doname(pick));
263		return(0);
264	}
265
266	if((picktyp != LOCK_PICK &&
267#ifdef TOURIST
268	    picktyp != CREDIT_CARD &&
269#endif
270	    picktyp != SKELETON_KEY)) {
271		impossible("picking lock with object %d?", picktyp);
272		return(0);
273	}
274	ch = 0;		/* lint suppression */
275
276	if(!get_adjacent_loc((char *)0, "Invalid location!", u.ux, u.uy, &cc)) return 0;
277	if (cc.x == u.ux && cc.y == u.uy) {	/* pick lock on a container */
278	    const char *verb;
279	    boolean it;
280	    int count;
281
282	    if (u.dz < 0) {
283		There("isn't any sort of lock up %s.",
284		      Levitation ? "here" : "there");
285		return 0;
286	    } else if (is_lava(u.ux, u.uy)) {
287		pline("Doing that would probably melt your %s.",
288		      xname(pick));
289		return 0;
290	    } else if (is_pool(u.ux, u.uy) && !Underwater) {
291		pline_The("water has no lock.");
292		return 0;
293	    }
294
295	    count = 0;
296	    c = 'n';			/* in case there are no boxes here */
297	    for(otmp = level.objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere)
298		if (Is_box(otmp)) {
299		    ++count;
300		    if (!can_reach_floor()) {
301			You_cant("reach %s from up here.", the(xname(otmp)));
302			return 0;
303		    }
304		    it = 0;
305		    if (otmp->obroken) verb = "fix";
306		    else if (!otmp->olocked) verb = "lock", it = 1;
307		    else if (picktyp != LOCK_PICK) verb = "unlock", it = 1;
308		    else verb = "pick";
309		    Sprintf(qbuf, "There is %s here, %s %s?",
310		    	    safe_qbuf("", sizeof("There is  here, unlock its lock?"),
311			    	doname(otmp), an(simple_typename(otmp->otyp)), "a box"),
312			    verb, it ? "it" : "its lock");
313
314		    c = ynq(qbuf);
315		    if(c == 'q') return(0);
316		    if(c == 'n') continue;
317
318		    if (otmp->obroken) {
319			You_cant("fix its broken lock with %s.", doname(pick));
320			return 0;
321		    }
322#ifdef TOURIST
323		    else if (picktyp == CREDIT_CARD && !otmp->olocked) {
324			/* credit cards are only good for unlocking */
325			You_cant("do that with %s.", doname(pick));
326			return 0;
327		    }
328#endif
329		    switch(picktyp) {
330#ifdef TOURIST
331			case CREDIT_CARD:
332			    ch = ACURR(A_DEX) + 20*Role_if(PM_ROGUE);
333			    break;
334#endif
335			case LOCK_PICK:
336			    ch = 4*ACURR(A_DEX) + 25*Role_if(PM_ROGUE);
337			    break;
338			case SKELETON_KEY:
339			    ch = 75 + ACURR(A_DEX);
340			    break;
341			default:	ch = 0;
342		    }
343		    if(otmp->cursed) ch /= 2;
344
345		    xlock.picktyp = picktyp;
346		    xlock.box = otmp;
347		    xlock.door = 0;
348		    break;
349		}
350	    if (c != 'y') {
351		if (!count)
352		    There("doesn't seem to be any sort of lock here.");
353		return(0);		/* decided against all boxes */
354	    }
355	} else {			/* pick the lock in a door */
356	    struct monst *mtmp;
357
358	    if (u.utrap && u.utraptype == TT_PIT) {
359		You_cant("reach over the edge of the pit.");
360		return(0);
361	    }
362
363	    door = &levl[cc.x][cc.y];
364	    if ((mtmp = m_at(cc.x, cc.y)) && canseemon(mtmp)
365			&& mtmp->m_ap_type != M_AP_FURNITURE
366			&& mtmp->m_ap_type != M_AP_OBJECT) {
367#ifdef TOURIST
368		if (picktyp == CREDIT_CARD &&
369		    (mtmp->isshk || mtmp->data == &mons[PM_ORACLE]))
370		    verbalize("No checks, no credit, no problem.");
371		else
372#endif
373		    pline("I don't think %s would appreciate that.", mon_nam(mtmp));
374		return(0);
375	    }
376	    if(!IS_DOOR(door->typ)) {
377		if (is_drawbridge_wall(cc.x,cc.y) >= 0)
378		    You("%s no lock on the drawbridge.",
379				Blind ? "feel" : "see");
380		else
381		    You("%s no door there.",
382				Blind ? "feel" : "see");
383		return(0);
384	    }
385	    switch (door->doormask) {
386		case D_NODOOR:
387		    pline("This doorway has no door.");
388		    return(0);
389		case D_ISOPEN:
390		    You("cannot lock an open door.");
391		    return(0);
392		case D_BROKEN:
393		    pline("This door is broken.");
394		    return(0);
395		default:
396#ifdef TOURIST
397		    /* credit cards are only good for unlocking */
398		    if(picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) {
399			You_cant("lock a door with a credit card.");
400			return(0);
401		    }
402#endif
403
404		    Sprintf(qbuf,"%sock it?",
405			(door->doormask & D_LOCKED) ? "Unl" : "L" );
406
407		    c = yn(qbuf);
408		    if(c == 'n') return(0);
409
410		    switch(picktyp) {
411#ifdef TOURIST
412			case CREDIT_CARD:
413			    ch = 2*ACURR(A_DEX) + 20*Role_if(PM_ROGUE);
414			    break;
415#endif
416			case LOCK_PICK:
417			    ch = 3*ACURR(A_DEX) + 30*Role_if(PM_ROGUE);
418			    break;
419			case SKELETON_KEY:
420			    ch = 70 + ACURR(A_DEX);
421			    break;
422			default:    ch = 0;
423		    }
424		    xlock.door = door;
425		    xlock.box = 0;
426	    }
427	}
428	flags.move = 0;
429	xlock.chance = ch;
430	xlock.picktyp = picktyp;
431	xlock.usedtime = 0;
432	set_occupation(picklock, lock_action(), 0);
433	return(1);
434}
435
436int
437doforce()		/* try to force a chest with your weapon */
438{
439	register struct obj *otmp;
440	register int c, picktyp;
441	char qbuf[QBUFSZ];
442
443	if(!uwep ||	/* proper type test */
444	   (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep) &&
445	    uwep->oclass != ROCK_CLASS) ||
446	   (objects[uwep->otyp].oc_skill < P_DAGGER) ||
447	   (objects[uwep->otyp].oc_skill > P_LANCE) ||
448	   uwep->otyp == FLAIL || uwep->otyp == AKLYS
449#ifdef KOPS
450	   || uwep->otyp == RUBBER_HOSE
451#endif
452	  ) {
453	    You_cant("force anything without a %sweapon.",
454		  (uwep) ? "proper " : "");
455	    return(0);
456	}
457
458	picktyp = is_blade(uwep);
459	if(xlock.usedtime && xlock.box && picktyp == xlock.picktyp) {
460	    You("resume your attempt to force the lock.");
461	    set_occupation(forcelock, "forcing the lock", 0);
462	    return(1);
463	}
464
465	/* A lock is made only for the honest man, the thief will break it. */
466	xlock.box = (struct obj *)0;
467	for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere)
468	    if(Is_box(otmp)) {
469		if (otmp->obroken || !otmp->olocked) {
470		    There("is %s here, but its lock is already %s.",
471			  doname(otmp), otmp->obroken ? "broken" : "unlocked");
472		    continue;
473		}
474		Sprintf(qbuf,"There is %s here, force its lock?",
475			safe_qbuf("", sizeof("There is  here, force its lock?"),
476				doname(otmp), an(simple_typename(otmp->otyp)),
477				"a box"));
478
479		c = ynq(qbuf);
480		if(c == 'q') return(0);
481		if(c == 'n') continue;
482
483		if(picktyp)
484		    You("force your %s into a crack and pry.", xname(uwep));
485		else
486		    You("start bashing it with your %s.", xname(uwep));
487		xlock.box = otmp;
488		xlock.chance = objects[uwep->otyp].oc_wldam * 2;
489		xlock.picktyp = picktyp;
490		xlock.usedtime = 0;
491		break;
492	    }
493
494	if(xlock.box)	set_occupation(forcelock, "forcing the lock", 0);
495	else		You("decide not to force the issue.");
496	return(1);
497}
498
499int
500doopen()		/* try to open a door */
501{
502	coord cc;
503	register struct rm *door;
504	struct monst *mtmp;
505
506	if (nohands(youmonst.data)) {
507	    You_cant("open anything -- you have no hands!");
508	    return 0;
509	}
510
511	if (u.utrap && u.utraptype == TT_PIT) {
512	    You_cant("reach over the edge of the pit.");
513	    return 0;
514	}
515
516	if(!get_adjacent_loc((char *)0, (char *)0, u.ux, u.uy, &cc)) return(0);
517
518	if((cc.x == u.ux) && (cc.y == u.uy)) return(0);
519
520	if ((mtmp = m_at(cc.x,cc.y))			&&
521		mtmp->m_ap_type == M_AP_FURNITURE	&&
522		(mtmp->mappearance == S_hcdoor ||
523			mtmp->mappearance == S_vcdoor)	&&
524		!Protection_from_shape_changers)	 {
525
526	    stumble_onto_mimic(mtmp);
527	    return(1);
528	}
529
530	door = &levl[cc.x][cc.y];
531
532	if(!IS_DOOR(door->typ)) {
533		if (is_db_wall(cc.x,cc.y)) {
534		    There("is no obvious way to open the drawbridge.");
535		    return(0);
536		}
537		You("%s no door there.",
538				Blind ? "feel" : "see");
539		return(0);
540	}
541
542	if (!(door->doormask & D_CLOSED)) {
543	    const char *mesg;
544
545	    switch (door->doormask) {
546	    case D_BROKEN: mesg = " is broken"; break;
547	    case D_NODOOR: mesg = "way has no door"; break;
548	    case D_ISOPEN: mesg = " is already open"; break;
549	    default:	   mesg = " is locked"; break;
550	    }
551	    pline("This door%s.", mesg);
552	    if (Blind) feel_location(cc.x,cc.y);
553	    return(0);
554	}
555
556	if(verysmall(youmonst.data)) {
557	    pline("You're too small to pull the door open.");
558	    return(0);
559	}
560
561	/* door is known to be CLOSED */
562	if (rnl(20) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) {
563	    pline_The("door opens.");
564	    if(door->doormask & D_TRAPPED) {
565		b_trapped("door", FINGER);
566		door->doormask = D_NODOOR;
567		if (*in_rooms(cc.x, cc.y, SHOPBASE)) add_damage(cc.x, cc.y, 0L);
568	    } else
569		door->doormask = D_ISOPEN;
570	    if (Blind)
571		feel_location(cc.x,cc.y);	/* the hero knows she opened it  */
572	    else
573		newsym(cc.x,cc.y);
574	    unblock_point(cc.x,cc.y);		/* vision: new see through there */
575	} else {
576	    exercise(A_STR, TRUE);
577	    pline_The("door resists!");
578	}
579
580	return(1);
581}
582
583STATIC_OVL
584boolean
585obstructed(x,y)
586register int x, y;
587{
588	register struct monst *mtmp = m_at(x, y);
589
590	if(mtmp && mtmp->m_ap_type != M_AP_FURNITURE) {
591		if (mtmp->m_ap_type == M_AP_OBJECT) goto objhere;
592		pline("%s stands in the way!", !canspotmon(mtmp) ?
593			"Some creature" : Monnam(mtmp));
594		if (!canspotmon(mtmp))
595		    map_invisible(mtmp->mx, mtmp->my);
596		return(TRUE);
597	}
598	if (OBJ_AT(x, y)) {
599objhere:	pline("%s's in the way.", Something);
600		return(TRUE);
601	}
602	return(FALSE);
603}
604
605int
606doclose()		/* try to close a door */
607{
608	register int x, y;
609	register struct rm *door;
610	struct monst *mtmp;
611
612	if (nohands(youmonst.data)) {
613	    You_cant("close anything -- you have no hands!");
614	    return 0;
615	}
616
617	if (u.utrap && u.utraptype == TT_PIT) {
618	    You_cant("reach over the edge of the pit.");
619	    return 0;
620	}
621
622	if(!getdir((char *)0)) return(0);
623
624	x = u.ux + u.dx;
625	y = u.uy + u.dy;
626	if((x == u.ux) && (y == u.uy)) {
627		You("are in the way!");
628		return(1);
629	}
630
631	if ((mtmp = m_at(x,y))				&&
632		mtmp->m_ap_type == M_AP_FURNITURE	&&
633		(mtmp->mappearance == S_hcdoor ||
634			mtmp->mappearance == S_vcdoor)	&&
635		!Protection_from_shape_changers)	 {
636
637	    stumble_onto_mimic(mtmp);
638	    return(1);
639	}
640
641	door = &levl[x][y];
642
643	if(!IS_DOOR(door->typ)) {
644		if (door->typ == DRAWBRIDGE_DOWN)
645		    There("is no obvious way to close the drawbridge.");
646		else
647		    You("%s no door there.",
648				Blind ? "feel" : "see");
649		return(0);
650	}
651
652	if(door->doormask == D_NODOOR) {
653	    pline("This doorway has no door.");
654	    return(0);
655	}
656
657	if(obstructed(x, y)) return(0);
658
659	if(door->doormask == D_BROKEN) {
660	    pline("This door is broken.");
661	    return(0);
662	}
663
664	if(door->doormask & (D_CLOSED | D_LOCKED)) {
665	    pline("This door is already closed.");
666	    return(0);
667	}
668
669	if(door->doormask == D_ISOPEN) {
670	    if(verysmall(youmonst.data)
671#ifdef STEED
672		&& !u.usteed
673#endif
674		) {
675		 pline("You're too small to push the door closed.");
676		 return(0);
677	    }
678	    if (
679#ifdef STEED
680		 u.usteed ||
681#endif
682		rn2(25) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) {
683		pline_The("door closes.");
684		door->doormask = D_CLOSED;
685		if (Blind)
686		    feel_location(x,y);	/* the hero knows she closed it */
687		else
688		    newsym(x,y);
689		block_point(x,y);	/* vision:  no longer see there */
690	    }
691	    else {
692	        exercise(A_STR, TRUE);
693	        pline_The("door resists!");
694	    }
695	}
696
697	return(1);
698}
699
700boolean			/* box obj was hit with spell effect otmp */
701boxlock(obj, otmp)	/* returns true if something happened */
702register struct obj *obj, *otmp;	/* obj *is* a box */
703{
704	register boolean res = 0;
705
706	switch(otmp->otyp) {
707	case WAN_LOCKING:
708	case SPE_WIZARD_LOCK:
709	    if (!obj->olocked) {	/* lock it; fix if broken */
710		pline("Klunk!");
711		obj->olocked = 1;
712		obj->obroken = 0;
713		res = 1;
714	    } /* else already closed and locked */
715	    break;
716	case WAN_OPENING:
717	case SPE_KNOCK:
718	    if (obj->olocked) {		/* unlock; couldn't be broken */
719		pline("Klick!");
720		obj->olocked = 0;
721		res = 1;
722	    } else			/* silently fix if broken */
723		obj->obroken = 0;
724	    break;
725	case WAN_POLYMORPH:
726	case SPE_POLYMORPH:
727	    /* maybe start unlocking chest, get interrupted, then zap it;
728	       we must avoid any attempt to resume unlocking it */
729	    if (xlock.box == obj)
730		reset_pick();
731	    break;
732	}
733	return res;
734}
735
736boolean			/* Door/secret door was hit with spell effect otmp */
737doorlock(otmp,x,y)	/* returns true if something happened */
738struct obj *otmp;
739int x, y;
740{
741	register struct rm *door = &levl[x][y];
742	boolean res = TRUE;
743	int loudness = 0;
744	const char *msg = (const char *)0;
745	const char *dustcloud = "A cloud of dust";
746	const char *quickly_dissipates = "quickly dissipates";
747
748	if (door->typ == SDOOR) {
749	    switch (otmp->otyp) {
750	    case WAN_OPENING:
751	    case SPE_KNOCK:
752	    case WAN_STRIKING:
753	    case SPE_FORCE_BOLT:
754		door->typ = DOOR;
755		door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
756		newsym(x,y);
757		if (cansee(x,y)) pline("A door appears in the wall!");
758		if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK)
759		    return TRUE;
760		break;		/* striking: continue door handling below */
761	    case WAN_LOCKING:
762	    case SPE_WIZARD_LOCK:
763	    default:
764		return FALSE;
765	    }
766	}
767
768	switch(otmp->otyp) {
769	case WAN_LOCKING:
770	case SPE_WIZARD_LOCK:
771#ifdef REINCARNATION
772	    if (Is_rogue_level(&u.uz)) {
773	    	boolean vis = cansee(x,y);
774		/* Can't have real locking in Rogue, so just hide doorway */
775		if (vis) pline("%s springs up in the older, more primitive doorway.",
776			dustcloud);
777		else
778			You_hear("a swoosh.");
779		if (obstructed(x,y)) {
780			if (vis) pline_The("cloud %s.",quickly_dissipates);
781			return FALSE;
782		}
783		block_point(x, y);
784		door->typ = SDOOR;
785		if (vis) pline_The("doorway vanishes!");
786		newsym(x,y);
787		return TRUE;
788	    }
789#endif
790	    if (obstructed(x,y)) return FALSE;
791	    /* Don't allow doors to close over traps.  This is for pits */
792	    /* & trap doors, but is it ever OK for anything else? */
793	    if (t_at(x,y)) {
794		/* maketrap() clears doormask, so it should be NODOOR */
795		pline(
796		"%s springs up in the doorway, but %s.",
797		dustcloud, quickly_dissipates);
798		return FALSE;
799	    }
800
801	    switch (door->doormask & ~D_TRAPPED) {
802	    case D_CLOSED:
803		msg = "The door locks!";
804		break;
805	    case D_ISOPEN:
806		msg = "The door swings shut, and locks!";
807		break;
808	    case D_BROKEN:
809		msg = "The broken door reassembles and locks!";
810		break;
811	    case D_NODOOR:
812		msg =
813		"A cloud of dust springs up and assembles itself into a door!";
814		break;
815	    default:
816		res = FALSE;
817		break;
818	    }
819	    block_point(x, y);
820	    door->doormask = D_LOCKED | (door->doormask & D_TRAPPED);
821	    newsym(x,y);
822	    break;
823	case WAN_OPENING:
824	case SPE_KNOCK:
825	    if (door->doormask & D_LOCKED) {
826		msg = "The door unlocks!";
827		door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
828	    } else res = FALSE;
829	    break;
830	case WAN_STRIKING:
831	case SPE_FORCE_BOLT:
832	    if (door->doormask & (D_LOCKED | D_CLOSED)) {
833		if (door->doormask & D_TRAPPED) {
834		    if (MON_AT(x, y))
835			(void) mb_trapped(m_at(x,y));
836		    else if (flags.verbose) {
837			if (cansee(x,y))
838			    pline("KABOOM!!  You see a door explode.");
839			else if (flags.soundok)
840			    You_hear("a distant explosion.");
841		    }
842		    door->doormask = D_NODOOR;
843		    unblock_point(x,y);
844		    newsym(x,y);
845		    loudness = 40;
846		    break;
847		}
848		door->doormask = D_BROKEN;
849		if (flags.verbose) {
850		    if (cansee(x,y))
851			pline_The("door crashes open!");
852		    else if (flags.soundok)
853			You_hear("a crashing sound.");
854		}
855		unblock_point(x,y);
856		newsym(x,y);
857		/* force vision recalc before printing more messages */
858		if (vision_full_recalc) vision_recalc(0);
859		loudness = 20;
860	    } else res = FALSE;
861	    break;
862	default: impossible("magic (%d) attempted on door.", otmp->otyp);
863	    break;
864	}
865	if (msg && cansee(x,y)) pline(msg);
866	if (loudness > 0) {
867	    /* door was destroyed */
868	    wake_nearto(x, y, loudness);
869	    if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L);
870	}
871
872	if (res && picking_at(x, y)) {
873	    /* maybe unseen monster zaps door you're unlocking */
874	    stop_occupation();
875	    reset_pick();
876	}
877	return res;
878}
879
880STATIC_OVL void
881chest_shatter_msg(otmp)
882struct obj *otmp;
883{
884	const char *disposition;
885	const char *thing;
886	long save_Blinded;
887
888	if (otmp->oclass == POTION_CLASS) {
889		You("%s %s shatter!", Blind ? "hear" : "see", an(bottlename()));
890		if (!breathless(youmonst.data) || haseyes(youmonst.data))
891			potionbreathe(otmp);
892		return;
893	}
894	/* We have functions for distant and singular names, but not one */
895	/* which does _both_... */
896	save_Blinded = Blinded;
897	Blinded = 1;
898	thing = singular(otmp, xname);
899	Blinded = save_Blinded;
900	switch (objects[otmp->otyp].oc_material) {
901	case PAPER:	disposition = "is torn to shreds";
902		break;
903	case WAX:	disposition = "is crushed";
904		break;
905	case VEGGY:	disposition = "is pulped";
906		break;
907	case FLESH:	disposition = "is mashed";
908		break;
909	case GLASS:	disposition = "shatters";
910		break;
911	case WOOD:	disposition = "splinters to fragments";
912		break;
913	default:	disposition = "is destroyed";
914		break;
915	}
916	pline("%s %s!", An(thing), disposition);
917}
918
919#endif /* OVLB */
920
921/*lock.c*/
922