/* NetHack 3.7 steal.c $NHDT-Date: 1720895742 2024/07/13 18:35:42 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.132 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" staticfn int unstolenarm(void); staticfn int stealarm(void); staticfn void worn_item_removal(struct monst *, struct obj *); /* proportional subset of gold; return value actually fits in an int */ long somegold(long lmoney) { int igold = (lmoney >= (long) LARGEST_INT) ? LARGEST_INT : (int) lmoney; if (igold < 50) ; /* all gold */ else if (igold < 100) igold = rn1(igold - 25 + 1, 25); else if (igold < 500) igold = rn1(igold - 50 + 1, 50); else if (igold < 1000) igold = rn1(igold - 100 + 1, 100); else if (igold < 5000) igold = rn1(igold - 500 + 1, 500); else if (igold < 10000) igold = rn1(igold - 1000 + 1, 1000); else igold = rn1(igold - 5000 + 1, 5000); return (long) igold; } /* * Find the first (and hopefully only) gold object in a chain. * Used when leprechaun (or you as leprechaun) looks for * someone else's gold. Returns a pointer so the gold may * be seized without further searching. * May search containers too. * Deals in gold only, as leprechauns don't care for lesser coins. */ struct obj * findgold(struct obj *argchain) { struct obj *chain = argchain; /* allow arg to be nonnull */ while (chain && chain->otyp != GOLD_PIECE) chain = chain->nobj; return chain; } /* * Steal gold coins only. Leprechauns don't care for lesser coins. */ void stealgold(struct monst *mtmp) { struct obj *fgold = g_at(u.ux, u.uy); struct obj *ygold; long tmp; struct monst *who; const char *whose, *what; /* skip lesser coins on the floor */ while (fgold && fgold->otyp != GOLD_PIECE) fgold = fgold->nexthere; /* Do you have real gold? */ ygold = findgold(gi.invent); if (fgold && (!ygold || fgold->quan > ygold->quan || !rn2(5))) { obj_extract_self(fgold); add_to_minv(mtmp, fgold); newsym(u.ux, u.uy); if (u.usteed) { who = u.usteed; whose = s_suffix(y_monnam(who)); what = makeplural(mbodypart(who, FOOT)); } else { who = &gy.youmonst; whose = "your"; what = makeplural(body_part(FOOT)); } /* [ avoid "between your rear regions" :-] */ if (slithy(who->data)) what = "coils"; /* reduce "rear hooves/claws" to "hooves/claws" */ if (!strncmp(what, "rear ", 5)) what += 5; pline("%s quickly snatches some gold from %s %s %s!", Monnam(mtmp), (Levitation || Flying) ? "beneath" : "between", whose, what); if (!ygold || !rn2(5)) { if (!tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); monflee(mtmp, 0, FALSE, FALSE); } } else if (ygold) { const int gold_price = objects[GOLD_PIECE].oc_cost; tmp = (somegold(money_cnt(gi.invent)) + gold_price - 1) / gold_price; tmp = min(tmp, ygold->quan); if (tmp < ygold->quan) ygold = splitobj(ygold, tmp); else setnotworn(ygold); freeinv(ygold); add_to_minv(mtmp, ygold); Your("purse feels lighter."); if (!tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); monflee(mtmp, 0, FALSE, FALSE); disp.botl = TRUE; } } /* monster who was stealing from hero has just died */ void thiefdead(void) { /* hero is busy taking off an item of armor which takes multiple turns */ gs.stealmid = 0; if (ga.afternmv == stealarm) { ga.afternmv = unstolenarm; gn.nomovemsg = (char *) 0; } } /* checks whether hero can be responsive to seduction attempts; similar to Unaware but also includes paralysis */ boolean unresponsive(void) { if (gm.multi >= 0) return FALSE; return (unconscious() || is_fainted() || (gm.multi_reason && (!strncmp(gm.multi_reason, "frozen", 6) || !strncmp(gm.multi_reason, "paralyzed", 9)))); } /* called via (*ga.afternmv)() when hero finishes taking off armor that was slated to be stolen but the thief died in the interim */ staticfn int unstolenarm(void) { struct obj *obj; /* find the object before clearing stealoid; it has already become not-worn and is still in hero's inventory */ for (obj = gi.invent; obj; obj = obj->nobj) if (obj->o_id == gs.stealoid) break; gs.stealoid = 0; if (obj) { You("finish taking off your %s.", armor_simple_name(obj)); } return 0; } /* finish stealing an item of armor which takes multiple turns to take off */ staticfn int stealarm(void) { struct monst *mtmp; struct obj *otmp, *nextobj; if (!gs.stealoid || !gs.stealmid) goto botm; for (otmp = gi.invent; otmp; otmp = nextobj) { nextobj = otmp->nobj; if (otmp->o_id == gs.stealoid) { for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (mtmp->m_id == gs.stealmid) { if (DEADMONSTER(mtmp)) { impossible("stealarm(): dead monster stealing"); goto botm; /* (could just use 'break' here) */ } /* maybe the thief polymorphed into something without a steal attack, or perhaps while stealing hero's suit the thief took away other items causing hero to fall into water or lava and then teleport to safety */ if (!dmgtype(mtmp->data, AD_SITM) || distu(mtmp->mx, mtmp->my) > 2) goto botm; /* (could just use 'break' here) */ if (otmp->unpaid) subfrombill(otmp, shop_keeper(*u.ushops)); freeinv(otmp); pline("%s steals %s!", Monnam(mtmp), doname(otmp)); (void) mpickobj(mtmp, otmp); /* may free otmp */ /* Implies seduction, "you gladly hand over ..." so we don't set mavenge bit here. */ monflee(mtmp, 0, FALSE, FALSE); if (!tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); break; } } break; } } botm: gs.stealoid = gs.stealmid = 0; /* in case only one has been reset so far */ return 0; } /* An object you're wearing has been taken off by a monster (theft or seduction). Also used if a worn item gets transformed (stone to flesh). */ void remove_worn_item( struct obj *obj, boolean unchain_ball) /* whether to unpunish or just unwield */ { unsigned oldinuse; if (donning(obj)) cancel_don(); if (!obj->owornmask) return; /* * Losing worn gear might drop hero into water or lava or onto a * location-changing trap or take away the ability to breathe in water. * Marking it 'in_use' prevents emergency_disrobe() from dropping it * and lava_effects() from destroying it; other cases impacting object * location (or destruction) might still have issues. * * Note: if a hangup save occurs when 'in_use' is set, the item will * be destroyed via useup() during restore. Maybe remove_worn_item() * and emergency_disrobe() should switch to using obj->bypass instead * but that would need a lot more cooperation by callers. It's a * tradeoff between protecting the player against unintentional hangup * and defending the game against deliberate hangup when player sees a * message about something undesirable followed by --More--. */ oldinuse = obj->in_use; obj->in_use = 1; if (obj->owornmask & W_ARMOR) { if (obj == uskin) { impossible("Removing embedded scales?"); skinback(TRUE); /* uarm = uskin; uskin = 0; */ } if (obj == uarm) (void) Armor_off(); else if (obj == uarmc) (void) Cloak_off(); else if (obj == uarmf) (void) Boots_off(); else if (obj == uarmg) (void) Gloves_off(); else if (obj == uarmh) (void) Helmet_off(); else if (obj == uarms) (void) Shield_off(); else if (obj == uarmu) (void) Shirt_off(); /* catchall -- should never happen */ else setworn((struct obj *) 0, obj->owornmask & W_ARMOR); } else if (obj->owornmask & W_AMUL) { Amulet_off(); } else if (obj->owornmask & W_RING) { Ring_gone(obj); } else if (obj->owornmask & W_TOOL) { Blindf_off(obj); } else if (obj->owornmask & W_WEAPONS) { if (obj == uwep) uwepgone(); if (obj == uswapwep) uswapwepgone(); if (obj == uquiver) uqwepgone(); } if (obj->owornmask & (W_BALL | W_CHAIN)) { if (unchain_ball) unpunish(); } else if (obj->owornmask) { /* catchall */ setnotworn(obj); } if (obj->where == OBJ_DELETED) debugpline1("remove_worn_item() \"%s\" deleted!", simpleonames(obj)); obj->in_use = oldinuse; } /* during theft of a worn item: remove_worn_item(), prefaced by a message */ staticfn void worn_item_removal( struct monst *mon, struct obj *obj) { char objbuf[BUFSZ], article[20], *p; const char *verb; int strip_art; Strcpy(objbuf, doname(obj)); /* massage the object description */ strip_art = !strncmp(objbuf, "the ", 4) ? 4 : !strncmp(objbuf, "an ", 3) ? 3 : !strncmp(objbuf, "a ", 2) ? 2 : 0; if (strip_art) { /* convert "a/an/the " to "your object" */ copynchars(article, objbuf, strip_art); /* when removing attached iron ball, caller passes 'uchain'; when formatted, it will be "an iron chain (attached to you)"; change "an" to "the" rather than to "your" in that situation */ (void) strsubst(objbuf, article, (obj == uchain) ? "the " : "your "); } /* these ought to be guarded against matching user-supplied name */ (void) strsubst(objbuf, " (being worn)", ""); (void) strsubst(objbuf, " (alternate weapon; not wielded)", ""); /* convert "ring (on left hand)" to "ring (from left hand)" */ if ((p = strstri(objbuf, " (on ")) && (!strncmp(p + 5, "left ", 5) || !strncmp(p + 5, "right ", 6))) (void) strsubst(p + 2, "on", "from"); /* slightly iffy for alternate weapon that isn't actively dual-wielded, but it's better to alert the player to the change in equipment than to suppress the message for that case */ verb = ((obj->owornmask & W_WEAPONS) != 0L) ? "disarms" : ((obj->owornmask & W_ACCESSORY) != 0L) ? "removes" : "takes off"; pline("%s %s %s.", Some_Monnam(mon), verb, objbuf); iflags.last_msg = PLNMSG_MON_TAKES_OFF_ITEM; /* removal might trigger more messages (due to loss of Lev|Fly; descending happens before the theft in progress finishes) */ remove_worn_item(obj, TRUE); } /* Returns 1 when something was stolen (or at least, when N should flee now), * returns -1 if the monster died in the attempt. * Avoid stealing the object 'stealoid'. * Nymphs and monkeys won't steal coins (so that their "steal item" attack * doesn't become a superset of leprechaun's "steal gold" attack). */ int steal(struct monst *mtmp, char *objnambuf) { struct obj *otmp; char Monnambuf[BUFSZ]; int tmp, could_petrify, armordelay, olddelay, icnt, named = 0, retrycnt = 0; boolean monkey_business = is_animal(mtmp->data), seen = canspotmon(mtmp), was_doffing, was_punished = Punished; if (objnambuf) *objnambuf = '\0'; /* the following is true if successful on first of two attacks. */ if (!monnear(mtmp, u.ux, u.uy)) return 0; /* stealing a worn item might drop hero into water or lava where teleporting to safety could result in a previously visible thief no longer being visible; it could also be a case of a blinded hero being able to see via wearing the Eyes of the Overworld and having those stolen; remember the name as it is now; if unseen, nymphs will be "Someone" and monkeys will be "Something" */ Strcpy(Monnambuf, Some_Monnam(mtmp)); /* food being eaten might already be used up but will not have been removed from inventory yet; we don't want to steal that, so this will cause it to be removed now */ if (go.occupation) (void) maybe_finished_meal(FALSE); icnt = inv_cnt(FALSE); /* don't include gold */ if (!icnt || (icnt == 1 && uskin)) { /* Not even a thousand men in armor can strip a naked man. */ nothing_to_steal: /* nymphs might target uchain if invent is empty; monkeys won't; hero becomes unpunished but nymph ends up empty handed */ if (Punished && !monkey_business && rn2(4)) { /* uball is not carried (uchain never is) */ assert(uball != NULL && uball->where == OBJ_FLOOR); worn_item_removal(mtmp, uchain); } else if (u.utrap && u.utraptype == TT_BURIEDBALL && !monkey_business && !rn2(4)) { boolean dummy; /* buried ball is not tracked via 'uball' and there is no chain at all (hence no uchain to take off) */ pline("%s takes off your unseen chain.", Monnambuf); (void) openholdingtrap(&gy.youmonst, &dummy); } else if (Blind) { pline("Somebody tries to rob you, but finds nothing to steal."); } else if (inv_cnt(TRUE) > inv_cnt(FALSE)) { pline("%s tries to rob you, but isn't interested in gold.", Monnambuf); } else { pline("%s tries to rob you, but there is nothing to steal!", Monnambuf); } return 1; /* let her flee */ } if (monkey_business || uarmg) { ; /* skip ring special cases */ } else if (Adornment & LEFT_RING) { otmp = uleft; goto gotobj; } else if (Adornment & RIGHT_RING) { otmp = uright; goto gotobj; } retry: tmp = 0; for (otmp = gi.invent; otmp; otmp = otmp->nobj) if ((!uarm || otmp != uarmc) && otmp != uskin && otmp->oclass != COIN_CLASS) tmp += (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) ? 5 : 1; if (!tmp) goto nothing_to_steal; tmp = rn2(tmp); for (otmp = gi.invent; otmp; otmp = otmp->nobj) if ((!uarm || otmp != uarmc) && otmp != uskin && otmp->oclass != COIN_CLASS) { tmp -= (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) ? 5 : 1; if (tmp < 0) break; } if (!otmp) { impossible("Steal fails!"); return 0; } /* can't steal ring(s) while wearing gloves */ if ((otmp == uleft || otmp == uright) && uarmg) otmp = uarmg; /* can't steal gloves while wielding - so steal the wielded item. */ if (otmp == uarmg && uwep) otmp = uwep; /* can't steal armor while wearing cloak - so steal the cloak. */ else if (otmp == uarm && uarmc) otmp = uarmc; /* can't steal shirt while wearing cloak or suit */ else if (otmp == uarmu && uarmc) otmp = uarmc; else if (otmp == uarmu && uarm) otmp = uarm; gotobj: if (otmp->o_id == gs.stealoid) return 0; if (otmp->otyp == BOULDER && !throws_rocks(mtmp->data)) { if (!retrycnt++) goto retry; goto cant_take; } /* animals can't overcome curse stickiness nor unlock chains */ if (monkey_business) { boolean ostuck; /* is the player prevented from voluntarily giving up this item? (ignores loadstones; the !can_carry() check will catch those) */ if (otmp == uball) ostuck = TRUE; /* effectively worn; curse is implicit */ else if (otmp == uquiver || (otmp == uswapwep && !u.twoweap)) ostuck = FALSE; /* not really worn; curse doesn't matter */ else ostuck = ((otmp->cursed && otmp->owornmask) /* nymphs can steal rings from under cursed weapon but animals can't */ || (otmp == RING_ON_PRIMARY && welded(uwep)) || (otmp == RING_ON_SECONDARY && welded(uwep) && bimanual(uwep))); if (ostuck || can_carry(mtmp, otmp) == 0) { static const char *const how[] = { "steal", "snatch", "grab", "take" }; cant_take: pline("%s tries to %s %s%s but gives up.", Monnambuf, ROLL_FROM(how), (otmp->owornmask & W_ARMOR) ? "your " : "", (otmp->owornmask & W_ARMOR) ? armor_simple_name(otmp) : yname(otmp)); /* the fewer items you have, the less likely the thief is going to stick around to try again (0) instead of running away (1) */ return !rn2(inv_cnt(FALSE) / 5 + 2); } } if (otmp->otyp == LEASH && otmp->leashmon) { if (monkey_business && otmp->cursed) goto cant_take; o_unleash(otmp); } was_doffing = doffing(otmp); /* stop donning/doffing now so that afternmv won't be clobbered below; stop_occupation doesn't handle donning/doffing */ olddelay = stop_donning(otmp); /* you're going to notice the theft... */ stop_occupation(); if (otmp->owornmask & (W_ARMOR | W_ACCESSORY)) { switch (otmp->oclass) { case TOOL_CLASS: case AMULET_CLASS: case RING_CLASS: case FOOD_CLASS: /* meat ring */ worn_item_removal(mtmp, otmp); break; case ARMOR_CLASS: armordelay = objects[otmp->otyp].oc_delay; if (olddelay > 0 && olddelay < armordelay) armordelay = olddelay; if (monkey_business || unresponsive()) { /* animals usually don't have enough patience to take off items which require extra time; unconscious or paralyzed hero can't be charmed into taking off his own armor */ if (armordelay >= 1 && !olddelay && rn2(10)) goto cant_take; worn_item_removal(mtmp, otmp); break; } else { int curssv = otmp->cursed; int slowly; otmp->cursed = 0; slowly = (armordelay >= 1 || gm.multi < 0); if (flags.female) urgent_pline("%s charms you. You gladly %s your %s.", !seen ? "She" : Monnambuf, curssv ? "let her take" : !slowly ? "hand over" : was_doffing ? "continue removing" : "start removing", armor_simple_name(otmp)); else urgent_pline("%s seduces you and %s off your %s.", !seen ? "She" : Adjmonnam(mtmp, "beautiful"), curssv ? "helps you to take" : !slowly ? "you take" : was_doffing ? "you continue taking" : "you start taking", armor_simple_name(otmp)); named++; /* the following is to set multi for later on */ nomul(-armordelay); gm.multi_reason = "taking off clothes"; gn.nomovemsg = 0; remove_worn_item(otmp, TRUE); otmp->cursed = curssv; if (gm.multi < 0) { gs.stealoid = otmp->o_id; gs.stealmid = mtmp->m_id; ga.afternmv = stealarm; return 0; } } break; default: impossible("Tried to steal a strange worn thing. [%d]", otmp->oclass); } /* hero's blindfold might have just been stolen; if so, replace cached "Someone" or "Something" with Monnam */ if (!seen && canspotmon(mtmp)) Strcpy(Monnambuf, Monnam(mtmp)); } else if (otmp->owornmask) { /* weapon or ball&chain */ struct obj *item = otmp; if (otmp == uball) /* non-Null uball implies non-Null uchain */ item = uchain; /* yields a more accurate 'takes off' message */ worn_item_removal(mtmp, item); /* if we switched from uball to uchain for the preface message, then unpunish() took place and both those pointers are now Null, with 'item' a stale pointer to freed chain; the ball is still present though and 'otmp' is still valid; if uball was also wielded or quivered, the corresponding weapon pointer hasn't been cleared yet; do that, with no preface message this time */ if ((otmp->owornmask & W_WEAPONS) != 0L) remove_worn_item(otmp, FALSE); } /* do this before removing it from inventory */ if (objnambuf) Strcpy(objnambuf, yname(otmp)); /* usually set mavenge bit so knights won't suffer an alignment penalty during retaliation; not applicable for removing attached iron ball */ if (!Conflict && !(was_punished && !Punished)) mtmp->mavenge = 1; if (otmp->unpaid) subfrombill(otmp, shop_keeper(*u.ushops)); freeinv(otmp); /* if we just gave a message about removing a worn item and there have been no intervening messages, shorten ' stole ' message */ if (iflags.last_msg == PLNMSG_MON_TAKES_OFF_ITEM && mtmp->data->mlet == S_NYMPH) ++named; urgent_pline("%s stole %s.", named ? "She" : Monnambuf, doname(otmp)); (void) encumber_msg(); could_petrify = (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])); otmp->how_lost = LOST_STOLEN; (void) mpickobj(mtmp, otmp); /* may free otmp */ if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) { minstapetrify(mtmp, TRUE); return -1; } return (gm.multi < 0) ? 0 : 1; } /* Returns 1 if otmp is free'd, 0 otherwise. */ int mpickobj(struct monst *mtmp, struct obj *otmp) { int freed_otmp; boolean snuff_otmp = FALSE; if (!otmp) { impossible("monster (%s) taking or picking up nothing?", pmname(mtmp->data, Mgender(mtmp))); return 1; } else if (otmp == uball || otmp == uchain) { impossible("monster (%s) taking or picking up attached %s (%s)?", pmname(mtmp->data, Mgender(mtmp)), (otmp == uchain) ? "chain" : "ball", simpleonames(otmp)); return 0; } /* if monster is acquiring a thrown or kicked object, the throwing or kicking code shouldn't continue to track and place it */ if (otmp == gt.thrownobj) gt.thrownobj = 0; else if (otmp == gk.kickedobj) gk.kickedobj = 0; /* an unpaid item can be on the floor; if a monster picks it up, take it off the shop bill */ if (otmp->unpaid || (Has_contents(otmp) && count_unpaid(otmp->cobj))) { subfrombill(otmp, find_objowner(otmp, otmp->ox, otmp->oy)); } /* don't want hidden light source inside the monster; assumes that engulfers won't have external inventories; whirly monsters cause the light to be extinguished rather than letting it shine thru */ if (obj_sheds_light(otmp) && attacktype(mtmp->data, AT_ENGL)) { /* this is probably a burning object that you dropped or threw */ if (engulfing_u(mtmp) && !Blind) pline("%s out.", Tobjnam(otmp, "go")); snuff_otmp = TRUE; } /* for hero owned object on shop floor, mtmp is taking possession and if it's eventually dropped in a shop, shk will claim it */ otmp->no_charge = 0; /* some object handling is only done if mtmp isn't a pet */ if (!mtmp->mtame) { /* if monst is unseen, some info hero knows about this object becomes lost; continual pickup and drop by pets makes this too annoying if it is applied to them; when engulfed (where monster can't be seen because vision is disabled), or when held (or poly'd and holding) while blind, behave as if the monster can be 'seen' by touch */ if (!canseemon(mtmp) && mtmp != u.ustuck) unknow_object(otmp); /* if otmp has flags set for how it left hero's inventory, change those flags; if thrown, now stolen and autopickup might override pickup_types and autopickup exceptions based on 'pickup_stolen' rather than 'pickup_thrown'; if previously stolen, stays stolen; if previously dropped, now forgotten and autopickup will operate normally regardless of the setting for 'dropped_nopick' */ if (otmp->how_lost == LOST_THROWN) otmp->how_lost = LOST_STOLEN; else if (otmp->how_lost == LOST_DROPPED) otmp->how_lost = LOST_NONE; } /* Must do carrying effects on object prior to add_to_minv() */ carry_obj_effects(otmp); /* add_to_minv() might free otmp [if merged with something else], so we have to call it after doing the object checks */ freed_otmp = add_to_minv(mtmp, otmp); /* and we had to defer this until object is in mtmp's inventory */ if (snuff_otmp) snuff_light_source(mtmp->mx, mtmp->my); return freed_otmp; } /* called for AD_SAMU (the Wizard and quest nemeses) */ void stealamulet(struct monst *mtmp) { char buf[BUFSZ]; struct obj *otmp = 0, *obj = 0; int real = 0, fake = 0, n; /* target every quest artifact, not just current role's; if hero has more than one, choose randomly so that player can't use inventory ordering to influence the theft */ for (n = 0, obj = gi.invent; obj; obj = obj->nobj) if (any_quest_artifact(obj)) ++n, otmp = obj; if (n > 1) { n = rnd(n); for (otmp = gi.invent; otmp; otmp = otmp->nobj) if (any_quest_artifact(otmp) && !--n) break; } if (!otmp) { /* if we didn't find any quest artifact, find another valuable item */ if (u.uhave.amulet) { real = AMULET_OF_YENDOR; fake = FAKE_AMULET_OF_YENDOR; } else if (u.uhave.bell) { real = BELL_OF_OPENING; fake = BELL; } else if (u.uhave.book) { real = SPE_BOOK_OF_THE_DEAD; } else if (u.uhave.menorah) { real = CANDELABRUM_OF_INVOCATION; } else return; /* you have nothing of special interest */ /* If we get here, real and fake have been set up. */ for (n = 0, obj = gi.invent; obj; obj = obj->nobj) if (obj->otyp == real || (obj->otyp == fake && !mtmp->iswiz)) ++n, otmp = obj; if (n > 1) { n = rnd(n); for (otmp = gi.invent; otmp; otmp = otmp->nobj) if ((otmp->otyp == real || (otmp->otyp == fake && !mtmp->iswiz)) && !--n) break; } } if (otmp) { /* we have something to snatch */ /* take off outer gear if we're targeting [hypothetical] quest artifact suit, shirt, gloves, or rings */ if ((otmp == uarm || otmp == uarmu) && uarmc) worn_item_removal(mtmp, uarmc); if (otmp == uarmu && uarm) worn_item_removal(mtmp, uarm); if ((otmp == uarmg || ((otmp == uright || otmp == uleft) && uarmg)) && uwep) { /* gloves are about to be unworn; unwield weapon(s) first */ if (u.twoweap) /* remove_worn_item(uswapwep) indirectly */ worn_item_removal(mtmp, uswapwep); /* clears u.twoweap */ worn_item_removal(mtmp, uwep); } if ((otmp == uright || otmp == uleft) && uarmg) /* calls Gloves_off() to handle wielded cockatrice corpse */ worn_item_removal(mtmp, uarmg); /* finally, steal the target item */ if (otmp->owornmask) worn_item_removal(mtmp, otmp); if (otmp->unpaid) subfrombill(otmp, shop_keeper(*u.ushops)); freeinv(otmp); Strcpy(buf, doname(otmp)); (void) mpickobj(mtmp, otmp); /* could merge and free otmp but won't */ pline("%s steals %s!", Some_Monnam(mtmp), buf); if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) (void) rloc(mtmp, RLOC_MSG); (void) encumber_msg(); } } /* when a mimic gets poked with something, it might take that thing (at present, only implemented for when the hero does the poking) */ void maybe_absorb_item( struct monst *mon, struct obj *obj, int ochance, int achance) /* percent chance for ordinary item, artifact */ { if (obj == uball || obj == uchain || obj->oclass == ROCK_CLASS || obj_resists(obj, 100 - ochance, 100 - achance) || !touch_artifact(obj, mon)) return; if (carried(obj)) { if (obj->owornmask) remove_worn_item(obj, TRUE); if (obj->unpaid) subfrombill(obj, shop_keeper(*u.ushops)); if (cansee(mon->mx, mon->my)) { /* Some_Monnam() avoids "It pulls ... and absorbs it!" if hero can see the location but not the monster */ pline("%s pulls %s away from you and absorbs %s!", Some_Monnam(mon), /* Monnam() or "Something" */ yname(obj), (obj->quan > 1L) ? "them" : "it"); } else { const char *hand_s = body_part(HAND); if (bimanual(obj)) hand_s = makeplural(hand_s); pline("%s %s pulled from your %s!", upstart(yname(obj)), otense(obj, "are"), hand_s); } freeinv(obj); (void) encumber_msg(); } else { /* not carried; presumably thrown or kicked */ if (canspotmon(mon)) pline("%s absorbs %s!", Monnam(mon), yname(obj)); } /* add to mon's inventory */ (void) mpickobj(mon, obj); } /* drop one object taken from a (possibly dead) monster's inventory */ void mdrop_obj( struct monst *mon, struct obj *obj, boolean verbosely) { coordxy omx = mon->mx, omy = mon->my; long unwornmask = obj->owornmask; /* call distant_name() for its possible side-effects even if the result might not be printed, and do it before extracting obj from minvent */ char *obj_name = distant_name(obj, doname); extract_from_minvent(mon, obj, FALSE, TRUE); /* don't charge for an owned saddle on dead steed (provided that the hero is within the same shop at the time) */ if (unwornmask && mon->mtame && (unwornmask & W_SADDLE) != 0L && !obj->unpaid && costly_spot(omx, omy) /* being at costly_spot guarantees lev->roomno is not 0 */ && strchr(in_rooms(u.ux, u.uy, SHOPBASE), levl[omx][omy].roomno)) { obj->no_charge = 1; } /* obj_no_longer_held(obj); -- done by place_object */ if (verbosely && cansee(omx, omy)) pline_mon(mon, "%s drops %s.", Monnam(mon), obj_name); if (!flooreffects(obj, omx, omy, "fall")) { place_object(obj, omx, omy); stackobj(obj); } /* do this last, after placing obj on floor; removing steed's saddle throws rider, possibly inflicting fatal damage and producing bones; this is why we had to call extract_from_minvent() with do_intrinsics=FALSE */ if (!DEADMONSTER(mon) && unwornmask) update_mon_extrinsics(mon, obj, FALSE, TRUE); } /* some monsters bypass the normal rules for moving between levels or even leaving the game entirely; when that happens, prevent them from taking the Amulet, invocation items, or quest artifact with them */ void mdrop_special_objs(struct monst *mon) { struct obj *obj, *otmp; for (obj = mon->minvent; obj; obj = otmp) { otmp = obj->nobj; /* the Amulet, invocation tools, and Rider corpses resist even when artifacts and ordinary objects are given 0% resistance chance; current role's quest artifact is rescued too--quest artifacts for the other roles are not */ if (obj_resists(obj, 0, 0) || is_quest_artifact(obj)) { if (mon->mx) { mdrop_obj(mon, obj, FALSE); } else { /* migrating monster not on map */ extract_from_minvent(mon, obj, TRUE, TRUE); rloco(obj); } } } } /* release the objects the creature is carrying */ void relobj( struct monst *mtmp, int show, boolean is_pet) /* If true, pet should keep wielded/worn items */ { struct obj *otmp; int omx = mtmp->mx, omy = mtmp->my; /* vault guard's gold goes away rather than be dropped... */ if (mtmp->isgd && (otmp = findgold(mtmp->minvent)) != 0) { if (canspotmon(mtmp)) pline("%s gold %s.", s_suffix(Monnam(mtmp)), canseemon(mtmp) ? "vanishes" : "seems to vanish"); obj_extract_self(otmp); obfree(otmp, (struct obj *) 0); } /* isgd && has gold */ while ((otmp = (is_pet ? droppables(mtmp) : mtmp->minvent)) != 0) { mdrop_obj(mtmp, otmp, is_pet && flags.verbose); } if (show && cansee(omx, omy)) newsym(omx, omy); } /*steal.c*/