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
7 STATIC_PTR int NDECL(stealarm);
8
9 #ifdef OVLB
10 STATIC_DCL const char *FDECL(equipname, (struct obj *));
11 STATIC_DCL void FDECL(mdrop_obj, (struct monst *,struct obj *,BOOLEAN_P));
12
13 STATIC_OVL const char *
equipname(otmp)14 equipname(otmp)
15 register 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
29 long /* actually returns something that fits in an int */
somegold()30 somegold()
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
40 void
stealgold(mtmp)41 stealgold(mtmp)
42 register 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
71 long /* actually returns something that fits in an int */
somegold(umoney)72 somegold(umoney)
73 long 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 /*
84 Find the first (and hopefully only) gold object in a chain.
85 Used when leprechaun (or you as leprechaun) looks for
86 someone else's gold. Returns a pointer so the gold may
87 be seized without further searching.
88 May search containers too.
89 Deals in gold only, as leprechauns don't care for lesser coins.
90 */
91 struct obj *
findgold(chain)92 findgold(chain)
93 register struct obj *chain;
94 {
95 while (chain && chain->otyp != GOLD_PIECE) chain = chain->nobj;
96 return chain;
97 }
98
99 /*
100 Steal gold coins only. Leprechauns don't care for lesser coins.
101 */
102 void
stealgold(mtmp)103 stealgold(mtmp)
104 register 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 */
142 unsigned int stealoid; /* object to be stolen */
143 unsigned int stealmid; /* monster doing the stealing */
144
145 STATIC_PTR int
stealarm()146 stealarm()
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 }
173 botm: 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). */
179 void
remove_worn_item(obj,unchain_ball)180 remove_worn_item(obj, unchain_ball)
181 struct obj *obj;
182 boolean 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 */
232 int
steal(mtmp,objnambuf)233 steal(mtmp, objnambuf)
234 struct monst *mtmp;
235 char *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)) {
251 nothing_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
305 gotobj:
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. */
432 int
mpickobj(mtmp,otmp)433 mpickobj(mtmp,otmp)
434 register struct monst *mtmp;
435 register 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
474 void
stealamulet(mtmp)475 stealamulet(mtmp)
476 struct 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 */
522 STATIC_OVL void
mdrop_obj(mon,obj,verbosely)523 mdrop_obj(mon, obj, verbosely)
524 struct monst *mon;
525 struct obj *obj;
526 boolean 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 */
557 void
mdrop_special_objs(mon)558 mdrop_special_objs(mon)
559 struct 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 */
575 void
relobj(mtmp,show,is_pet)576 relobj(mtmp,show,is_pet)
577 register struct monst *mtmp;
578 register int show;
579 boolean 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