1/*	SCCS Id: @(#)steal.c	3.4	2003/12/04	*/
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(stealarm);
8
9#ifdef OVLB
10STATIC_DCL const char *FDECL(equipname, (struct obj *));
11STATIC_DCL void FDECL(mdrop_obj, (struct monst *,struct obj *,BOOLEAN_P));
12
13STATIC_OVL const char *
14equipname(otmp)
15register struct obj *otmp;
16{
17	return (
18#ifdef TOURIST
19		(otmp == uarmu) ? "shirt" :
20#endif
21		(otmp == uarmf) ? "boots" :
22		(otmp == uarms) ? "shield" :
23		(otmp == uarmg) ? "gloves" :
24		(otmp == uarmc) ? cloak_simple_name(otmp) :
25		(otmp == uarmh) ? "helmet" : "armor");
26}
27
28#ifndef GOLDOBJ
29long		/* actually returns something that fits in an int */
30somegold()
31{
32#ifdef LINT	/* long conv. ok */
33	return(0L);
34#else
35	return (long)( (u.ugold < 100) ? u.ugold :
36		(u.ugold > 10000) ? rnd(10000) : rnd((int) u.ugold) );
37#endif
38}
39
40void
41stealgold(mtmp)
42register struct monst *mtmp;
43{
44	register struct obj *gold = g_at(u.ux, u.uy);
45	register long tmp;
46
47	if (gold && ( !u.ugold || gold->quan > u.ugold || !rn2(5))) {
48	    mtmp->mgold += gold->quan;
49	    delobj(gold);
50	    newsym(u.ux, u.uy);
51	    pline("%s quickly snatches some gold from between your %s!",
52		    Monnam(mtmp), makeplural(body_part(FOOT)));
53	    if(!u.ugold || !rn2(5)) {
54		if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE);
55		/* do not set mtmp->mavenge here; gold on the floor is fair game */
56		monflee(mtmp, 0, FALSE, FALSE);
57	    }
58	} else if(u.ugold) {
59	    u.ugold -= (tmp = somegold());
60	    Your("purse feels lighter.");
61	    mtmp->mgold += tmp;
62	if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE);
63	    mtmp->mavenge = 1;
64	    monflee(mtmp, 0, FALSE, FALSE);
65	    flags.botl = 1;
66	}
67}
68
69#else /* !GOLDOBJ */
70
71long		/* actually returns something that fits in an int */
72somegold(umoney)
73long umoney;
74{
75#ifdef LINT	/* long conv. ok */
76	return(0L);
77#else
78	return (long)( (umoney < 100) ? umoney :
79		(umoney > 10000) ? rnd(10000) : rnd((int) umoney) );
80#endif
81}
82
83/*
84Find the first (and hopefully only) gold object in a chain.
85Used when leprechaun (or you as leprechaun) looks for
86someone else's gold.  Returns a pointer so the gold may
87be seized without further searching.
88May search containers too.
89Deals in gold only, as leprechauns don't care for lesser coins.
90*/
91struct obj *
92findgold(chain)
93register struct obj *chain;
94{
95        while (chain && chain->otyp != GOLD_PIECE) chain = chain->nobj;
96        return chain;
97}
98
99/*
100Steal gold coins only.  Leprechauns don't care for lesser coins.
101*/
102void
103stealgold(mtmp)
104register struct monst *mtmp;
105{
106	register struct obj *fgold = g_at(u.ux, u.uy);
107	register struct obj *ygold;
108	register long tmp;
109
110        /* skip lesser coins on the floor */
111        while (fgold && fgold->otyp != GOLD_PIECE) fgold = fgold->nexthere;
112
113        /* Do you have real gold? */
114        ygold = findgold(invent);
115
116	if (fgold && ( !ygold || fgold->quan > ygold->quan || !rn2(5))) {
117            obj_extract_self(fgold);
118	    add_to_minv(mtmp, fgold);
119	    newsym(u.ux, u.uy);
120	    pline("%s quickly snatches some gold from between your %s!",
121		    Monnam(mtmp), makeplural(body_part(FOOT)));
122	    if(!ygold || !rn2(5)) {
123		if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE);
124		monflee(mtmp, 0, FALSE, FALSE);
125	    }
126	} else if(ygold) {
127            const int gold_price = objects[GOLD_PIECE].oc_cost;
128	    tmp = (somegold(money_cnt(invent)) + gold_price - 1) / gold_price;
129	    tmp = min(tmp, ygold->quan);
130            if (tmp < ygold->quan) ygold = splitobj(ygold, tmp);
131            freeinv(ygold);
132            add_to_minv(mtmp, ygold);
133	    Your("purse feels lighter.");
134	    if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE);
135	    monflee(mtmp, 0, FALSE, FALSE);
136	    flags.botl = 1;
137	}
138}
139#endif /* GOLDOBJ */
140
141/* steal armor after you finish taking it off */
142unsigned int stealoid;		/* object to be stolen */
143unsigned int stealmid;		/* monster doing the stealing */
144
145STATIC_PTR int
146stealarm()
147{
148	register struct monst *mtmp;
149	register struct obj *otmp;
150
151	for(otmp = invent; otmp; otmp = otmp->nobj) {
152	    if(otmp->o_id == stealoid) {
153		for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
154		    if(mtmp->m_id == stealmid) {
155			if(DEADMONSTER(mtmp)) impossible("stealarm(): dead monster stealing");
156			if(!dmgtype(mtmp->data, AD_SITM)) /* polymorphed */
157			    goto botm;
158			if(otmp->unpaid)
159			    subfrombill(otmp, shop_keeper(*u.ushops));
160			freeinv(otmp);
161			pline("%s steals %s!", Monnam(mtmp), doname(otmp));
162			(void) mpickobj(mtmp,otmp);	/* may free otmp */
163			/* Implies seduction, "you gladly hand over ..."
164			   so we don't set mavenge bit here. */
165			monflee(mtmp, 0, FALSE, FALSE);
166			if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE);
167		        break;
168		    }
169		}
170		break;
171	    }
172	}
173botm:   stealoid = 0;
174	return 0;
175}
176
177/* An object you're wearing has been taken off by a monster (theft or
178   seduction).  Also used if a worn item gets transformed (stone to flesh). */
179void
180remove_worn_item(obj, unchain_ball)
181struct obj *obj;
182boolean unchain_ball;	/* whether to unpunish or just unwield */
183{
184	if (donning(obj))
185	    cancel_don();
186	if (!obj->owornmask)
187	    return;
188
189	if (obj->owornmask & W_ARMOR) {
190	    if (obj == uskin) {
191		impossible("Removing embedded scales?");
192		skinback(TRUE);		/* uarm = uskin; uskin = 0; */
193	    }
194	    if (obj == uarm) (void) Armor_off();
195	    else if (obj == uarmc) (void) Cloak_off();
196	    else if (obj == uarmf) (void) Boots_off();
197	    else if (obj == uarmg) (void) Gloves_off();
198	    else if (obj == uarmh) (void) Helmet_off();
199	    else if (obj == uarms) (void) Shield_off();
200#ifdef TOURIST
201	    else if (obj == uarmu) (void) Shirt_off();
202#endif
203	    /* catchall -- should never happen */
204	    else setworn((struct obj *)0, obj->owornmask & W_ARMOR);
205	} else if (obj->owornmask & W_AMUL) {
206	    Amulet_off();
207	} else if (obj->owornmask & W_RING) {
208	    Ring_gone(obj);
209	} else if (obj->owornmask & W_TOOL) {
210	    Blindf_off(obj);
211	} else if (obj->owornmask & (W_WEP|W_SWAPWEP|W_QUIVER)) {
212	    if (obj == uwep)
213		uwepgone();
214	    if (obj == uswapwep)
215		uswapwepgone();
216	    if (obj == uquiver)
217		uqwepgone();
218	}
219
220	if (obj->owornmask & (W_BALL|W_CHAIN)) {
221	    if (unchain_ball) unpunish();
222	} else if (obj->owornmask) {
223	    /* catchall */
224	    setnotworn(obj);
225	}
226}
227
228/* Returns 1 when something was stolen (or at least, when N should flee now)
229 * Returns -1 if the monster died in the attempt
230 * Avoid stealing the object stealoid
231 */
232int
233steal(mtmp, objnambuf)
234struct monst *mtmp;
235char *objnambuf;
236{
237	struct obj *otmp;
238	int tmp, could_petrify, named = 0, armordelay;
239	boolean monkey_business; /* true iff an animal is doing the thievery */
240
241	if (objnambuf) *objnambuf = '\0';
242	/* the following is true if successful on first of two attacks. */
243	if(!monnear(mtmp, u.ux, u.uy)) return(0);
244
245	/* food being eaten might already be used up but will not have
246	   been removed from inventory yet; we don't want to steal that,
247	   so this will cause it to be removed now */
248	if (occupation) (void) maybe_finished_meal(FALSE);
249
250	if (!invent || (inv_cnt() == 1 && uskin)) {
251nothing_to_steal:
252	    /* Not even a thousand men in armor can strip a naked man. */
253	    if(Blind)
254	      pline("Somebody tries to rob you, but finds nothing to steal.");
255	    else
256	      pline("%s tries to rob you, but there is nothing to steal!",
257		Monnam(mtmp));
258	    return(1);	/* let her flee */
259	}
260
261	monkey_business = is_animal(mtmp->data);
262	if (monkey_business) {
263	    ;	/* skip ring special cases */
264	} else if (Adornment & LEFT_RING) {
265	    otmp = uleft;
266	    goto gotobj;
267	} else if (Adornment & RIGHT_RING) {
268	    otmp = uright;
269	    goto gotobj;
270	}
271
272	tmp = 0;
273	for(otmp = invent; otmp; otmp = otmp->nobj)
274	    if ((!uarm || otmp != uarmc) && otmp != uskin
275#ifdef INVISIBLE_OBJECTS
276				&& (!otmp->oinvis || perceives(mtmp->data))
277#endif
278				)
279		tmp += ((otmp->owornmask &
280			(W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1);
281	if (!tmp) goto nothing_to_steal;
282	tmp = rn2(tmp);
283	for(otmp = invent; otmp; otmp = otmp->nobj)
284	    if ((!uarm || otmp != uarmc) && otmp != uskin
285#ifdef INVISIBLE_OBJECTS
286				&& (!otmp->oinvis || perceives(mtmp->data))
287#endif
288			)
289		if((tmp -= ((otmp->owornmask &
290			(W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1)) < 0)
291			break;
292	if(!otmp) {
293		impossible("Steal fails!");
294		return(0);
295	}
296	/* can't steal gloves while wielding - so steal the wielded item. */
297	if (otmp == uarmg && uwep)
298	    otmp = uwep;
299	/* can't steal armor while wearing cloak - so steal the cloak. */
300	else if(otmp == uarm && uarmc) otmp = uarmc;
301#ifdef TOURIST
302	else if(otmp == uarmu && uarmc) otmp = uarmc;
303	else if(otmp == uarmu && uarm) otmp = uarm;
304#endif
305gotobj:
306	if(otmp->o_id == stealoid) return(0);
307
308	/* animals can't overcome curse stickiness nor unlock chains */
309	if (monkey_business) {
310	    boolean ostuck;
311	    /* is the player prevented from voluntarily giving up this item?
312	       (ignores loadstones; the !can_carry() check will catch those) */
313	    if (otmp == uball)
314		ostuck = TRUE;	/* effectively worn; curse is implicit */
315	    else if (otmp == uquiver || (otmp == uswapwep && !u.twoweap))
316		ostuck = FALSE;	/* not really worn; curse doesn't matter */
317	    else
318		ostuck = (otmp->cursed && otmp->owornmask);
319
320	    if (ostuck || !can_carry(mtmp, otmp)) {
321		static const char * const how[] = { "steal","snatch","grab","take" };
322 cant_take:
323		pline("%s tries to %s your %s but gives up.",
324		      Monnam(mtmp), how[rn2(SIZE(how))],
325		      (otmp->owornmask & W_ARMOR) ? equipname(otmp) :
326		       cxname(otmp));
327		/* the fewer items you have, the less likely the thief
328		   is going to stick around to try again (0) instead of
329		   running away (1) */
330		return !rn2(inv_cnt() / 5 + 2);
331	    }
332	}
333
334	if (otmp->otyp == LEASH && otmp->leashmon) {
335	    if (monkey_business && otmp->cursed) goto cant_take;
336	    o_unleash(otmp);
337	}
338
339	/* you're going to notice the theft... */
340	stop_occupation();
341
342	if((otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))){
343		switch(otmp->oclass) {
344		case TOOL_CLASS:
345		case AMULET_CLASS:
346		case RING_CLASS:
347		case FOOD_CLASS: /* meat ring */
348		    remove_worn_item(otmp, TRUE);
349		    break;
350		case ARMOR_CLASS:
351		    armordelay = objects[otmp->otyp].oc_delay;
352		    /* Stop putting on armor which has been stolen. */
353		    if (donning(otmp)) {
354			remove_worn_item(otmp, TRUE);
355			break;
356		    } else if (monkey_business) {
357			/* animals usually don't have enough patience
358			   to take off items which require extra time */
359			if (armordelay >= 1 && rn2(10)) goto cant_take;
360			remove_worn_item(otmp, TRUE);
361			break;
362		    } else {
363			int curssv = otmp->cursed;
364			int slowly;
365			boolean seen = canspotmon(mtmp);
366
367			otmp->cursed = 0;
368			/* can't charm you without first waking you */
369			if (multi < 0 && is_fainted()) unmul((char *)0);
370			slowly = (armordelay >= 1 || multi < 0);
371			if(flags.female)
372			    pline("%s charms you.  You gladly %s your %s.",
373				  !seen ? "She" : Monnam(mtmp),
374				  curssv ? "let her take" :
375				  slowly ? "start removing" : "hand over",
376				  equipname(otmp));
377			else
378			    pline("%s seduces you and %s off your %s.",
379				  !seen ? "She" : Adjmonnam(mtmp, "beautiful"),
380				  curssv ? "helps you to take" :
381				  slowly ? "you start taking" : "you take",
382				  equipname(otmp));
383			named++;
384			/* the following is to set multi for later on */
385			nomul(-armordelay);
386			remove_worn_item(otmp, TRUE);
387			otmp->cursed = curssv;
388			if(multi < 0){
389				/*
390				multi = 0;
391				nomovemsg = 0;
392				afternmv = 0;
393				*/
394				stealoid = otmp->o_id;
395				stealmid = mtmp->m_id;
396				afternmv = stealarm;
397				return(0);
398			}
399		    }
400		    break;
401		default:
402		    impossible("Tried to steal a strange worn thing. [%d]",
403			       otmp->oclass);
404		}
405	}
406	else if (otmp->owornmask)
407	    remove_worn_item(otmp, TRUE);
408
409	/* do this before removing it from inventory */
410	if (objnambuf) Strcpy(objnambuf, yname(otmp));
411	/* set mavenge bit so knights won't suffer an
412	 * alignment penalty during retaliation;
413	 */
414	mtmp->mavenge = 1;
415
416	freeinv(otmp);
417	pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp));
418	could_petrify = (otmp->otyp == CORPSE &&
419			 touch_petrifies(&mons[otmp->corpsenm]));
420	(void) mpickobj(mtmp,otmp);	/* may free otmp */
421	if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) {
422	    minstapetrify(mtmp, TRUE);
423	    return -1;
424	}
425	return((multi < 0) ? 0 : 1);
426}
427
428#endif /* OVLB */
429#ifdef OVL1
430
431/* Returns 1 if otmp is free'd, 0 otherwise. */
432int
433mpickobj(mtmp,otmp)
434register struct monst *mtmp;
435register struct obj *otmp;
436{
437    int freed_otmp;
438
439#ifndef GOLDOBJ
440    if (otmp->oclass == COIN_CLASS) {
441	mtmp->mgold += otmp->quan;
442	obfree(otmp, (struct obj *)0);
443	freed_otmp = 1;
444    } else {
445#endif
446    boolean snuff_otmp = FALSE;
447    /* don't want hidden light source inside the monster; assumes that
448       engulfers won't have external inventories; whirly monsters cause
449       the light to be extinguished rather than letting it shine thru */
450    if (otmp->lamplit &&  /* hack to avoid function calls for most objs */
451      	obj_sheds_light(otmp) &&
452	attacktype(mtmp->data, AT_ENGL)) {
453	/* this is probably a burning object that you dropped or threw */
454	if (u.uswallow && mtmp == u.ustuck && !Blind)
455	    pline("%s out.", Tobjnam(otmp, "go"));
456	snuff_otmp = TRUE;
457    }
458    /* Must do carrying effects on object prior to add_to_minv() */
459    carry_obj_effects(otmp);
460    /* add_to_minv() might free otmp [if merged with something else],
461       so we have to call it after doing the object checks */
462    freed_otmp = add_to_minv(mtmp, otmp);
463    /* and we had to defer this until object is in mtmp's inventory */
464    if (snuff_otmp) snuff_light_source(mtmp->mx, mtmp->my);
465#ifndef GOLDOBJ
466    }
467#endif
468    return freed_otmp;
469}
470
471#endif /* OVL1 */
472#ifdef OVLB
473
474void
475stealamulet(mtmp)
476struct monst *mtmp;
477{
478    struct obj *otmp = (struct obj *)0;
479    int real=0, fake=0;
480
481    /* select the artifact to steal */
482    if(u.uhave.amulet) {
483	real = AMULET_OF_YENDOR;
484	fake = FAKE_AMULET_OF_YENDOR;
485    } else if(u.uhave.questart) {
486	for(otmp = invent; otmp; otmp = otmp->nobj)
487	    if(is_quest_artifact(otmp)) break;
488	if (!otmp) return;	/* should we panic instead? */
489    } else if(u.uhave.bell) {
490	real = BELL_OF_OPENING;
491	fake = BELL;
492    } else if(u.uhave.book) {
493	real = SPE_BOOK_OF_THE_DEAD;
494    } else if(u.uhave.menorah) {
495	real = CANDELABRUM_OF_INVOCATION;
496    } else return;	/* you have nothing of special interest */
497
498    if (!otmp) {
499	/* If we get here, real and fake have been set up. */
500	for(otmp = invent; otmp; otmp = otmp->nobj)
501	    if(otmp->otyp == real || (otmp->otyp == fake && !mtmp->iswiz))
502		break;
503    }
504
505    if (otmp) { /* we have something to snatch */
506	if (otmp->owornmask)
507	    remove_worn_item(otmp, TRUE);
508	freeinv(otmp);
509	/* mpickobj wont merge otmp because none of the above things
510	   to steal are mergable */
511	(void) mpickobj(mtmp,otmp);	/* may merge and free otmp */
512	pline("%s stole %s!", Monnam(mtmp), doname(otmp));
513	if (can_teleport(mtmp->data) && !tele_restrict(mtmp))
514	    (void) rloc(mtmp, FALSE);
515    }
516}
517
518#endif /* OVLB */
519#ifdef OVL0
520
521/* drop one object taken from a (possibly dead) monster's inventory */
522STATIC_OVL void
523mdrop_obj(mon, obj, verbosely)
524struct monst *mon;
525struct obj *obj;
526boolean verbosely;
527{
528    int omx = mon->mx, omy = mon->my;
529
530    if (obj->owornmask) {
531	/* perform worn item handling if the monster is still alive */
532	if (mon->mhp > 0) {
533	    mon->misc_worn_check &= ~obj->owornmask;
534	    update_mon_intrinsics(mon, obj, FALSE, TRUE);
535	 /* obj_no_longer_held(obj); -- done by place_object */
536	    if (obj->owornmask & W_WEP) setmnotwielded(mon, obj);
537#ifdef STEED
538	/* don't charge for an owned saddle on dead steed */
539	} else if (mon->mtame && (obj->owornmask & W_SADDLE) &&
540		!obj->unpaid && costly_spot(omx, omy)) {
541	    obj->no_charge = 1;
542#endif
543	}
544	obj->owornmask = 0L;
545    }
546    if (verbosely && cansee(omx, omy))
547	pline("%s drops %s.", Monnam(mon), distant_name(obj, doname));
548    if (!flooreffects(obj, omx, omy, "fall")) {
549	place_object(obj, omx, omy);
550	stackobj(obj);
551    }
552}
553
554/* some monsters bypass the normal rules for moving between levels or
555   even leaving the game entirely; when that happens, prevent them from
556   taking the Amulet or invocation tools with them */
557void
558mdrop_special_objs(mon)
559struct monst *mon;
560{
561    struct obj *obj, *otmp;
562
563    for (obj = mon->minvent; obj; obj = otmp) {
564	otmp = obj->nobj;
565	/* the Amulet, invocation tools, and Rider corpses resist even when
566	   artifacts and ordinary objects are given 0% resistance chance */
567	if (obj_resists(obj, 0, 0)) {
568	    obj_extract_self(obj);
569	    mdrop_obj(mon, obj, FALSE);
570	}
571    }
572}
573
574/* release the objects the creature is carrying */
575void
576relobj(mtmp,show,is_pet)
577register struct monst *mtmp;
578register int show;
579boolean is_pet;		/* If true, pet should keep wielded/worn items */
580{
581	register struct obj *otmp;
582	register int omx = mtmp->mx, omy = mtmp->my;
583	struct obj *keepobj = 0;
584	struct obj *wep = MON_WEP(mtmp);
585	boolean item1 = FALSE, item2 = FALSE;
586
587	if (!is_pet || mindless(mtmp->data) || is_animal(mtmp->data))
588		item1 = item2 = TRUE;
589	if (!tunnels(mtmp->data) || !needspick(mtmp->data))
590		item1 = TRUE;
591
592	while ((otmp = mtmp->minvent) != 0) {
593		obj_extract_self(otmp);
594		/* special case: pick-axe and unicorn horn are non-worn */
595		/* items that we also want pets to keep 1 of */
596		/* (It is a coincidence that these can also be wielded.) */
597		if (otmp->owornmask || otmp == wep ||
598		    ((!item1 && otmp->otyp == PICK_AXE) ||
599		     (!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) {
600			if (is_pet) { /* dont drop worn/wielded item */
601				if (otmp->otyp == PICK_AXE)
602					item1 = TRUE;
603				if (otmp->otyp == UNICORN_HORN && !otmp->cursed)
604					item2 = TRUE;
605				otmp->nobj = keepobj;
606				keepobj = otmp;
607				continue;
608			}
609		}
610		mdrop_obj(mtmp, otmp, is_pet && flags.verbose);
611	}
612
613	/* put kept objects back */
614	while ((otmp = keepobj) != (struct obj *)0) {
615	    keepobj = otmp->nobj;
616	    (void) add_to_minv(mtmp, otmp);
617	}
618#ifndef GOLDOBJ
619	if (mtmp->mgold) {
620		register long g = mtmp->mgold;
621		(void) mkgold(g, omx, omy);
622		if (is_pet && cansee(omx, omy) && flags.verbose)
623			pline("%s drops %ld gold piece%s.", Monnam(mtmp),
624				g, plur(g));
625		mtmp->mgold = 0L;
626	}
627#endif
628
629	if (show & cansee(omx, omy))
630		newsym(omx, omy);
631}
632
633#endif /* OVL0 */
634
635/*steal.c*/
636