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