OSDN Git Service

shrink mine
[nethackexpress/trunk.git] / src / steal.c
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 *
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 */
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
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 */
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 *
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
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
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
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
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
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
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
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
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
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*/