1    | /*	SCCS Id: @(#)steal.c	3.3	1999/02/13	*/
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(stealarm);
8    | 
9    | #ifdef OVLB
10   | STATIC_DCL const char *FDECL(equipname, (struct obj *));
11   | 
12   | STATIC_OVL const char *
13   | equipname(otmp)
14   | register struct obj *otmp;
15   | {
16   | 	return (
17   | #ifdef TOURIST
18   | 		(otmp == uarmu) ? "shirt" :
19   | #endif
20   | 		(otmp == uarmf) ? "boots" :
21   | 		(otmp == uarms) ? "shield" :
22   | 		(otmp == uarmg) ? "gloves" :
23   | 		(otmp == uarmc) ? "cloak" :
24   | 		(otmp == uarmh) ? "helmet" : "armor");
25   | }
26   | 
27   | long		/* actually returns something that fits in an int */
28   | somegold()
29   | {
30   | #ifdef LINT	/* long conv. ok */
31   | 	return(0L);
32   | #else
33   | 	return (long)( (u.ugold < 100) ? u.ugold :
34   | 		(u.ugold > 10000) ? rnd(10000) : rnd((int) u.ugold) );
35   | #endif
36   | }
37   | 
38   | void
39   | stealgold(mtmp)
40   | register struct monst *mtmp;
41   | {
42   | 	register struct obj *gold = g_at(u.ux, u.uy);
43   | 	register long tmp;
44   | 
45   | 	if (gold && ( !u.ugold || gold->quan > u.ugold || !rn2(5))) {
46   | 	    mtmp->mgold += gold->quan;
47   | 	    delobj(gold);
48   | 	    newsym(u.ux, u.uy);
49   | 	    pline("%s quickly snatches some gold from between your %s!",
50   | 		    Monnam(mtmp), makeplural(body_part(FOOT)));
51   | 	    if(!u.ugold || !rn2(5)) {
52   | 		if (!tele_restrict(mtmp)) rloc(mtmp);
53   | 		mtmp->mflee = 1;
54   | 	    }
55   | 	} else if(u.ugold) {
56   | 	    u.ugold -= (tmp = somegold());
57   | 	    Your("purse feels lighter.");
58   | 	    mtmp->mgold += tmp;
59   | 	    if (!tele_restrict(mtmp)) rloc(mtmp);
60   | 	    mtmp->mflee = 1;
61   | 	    flags.botl = 1;
62   | 	}
63   | }
64   | 
65   | /* steal armor after you finish taking it off */
66   | unsigned int stealoid;		/* object to be stolen */
67   | unsigned int stealmid;		/* monster doing the stealing */
68   | 
69   | STATIC_PTR int
70   | stealarm()
71   | {
72   | 	register struct monst *mtmp;
73   | 	register struct obj *otmp;
74   | 
75   | 	for(otmp = invent; otmp; otmp = otmp->nobj) {
76   | 	    if(otmp->o_id == stealoid) {
77   | 		for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
78   | 		    if(mtmp->m_id == stealmid) {
79   | 			if(DEADMONSTER(mtmp)) impossible("stealarm(): dead monster stealing"); 
80   | 			if(!dmgtype(mtmp->data, AD_SITM)) /* polymorphed */
81   | 			    goto botm;
82   | 			if(otmp->unpaid)
83   | 			    subfrombill(otmp, shop_keeper(*u.ushops));
84   | 			freeinv(otmp);
85   | 			pline("%s steals %s!", Monnam(mtmp), doname(otmp));
86   | 			(void) mpickobj(mtmp,otmp);	/* may free otmp */
87   | 			mtmp->mflee = 1;
88   | 			if (!tele_restrict(mtmp)) rloc(mtmp);
89   | 		        break;
90   | 		    }
91   | 		}
92   | 		break;
93   | 	    }
94   | 	}
95   | botm:   stealoid = 0;
96   | 	return 0;
97   | }
98   | 
99   | /* An object you're wearing has been taken off my a monster (theft or
100  |    seduction).  Also used if a worn item gets transformed (stone to flesh). */
101  | void
102  | remove_worn_item(obj)
103  | struct obj *obj;
104  | {
105  | 	if (donning(obj))
106  | 	    cancel_don();
107  | 	if (!obj->owornmask)
108  | 	    return;
109  | 
110  | 	switch (obj->oclass) {
111  | 	 case TOOL_CLASS:
112  | 	    if (obj == ublindf) Blindf_off(obj);
113  | 	    break;
114  | 	 case AMULET_CLASS:
115  | 	    Amulet_off();
116  | 	    break;
117  | 	 case RING_CLASS:
118  | 	 case FOOD_CLASS: /* meat ring */
119  | 	    Ring_gone(obj);
120  | 	    break;
121  | 	 case ARMOR_CLASS:
122  | 	    if (obj == uarm) (void) Armor_off();
123  | 	    else if (obj == uarmc) (void) Cloak_off();
124  | 	    else if (obj == uarmf) (void) Boots_off();
125  | 	    else if (obj == uarmg) (void) Gloves_off();
126  | 	    else if (obj == uarmh) (void) Helmet_off();
127  | 	    else if (obj == uarms) (void) Shield_off();
128  | 	    else setworn((struct obj *)0, obj->owornmask & W_ARMOR);
129  | 	    break;
130  | 	 default:
131  | 	    /* shouldn't reach here, but just in case... */
132  | 	    setnotworn(obj);
133  | 	    break;
134  | 	}
135  | }
136  | 
137  | /* Returns 1 when something was stolen (or at least, when N should flee now)
138  |  * Returns -1 if the monster died in the attempt
139  |  * Avoid stealing the object stealoid
140  |  */
141  | int
142  | steal(mtmp)
143  | struct monst *mtmp;
144  | {
145  | 	struct obj *otmp;
146  | 	int tmp, could_petrify, named = 0;
147  | 
148  | 	/* the following is true if successful on first of two attacks. */
149  | 	if(!monnear(mtmp, u.ux, u.uy)) return(0);
150  | 
151  | 	if (!invent || (inv_cnt() == 1 && uskin)) {
152  | nothing_to_steal:
153  | 	    /* Not even a thousand men in armor can strip a naked man. */
154  | 	    if(Blind)
155  | 	      pline("Somebody tries to rob you, but finds nothing to steal.");
156  | 	    else
157  | 	      pline("%s tries to rob you, but there is nothing to steal!",
158  | 		Monnam(mtmp));
159  | 	    return(1);	/* let her flee */
160  | 	}
161  | 
162  | 	if (Adornment & LEFT_RING) {
163  | 	    otmp = uleft;
164  | 	    goto gotobj;
165  | 	} else if (Adornment & RIGHT_RING) {
166  | 	    otmp = uright;
167  | 	    goto gotobj;
168  | 	}
169  | 
170  | 	tmp = 0;
171  | 	for(otmp = invent; otmp; otmp = otmp->nobj)
172  | 	    if ((!uarm || otmp != uarmc) && otmp != uskin
173  | #ifdef INVISIBLE_OBJECTS
174  | 				&& (!otmp->oinvis || perceives(mtmp->data))
175  | #endif
176  | 				)
177  | 		tmp += ((otmp->owornmask &
178  | 			(W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1);
179  | 	if (!tmp) goto nothing_to_steal;
180  | 	tmp = rn2(tmp);
181  | 	for(otmp = invent; otmp; otmp = otmp->nobj)
182  | 	    if ((!uarm || otmp != uarmc) && otmp != uskin
183  | #ifdef INVISIBLE_OBJECTS
184  | 				&& (!otmp->oinvis || perceives(mtmp->data))
185  | #endif
186  | 	    		)
187  | 		if((tmp -= ((otmp->owornmask &
188  | 			(W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1)) < 0)
189  | 			break;
190  | 	if(!otmp) {
191  | 		impossible("Steal fails!");
192  | 		return(0);
193  | 	}
194  | 	/* can't steal gloves while wielding - so steal the wielded item. */
195  | 	if (otmp == uarmg && uwep)
196  | 	    otmp = uwep;
197  | 	/* can't steal armor while wearing cloak - so steal the cloak. */
198  | 	else if(otmp == uarm && uarmc) otmp = uarmc;
199  | #ifdef TOURIST
200  | 	else if(otmp == uarmu && uarmc) otmp = uarmc;
201  | 	else if(otmp == uarmu && uarm) otmp = uarm;
202  | #endif
203  | gotobj:
204  | 	if(otmp->o_id == stealoid) return(0);
205  | 
206  | 	if(otmp->otyp == LEASH && otmp->leashmon) o_unleash(otmp);
207  | 
208  | 	if((otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))){
209  | 		switch(otmp->oclass) {
210  | 		case TOOL_CLASS:
211  | 		case AMULET_CLASS:
212  | 		case RING_CLASS:
213  | 		case FOOD_CLASS: /* meat ring */
214  | 			remove_worn_item(otmp);
215  | 			break;
216  | 		case ARMOR_CLASS:
217  | 			/* Stop putting on armor which has been stolen. */
218  | 			if (donning(otmp) || is_animal(mtmp->data)) {
219  | 			    remove_worn_item(otmp);
220  | 			    break;
221  | 			} else {
222  | 			int curssv = otmp->cursed;
223  | 
224  | 			otmp->cursed = 0;
225  | 			stop_occupation();
226  | 			if(flags.female)
227  | 			    pline("%s charms you.  You gladly %s your %s.",
228  | 				  Blind ? "She" : Monnam(mtmp),
229  | 				  curssv ? "let her take" :
230  | 	(objects[otmp->otyp].oc_delay > 1) ? "start removing" : "hand over",
231  | 				  equipname(otmp));
232  | 			else
233  | 			    pline("%s seduces you and %s off your %s.",
234  | 				  Blind ? "It" : Adjmonnam(mtmp, "beautiful"),
235  | 				  curssv ? "helps you to take" :
236  | 	(objects[otmp->otyp].oc_delay > 1) ? "you start taking" : "you take",
237  | 				  equipname(otmp));
238  | 			named++;
239  | 			/* the following is to set multi for later on */
240  | 			nomul(-objects[otmp->otyp].oc_delay);
241  | 			remove_worn_item(otmp);
242  | 			otmp->cursed = curssv;
243  | 			if(multi < 0){
244  | 				/*
245  | 				multi = 0;
246  | 				nomovemsg = 0;
247  | 				afternmv = 0;
248  | 				*/
249  | 				stealoid = otmp->o_id;
250  | 				stealmid = mtmp->m_id;
251  | 				afternmv = stealarm;
252  | 				return(0);
253  | 			}
254  | 		    }
255  | 			break;
256  | 		default:
257  | 			impossible("Tried to steal a strange worn thing.");
258  | 		}
259  | 	}
260  | 	else if (otmp == uwep) uwepgone();
261  | 	else if (otmp == uquiver) uqwepgone();
262  | 	else if (otmp == uswapwep) uswapwepgone();
263  | 
264  | 	if(otmp == uball) unpunish();
265  | 
266  | 	freeinv(otmp);
267  | 	pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp));
268  | 	could_petrify = otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]);
269  | 	(void) mpickobj(mtmp,otmp);	/* may free otmp */
270  | 	if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) {
271  | 	    minstapetrify(mtmp, TRUE);
272  | 	    return -1;
273  | 	}
274  | 	return((multi < 0) ? 0 : 1);
275  | }
276  | 
277  | #endif /* OVLB */
278  | #ifdef OVL1
279  | 
280  | /* Returns 1 if otmp is free'd, 0 otherwise. */
281  | int
282  | mpickobj(mtmp,otmp)
283  | register struct monst *mtmp;
284  | register struct obj *otmp;
285  | {
286  |     int freed_otmp;
287  | 
288  |     if (otmp->oclass == GOLD_CLASS) {
289  | 	mtmp->mgold += otmp->quan;
290  | 	obfree(otmp, (struct obj *)0);
291  | 	freed_otmp = 1;
292  |     } else {
293  | 	boolean snuff_otmp = FALSE;
294  | 	/* don't want hidden light source inside the monster; assumes that
295  | 	   engulfers won't have external inventories; whirly monsters cause
296  | 	   the light to be extinguished rather than letting it shine thru */
297  | 	if (otmp->lamplit &&  /* hack to avoid function calls for most objs */
298  | 		obj_sheds_light(otmp) &&
299  | 		attacktype(mtmp->data, AT_ENGL)) {
300  | 	    /* this is probably a burning object that you dropped or threw */
301  | 	    if (u.uswallow && mtmp == u.ustuck && !Blind)
302  | 		pline("%s go%s out.", The(xname(otmp)),
303  | 		      otmp->quan == 1L ? "es" : "");
304  | 	    snuff_otmp = TRUE;
305  | 	}
306  | 	/* Must do carrying effects on object prior to add_to_minv() */
307  | 	carry_obj_effects(otmp);
308  | 	/* add_to_minv() might free otmp [if merged with something else],
309  | 	   so we have to call it after doing the object checks */
310  | 	freed_otmp = add_to_minv(mtmp, otmp);
311  | 	/* and we had to defer this until object is in mtmp's inventory */
312  | 	if (snuff_otmp) snuff_light_source(mtmp->mx, mtmp->my);
313  |     }
314  |     return freed_otmp;
315  | }
316  | 
317  | #endif /* OVL1 */
318  | #ifdef OVLB
319  | 
320  | void
321  | stealamulet(mtmp)
322  | struct monst *mtmp;
323  | {
324  |     struct obj *otmp = (struct obj *)0;
325  |     int real=0, fake=0;
326  | 
327  |     /* select the artifact to steal */
328  |     if(u.uhave.amulet) {
329  | 	real = AMULET_OF_YENDOR;
330  | 	fake = FAKE_AMULET_OF_YENDOR;
331  |     } else if(u.uhave.questart) {
332  | 	for(otmp = invent; otmp; otmp = otmp->nobj)
333  | 	    if(is_quest_artifact(otmp)) break;
334  | 	if (!otmp) return;	/* should we panic instead? */
335  |     } else if(u.uhave.bell) {
336  | 	real = BELL_OF_OPENING;
337  | 	fake = BELL;
338  |     } else if(u.uhave.book) {
339  | 	real = SPE_BOOK_OF_THE_DEAD;
340  |     } else if(u.uhave.menorah) {
341  | 	real = CANDELABRUM_OF_INVOCATION;
342  |     } else return;	/* you have nothing of special interest */
343  | 
344  |     if (!otmp) {
345  | 	/* If we get here, real and fake have been set up. */
346  | 	for(otmp = invent; otmp; otmp = otmp->nobj)
347  | 	    if(otmp->otyp == real || (otmp->otyp == fake && !mtmp->iswiz))
348  | 		break;
349  |     }
350  | 
351  |     if (otmp) { /* we have something to snatch */
352  | 	if (otmp->owornmask)
353  | 	    remove_worn_item(otmp);
354  | 	freeinv(otmp);
355  | 	/* mpickobj wont merge otmp because none of the above things
356  | 	   to steal are mergable */
357  | 	(void) mpickobj(mtmp,otmp);	/* may merge and free otmp */
358  | 	pline("%s stole %s!", Monnam(mtmp), doname(otmp));
359  | 	if (can_teleport(mtmp->data) && !tele_restrict(mtmp))
360  | 	    rloc(mtmp);
361  |     }
362  | }
363  | 
364  | #endif /* OVLB */
365  | #ifdef OVL0
366  | 
367  | /* release the objects the creature is carrying */
368  | void
369  | relobj(mtmp,show,is_pet)
370  | register struct monst *mtmp;
371  | register int show;
372  | boolean is_pet;		/* If true, pet should keep wielded/worn items */
373  | {
374  | 	register struct obj *otmp;
375  | 	register int omx = mtmp->mx, omy = mtmp->my;
376  | 	struct obj *keepobj = 0;
377  | 	struct obj *wep = MON_WEP(mtmp);
378  | 	boolean item1 = FALSE, item2 = FALSE;
379  | 
380  | 	if (!is_pet || mindless(mtmp->data) || is_animal(mtmp->data))
381  | 		item1 = item2 = TRUE;
382  | 	if (!tunnels(mtmp->data) || !needspick(mtmp->data))
383  | 		item1 = TRUE;
384  | 	while ((otmp = mtmp->minvent) != 0) {
385  | 		obj_extract_self(otmp);
386  | 		/* special case: pick-axe and unicorn horn are non-worn */
387  | 		/* items that we also want pets to keep 1 of */
388  | 		/* (It is a coincidence that these can also be wielded. */
389  | 		if (otmp->owornmask || otmp == wep ||
390  | 		    ((!item1 && otmp->otyp == PICK_AXE) ||
391  | 		     (!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) {
392  | 			if (is_pet) { /* dont drop worn/wielded item */
393  | 				if (otmp->otyp == PICK_AXE)
394  | 					item1 = TRUE;
395  | 				if (otmp->otyp == UNICORN_HORN && !otmp->cursed)
396  | 					item2 = TRUE;
397  | 				otmp->nobj = keepobj;
398  | 				keepobj = otmp;
399  | 				continue;
400  | 			}
401  | 			mtmp->misc_worn_check &= ~(otmp->owornmask);
402  | 			otmp->owornmask = 0L;
403  | 		}
404  | 		if (is_pet && cansee(omx, omy) && flags.verbose)
405  | 			pline("%s drops %s.", Monnam(mtmp),
406  | 					distant_name(otmp, doname));
407  | 		if (flooreffects(otmp, omx, omy, "fall")) continue;
408  | 		place_object(otmp, omx, omy);
409  | 		stackobj(otmp);
410  | 	}
411  | 	/* put kept objects back */
412  | 	while ((otmp = keepobj) != (struct obj *)0) {
413  | 	    keepobj = otmp->nobj;
414  | 	    (void) add_to_minv(mtmp, otmp);
415  | 	}
416  | 
417  | 	if (mtmp->mgold) {
418  | 		register long g = mtmp->mgold;
419  | 		(void) mkgold(g, omx, omy);
420  | 		if (is_pet && cansee(omx, omy) && flags.verbose)
421  | 			pline("%s drops %ld gold piece%s.", Monnam(mtmp),
422  | 				g, plur(g));
423  | 		mtmp->mgold = 0L;
424  | 	}
425  | 	if (show & cansee(omx, omy))
426  | 		newsym(omx, omy);
427  | }
428  | 
429  | #endif /* OVL0 */
430  | 
431  | /*steal.c*/