/* drop one object taken from a (possibly dead) monster's inventory */ static void mdrop_obj(struct monst *mon, struct obj *obj, boolean verbosely) { int omx = mon->mx, omy = mon->my; if (obj->owornmask) { /* perform worn item handling if the monster is still alive */ if (mon->mhp > 0) { mon->misc_worn_check &= ~obj->owornmask; update_mon_intrinsics(mon, obj, FALSE, TRUE); /* obj_no_longer_held(obj); -- done by place_object */ if (obj->owornmask & W_MASK(os_wep)) setmnotwielded(mon, obj); /* don't charge for an owned saddle on dead steed */ } else if (mon->mtame && (obj->owornmask & W_MASK(os_saddle)) && !obj->unpaid && costly_spot(omx, omy)) { obj->no_charge = 1; } obj->owornmask = 0L; } if (!DEADMONSTER(mon) && obj->otyp == LOADSTONE && !obj->cursed) curse(obj); if (verbosely && cansee(omx, omy)) pline("%s drops %s.", Monnam(mon), distant_name(obj, doname)); if (!flooreffects(obj, omx, omy, "fall")) { place_object(obj, level, omx, omy); stackobj(obj); } }
/* * This function is only called from newgame, so we know: * - pets aren't genocided so makemon will always work * - the petname has not been used yet * This is called very late in the newgame sequence, and so can safely clobber * the charstats RNGs. */ struct monst * makedog(struct newgame_options *ngo) { struct monst *mtmp; struct obj *otmp; const char *petname; int pettype; if (ngo->preferred_pet == 'n') return NULL; pettype = pet_type(ngo); if (pettype == PM_LITTLE_DOG) petname = ngo->dogname; else if (pettype == PM_PONY) petname = ngo->horsename; else petname = ngo->catname; /* default pet names */ if (!*petname && pettype == PM_LITTLE_DOG) { /* All of these names were for dogs. */ if (Role_if(PM_CAVEMAN)) petname = "Slasher"; /* The Warrior */ if (Role_if(PM_SAMURAI)) petname = "Hachi"; /* Shibuya Station */ if (Role_if(PM_BARBARIAN)) petname = "Idefix"; /* Obelix */ if (Role_if(PM_RANGER)) petname = "Sirius"; /* Orion's dog */ } /* ideally we'd use rng_charstats_role for this, but... */ mtmp = makemon(&mons[pettype], level, u.ux, u.uy, MM_EDOG); /* Horses already wear a saddle */ if (pettype == PM_PONY && ((otmp = mksobj(level, SADDLE, TRUE, FALSE, rng_charstats_role)))) { if (mpickobj(mtmp, otmp)) panic("merged saddle?"); mtmp->misc_worn_check |= W_MASK(os_saddle); otmp->dknown = otmp->bknown = otmp->rknown = 1; otmp->owornmask = W_MASK(os_saddle); otmp->leashmon = mtmp->m_id; update_mon_intrinsics(mtmp, otmp, TRUE, TRUE); } if (*petname) mtmp = christen_monst(mtmp, petname); initedog(mtmp); return mtmp; }
/* 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; struct obj *keepobj = 0; struct pet_weapons p; initialize_pet_weapons(mtmp, &p); while ((otmp = mtmp->minvent) != 0) { obj_extract_self(otmp); if (is_pet && pet_wants_object(&p, otmp)) { otmp->nobj = keepobj; keepobj = otmp; continue; } if (otmp->owornmask & W_MASK(os_wep)) setmnotwielded(mtmp, otmp); mdrop_obj(mtmp, otmp, is_pet && flags.verbose); } /* put kept objects back */ while ((otmp = keepobj) != NULL) { keepobj = otmp->nobj; add_to_minv(mtmp, otmp); } if (show & cansee(omx, omy) && mtmp->dlevel == level) newsym(omx, omy); }
/* Note: may return TRUE when mdef is blind (e.g. new cream-pie attack) */ boolean can_blnd(struct monst * magr, /* NULL == no specific aggressor */ struct monst * mdef, uchar aatyp, struct obj * obj) { /* aatyp == AT_WEAP, AT_SPIT */ boolean is_you = (mdef == &youmonst); boolean check_visor = FALSE; struct obj *o; const char *s; /* no eyes protect against all attacks for now */ if (!haseyes(mdef->data)) return FALSE; switch (aatyp) { case AT_EXPL: case AT_BOOM: case AT_GAZE: case AT_MAGC: case AT_BREA: /* assumed to be lightning */ /* light-based attacks may be cancelled or resisted */ if (magr && magr->mcan) return FALSE; return !resists_blnd(mdef); case AT_WEAP: case AT_SPIT: case AT_NONE: /* an object is used (thrown/spit/other) */ if (obj && (obj->otyp == CREAM_PIE)) { if (is_you && Blindfolded) return FALSE; } else if (obj && (obj->otyp == BLINDING_VENOM)) { /* all ublindf, including LENSES, protect, cream-pies too */ if (is_you && (ublindf || u.ucreamed)) return FALSE; check_visor = TRUE; } else if (obj && (obj->otyp == POT_BLINDNESS)) { return TRUE; /* no defense */ } else return FALSE; /* other objects cannot cause blindness yet */ if ((magr == &youmonst) && Engulfed) return FALSE; /* can't affect eyes while inside monster */ break; case AT_ENGL: if (is_you && (Blindfolded || u_helpless(hm_asleep) || u.ucreamed)) return FALSE; if (!is_you && mdef->msleeping) return FALSE; break; case AT_CLAW: /* e.g. raven: all ublindf, including LENSES, protect */ if (is_you && ublindf) return FALSE; if ((magr == &youmonst) && Engulfed) return FALSE; /* can't affect eyes while inside monster */ check_visor = TRUE; break; case AT_TUCH: case AT_STNG: /* some physical, blind-inducing attacks can be cancelled */ if (magr && magr->mcan) return FALSE; break; default: break; } /* check if wearing a visor (only checked if visor might help) */ if (check_visor) { o = (mdef == &youmonst) ? invent : mdef->minvent; for (; o; o = o->nobj) if ((o->owornmask & W_MASK(os_armh)) && (s = OBJ_DESCR(objects[o->otyp])) != NULL && !strcmp(s, "visored helmet")) return FALSE; } return TRUE; }
/* Returns an object slot mask giving all the reasons why the given player/monster might have the given property, limited by "reasons", an object slot mask (W_EQUIP, INTRINSIC, and ANY_PROPERTY are the most likely values here, but you can specify slots individually if you like). */ unsigned m_has_property(const struct monst *mon, enum youprop property, unsigned reasons, boolean even_if_blocked) { unsigned rv = 0; struct obj *otmp; /* The general case for equipment */ rv |= mworn_extrinsic(mon, property); if (mon == &youmonst) { /* Intrinsics */ if (u.uintrinsic[property] & TIMEOUT) rv |= W_MASK(os_timeout); rv |= u.uintrinsic[property] & (INTRINSIC | I_SPECIAL); /* Birth options */ if (property == BLINDED && flags.permablind) rv |= W_MASK(os_birthopt); if (property == HALLUC && flags.permahallu) rv |= W_MASK(os_birthopt); if (property == UNCHANGING && flags.polyinit_mnum != -1) rv |= W_MASK(os_birthopt); } else { /* Monster tempraries are boolean flags. TODO: Monsters with no eyes are not considered blind. This doesn't make much sense. However, changing it would be a major balance change (due to Elbereth), and so it has been left alone for now. */ if (property == BLINDED && (!mon->mcansee || mon->mblinded)) rv |= W_MASK(os_timeout); if (property == FAST && mon->mspeed == MFAST) rv |= (mon->permspeed == FAST ? W_MASK(os_polyform) : W_MASK(os_outside)); if (property == INVIS && mon->perminvis && !pm_invisible(mon->data)) rv |= W_MASK(os_outside); if (property == STUNNED && mon->mstun) rv |= W_MASK(os_timeout); if (property == CONFUSION && mon->mconf) rv |= W_MASK(os_timeout); } /* Polyform / monster intrinsic */ /* TODO: Change the monster data code into something that doesn't require a giant switch statement or ternary chain to get useful information from it. We use a ternary chain here because it cuts down on repetitive code and so is easier to read. */ if (property == FIRE_RES ? resists_fire(mon) : property == COLD_RES ? resists_cold(mon) : property == SLEEP_RES ? resists_sleep(mon) : property == DISINT_RES ? resists_disint(mon) : property == SHOCK_RES ? resists_elec(mon) : property == POISON_RES ? resists_poison(mon) : property == DRAIN_RES ? resists_drli(mon) : property == SICK_RES ? mon->data->mlet == S_FUNGUS || mon->data == &mons[PM_GHOUL] : property == ANTIMAGIC ? resists_magm(mon) : property == ACID_RES ? resists_acid(mon) : property == STONE_RES ? resists_ston(mon) : property == STUNNED ? u.umonnum == PM_STALKER || mon->data->mlet == S_BAT : property == BLINDED ? !haseyes(mon->data) : property == HALLUC ? Upolyd && dmgtype(mon->data, AD_HALU) : property == SEE_INVIS ? perceives(mon->data) : property == TELEPAT ? telepathic(mon->data) : property == INFRAVISION ? infravision(mon->data) : /* Note: This one assumes that there's no way to permanently turn visible when you're in stalker form (i.e. mummy wrappings only). */ property == INVIS ? pm_invisible(mon->data) : property == TELEPORT ? can_teleport(mon->data) : property == LEVITATION ? is_floater(mon->data) : property == FLYING ? is_flyer(mon->data) : property == SWIMMING ? is_swimmer(mon->data) : property == PASSES_WALLS ? passes_walls(mon->data) : property == REGENERATION ? regenerates(mon->data) : property == REFLECTING ? mon->data == &mons[PM_SILVER_DRAGON] : property == TELEPORT_CONTROL ? control_teleport(mon->data) : property == MAGICAL_BREATHING ? amphibious(mon->data) : 0) rv |= W_MASK(os_polyform); if (mon == &youmonst) { /* External circumstances */ if (property == BLINDED && u_helpless(hm_unconscious)) rv |= W_MASK(os_circumstance); /* Riding */ if (property == FLYING && u.usteed && is_flyer(u.usteed->data)) rv |= W_MASK(os_saddle); if (property == SWIMMING && u.usteed && is_swimmer(u.usteed->data)) rv |= W_MASK(os_saddle); } /* Overrides */ if (!even_if_blocked) { if (property == BLINDED) { for (otmp = m_minvent(mon); otmp; otmp = otmp->nobj) if (otmp->oartifact == ART_EYES_OF_THE_OVERWORLD && otmp->owornmask & W_MASK(os_tool)) rv &= (unsigned)(W_MASK(os_circumstance) | W_MASK(os_birthopt)); } if (property == WWALKING && Is_waterlevel(m_mz(mon))) rv &= (unsigned)(W_MASK(os_birthopt)); if (mworn_blocked(mon, property)) rv &= (unsigned)(W_MASK(os_birthopt)); } return rv & reasons; }
/* called from resurrect() in addition to losedogs() */ void mon_arrive(struct monst *mtmp, boolean with_you) { struct trap *t; struct obj *otmp; xchar xlocale, ylocale, xyloc, xyflags, wander; int num_segs; mtmp->dlevel = level; mtmp->nmon = level->monlist; level->monlist = mtmp; if (mtmp->isshk) set_residency(mtmp, FALSE); num_segs = mtmp->wormno; /* baby long worms have no tail so don't use is_longworm() */ if ((mtmp->data == &mons[PM_LONG_WORM]) && (mtmp->wormno = get_wormno(mtmp->dlevel)) != 0) { initworm(mtmp, num_segs); /* tail segs are not yet initialized or displayed */ } else mtmp->wormno = 0; /* some monsters might need to do something special upon arrival _after_ the current level has been fully set up; see dochug() */ mtmp->mstrategy |= STRAT_ARRIVE; xyloc = mtmp->xyloc; xyflags = mtmp->xyflags; xlocale = mtmp->xlocale; ylocale = mtmp->ylocale; for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) set_obj_level(mtmp->dlevel, otmp); if (mtmp == u.usteed) return; /* don't place steed on the map */ if (with_you) { /* When a monster accompanies you, sometimes it will arrive at your intended destination and you'll end up next to that spot. This code doesn't control the final outcome; goto_level(do.c) decides who ends up at your target spot when there is a monster there too. */ if (!MON_AT(level, u.ux, u.uy) && !rn2(mtmp->mtame ? 10 : mtmp->mpeaceful ? 5 : 2)) rloc_to(mtmp, u.ux, u.uy); else mnexto(mtmp); return; } /* * The monster arrived on this level independently of the player. * Its coordinate fields were overloaded for use as flags that * specify its final destination. */ if (mtmp->mlstmv < moves - 1L) { /* heal monster for time spent in limbo */ long nmv = moves - 1L - mtmp->mlstmv; mon_catchup_elapsed_time(mtmp, nmv); mtmp->mlstmv = moves - 1L; /* let monster move a bit on new level (see placement code below) */ wander = (xchar) min(nmv, 8); } else wander = 0; switch (xyloc) { case MIGR_APPROX_XY: /* {x,y}locale set above */ break; case MIGR_EXACT_XY: wander = 0; break; case MIGR_NEAR_PLAYER: xlocale = u.ux, ylocale = u.uy; break; case MIGR_STAIRS_UP: xlocale = level->upstair.sx, ylocale = level->upstair.sy; break; case MIGR_STAIRS_DOWN: xlocale = level->dnstair.sx, ylocale = level->dnstair.sy; break; case MIGR_LADDER_UP: xlocale = level->upladder.sx, ylocale = level->upladder.sy; break; case MIGR_LADDER_DOWN: xlocale = level->dnladder.sx, ylocale = level->dnladder.sy; break; case MIGR_SSTAIRS: xlocale = level->sstairs.sx, ylocale = level->sstairs.sy; break; case MIGR_PORTAL: if (In_endgame(&u.uz)) { /* there is no arrival portal for endgame levels */ /* BUG[?]: for simplicity, this code relies on the fact that we know that the current endgame levels always build upwards and never have any exclusion subregion inside their TELEPORT_REGION settings. */ xlocale = rn1(level->updest.hx - level->updest.lx + 1, level->updest.lx); ylocale = rn1(level->updest.hy - level->updest.ly + 1, level->updest.ly); break; } /* find the arrival portal */ for (t = level->lev_traps; t; t = t->ntrap) if (t->ttyp == MAGIC_PORTAL) break; if (t) { xlocale = t->tx, ylocale = t->ty; break; } else { impossible("mon_arrive: no corresponding portal?"); } /*FALLTHRU*/ default: case MIGR_RANDOM: xlocale = COLNO; ylocale = ROWNO; break; } if ((xlocale != COLNO) && wander) { /* monster moved a bit; pick a nearby location */ /* mnearto() deals w/stone, et al */ char *r = in_rooms(level, xlocale, ylocale, 0); if (r && *r) { coord c; /* somexy() handles irregular level->rooms */ if (somexy(level, &level->rooms[*r - ROOMOFFSET], &c, rng_main)) xlocale = c.x, ylocale = c.y; else { xlocale = COLNO; ylocale = ROWNO; } } else { /* not in a room */ int i, j; i = max(0, xlocale - wander); j = min(COLNO - 1, xlocale + wander); xlocale = rn1(j - i, i); i = max(0, ylocale - wander); j = min(ROWNO - 1, ylocale + wander); ylocale = rn1(j - i, i); } } /* moved a bit */ mtmp->mx = COLNO; /* (already is 0) */ mtmp->my = xyflags; if (xlocale != COLNO) mnearto(mtmp, xlocale, ylocale, FALSE); else { if (!rloc(mtmp, TRUE)) { /* Failed to place migrating monster, probably because the level is full. Dump the monster's cargo and leave the monster dead. */ struct obj *obj; while ((obj = mtmp->minvent) != 0) { obj_extract_self(obj); obj_no_longer_held(obj); if (obj->owornmask & W_MASK(os_wep)) setmnotwielded(mtmp, obj); obj->owornmask = 0L; if (xlocale != COLNO && ylocale != ROWNO) place_object(obj, level, xlocale, ylocale); else { rloco(obj); get_obj_location(obj, &xlocale, &ylocale, 0); } } mkcorpstat(CORPSE, NULL, mtmp->data, level, xlocale, ylocale, FALSE, rng_main); mongone(mtmp); } } mtmp->mux = COLNO; mtmp->muy = ROWNO; }
int use_saddle(struct obj *otmp, const struct nh_cmd_arg *arg) { struct monst *mtmp; const struct permonst *ptr; int chance; const char *s; schar dx, dy, dz; /* Can you use it? */ if (nohands(youmonst.data)) { pline("You have no hands!"); /* not `body_part(HAND)' */ return 0; } else if (!freehand()) { pline("You have no free %s.", body_part(HAND)); return 0; } /* Select an animal */ if (Engulfed || Underwater || !getargdir(arg, NULL, &dx, &dy, &dz)) { pline("Never mind."); return 0; } if (!dx && !dy) { pline("Saddle yourself? Very funny..."); return 0; } if (!isok(u.ux + dx, u.uy + dy) || !((mtmp = m_at(level, u.ux + dx, u.uy + dy))) || !canspotmon(mtmp)) { if (knownwormtail(u.ux + dx, u.uy + dy)) pline("It's hard to strap a saddle to a tail."); else pline("I see nobody there."); return 0; } /* Is this a valid monster? */ if (mtmp->misc_worn_check & W_MASK(os_saddle) || which_armor(mtmp, os_saddle)) { pline("%s doesn't need another one.", Monnam(mtmp)); return 0; } ptr = mtmp->data; if (!uarmg && touched_monster(ptr - mons)) { pline("You touch %s.", mon_nam(mtmp)); instapetrify(killer_msg(STONING, msgcat("attempting to saddle ", an(mtmp->data->mname)))); } if (ptr == &mons[PM_INCUBUS] || ptr == &mons[PM_SUCCUBUS]) { pline("Shame on you!"); exercise(A_WIS, FALSE); return 1; } if (mtmp->isminion || mtmp->isshk || mtmp->ispriest || mtmp->isgd || mtmp->iswiz) { pline("I think %s would mind.", mon_nam(mtmp)); return 0; } if (!can_saddle(mtmp)) { pline("You can't saddle such a creature."); return 0; } /* Calculate your chance */ chance = ACURR(A_DEX) + ACURR(A_CHA) / 2 + 2 * mtmp->mtame; chance += u.ulevel * (mtmp->mtame ? 20 : 5); if (!mtmp->mtame) chance -= 10 * mtmp->m_lev; if (Role_if(PM_KNIGHT)) chance += 20; switch (P_SKILL(P_RIDING)) { case P_ISRESTRICTED: case P_UNSKILLED: default: chance -= 20; break; case P_BASIC: break; case P_SKILLED: chance += 15; break; case P_EXPERT: chance += 30; break; } if (Confusion || Fumbling || Glib) chance -= 20; else if (uarmg && (s = OBJ_DESCR(objects[uarmg->otyp])) != NULL && !strncmp(s, "riding ", 7)) /* Bonus for wearing "riding" (but not fumbling) gloves */ chance += 10; else if (uarmf && (s = OBJ_DESCR(objects[uarmf->otyp])) != NULL && !strncmp(s, "riding ", 7)) /* ... or for "riding boots" */ chance += 10; if (otmp->cursed) chance -= 50; /* steed becomes alert if possible */ maybewakesteed(mtmp); /* Make the attempt */ if (rn2(100) < chance) { pline("You put the saddle on %s.", mon_nam(mtmp)); if (otmp->owornmask) remove_worn_item(otmp, FALSE); freeinv(otmp); /* mpickobj may free otmp it if merges, but we have already checked for a saddle above, so no merger should happen */ mpickobj(mtmp, otmp); mtmp->misc_worn_check |= W_MASK(os_saddle); otmp->owornmask = W_MASK(os_saddle); otmp->leashmon = mtmp->m_id; update_mon_intrinsics(mtmp, otmp, TRUE, FALSE); } else pline("%s resists!", Monnam(mtmp)); return 1; }
/* 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 */ int steal(struct monst *mtmp, const char **objnambuf) { struct obj *otmp; int tmp, could_petrify, named = 0, armordelay, slowly = 0; boolean monkey_business; /* true iff an animal is doing the thievery */ if (objnambuf) *objnambuf = ""; /* the following is true if successful on first of two attacks. */ if (!monnear(mtmp, u.ux, u.uy)) return 0; if (!invent || (inv_cnt(FALSE) == 1 && uskin())) { nothing_to_steal: /* Not even a thousand men in armor can strip a naked man. */ if (Blind) pline("Somebody tries to rob you, but finds nothing to steal."); else pline("%s tries to rob you, but there is nothing to steal!", Monnam(mtmp)); return 1; /* let her flee */ } monkey_business = is_animal(mtmp->data); if (monkey_business) { ; /* skip ring special cases */ } else if (Adornment & W_MASK(os_ringl)) { otmp = uleft; goto gotobj; } else if (Adornment & W_MASK(os_ringr)) { otmp = uright; goto gotobj; } tmp = 0; for (otmp = invent; otmp; otmp = otmp->nobj) if ((!uarm || otmp != uarmc) && otmp != uskin() #ifdef INVISIBLE_OBJECTS && (!otmp->oinvis || perceives(mtmp->data)) #endif ) tmp += ((otmp->owornmask & W_WORN) ? 5 : 1); if (!tmp) goto nothing_to_steal; tmp = rn2(tmp); for (otmp = invent; otmp; otmp = otmp->nobj) if ((!uarm || otmp != uarmc) && otmp != uskin() #ifdef INVISIBLE_OBJECTS && (!otmp->oinvis || perceives(mtmp->data)) #endif ) if ((tmp -= ((otmp->owornmask & W_WORN) ? 5 : 1)) < 0) break; if (!otmp) { impossible("Steal fails!"); return 0; } /* 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; else if (otmp == uarmu && uarmc) otmp = uarmc; else if (otmp == uarmu && uarm && !uskin()) otmp = uarm; gotobj: /* 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); if (ostuck || !can_carry(mtmp, otmp)) { static const char *const how[] = { "steal", "snatch", "grab", "take" }; cant_take: pline("%s tries to %s your %s but gives up.", Monnam(mtmp), how[rn2(SIZE(how))], (otmp->owornmask & W_ARMOR) ? equipname(otmp) : cxname(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); } /* you're going to notice the theft... */ action_interrupted(); if (otmp->owornmask & W_WORN) { switch (otmp->oclass) { case TOOL_CLASS: case AMULET_CLASS: case RING_CLASS: case FOOD_CLASS: /* meat ring */ remove_worn_item(otmp, TRUE); break; case ARMOR_CLASS: armordelay = objects[otmp->otyp].oc_delay; if (monkey_business) { /* animals usually don't have enough patience to take off items which require extra time */ if (armordelay >= 1 && rn2(10)) goto cant_take; remove_worn_item(otmp, TRUE); break; } else { int curssv = otmp->cursed; boolean seen = canspotmon(mtmp); otmp->cursed = 0; /* can't charm you without first waking you */ cancel_helplessness(hm_fainted, "Someone revives you."); slowly = (armordelay >= 1 || u_helpless(hm_all)); if (u_helpless(hm_all)) { pline("%s tries to %s you, but is dismayed by your lack of " "response.", !seen ? "She" : Monnam(mtmp), u.ufemale ? "charm" : "seduce"); return (0); } if (u.ufemale) pline("%s charms you. You gladly %s your %s.", !seen ? "She" : Monnam(mtmp), curssv ? "let her take" : slowly ? "start removing" : "hand over", equipname(otmp)); else pline("%s seduces you and %s off your %s.", !seen ? "She" : Adjmonnam(mtmp, "beautiful"), curssv ? "helps you to take" : slowly ? "you start taking" : "you take", equipname(otmp)); named++; if (armordelay) helpless(armordelay, hr_busy, "taking off clothes", "You finish disrobing."); remove_worn_item(otmp, TRUE); otmp->cursed = curssv; /* Note: it used to be that the nymph would wait for you to disrobe, then take the item, but that lead to huge complications in the code (and a rather unfun situation where the nymph could chain armor theft), and some resulting bugs. Instead, we just go down the normal codepath; you lose the item, and you're left helpless for the length of time it should have taken to remove. The nymph will stay around (due to the slowly || u_helpless(hm_all) check at the end of the function). */ } break; default: impossible("Tried to steal a strange worn thing. [%d]", otmp->oclass); } } else if (otmp->owornmask) remove_worn_item(otmp, TRUE); /* do this before removing it from inventory */ if (objnambuf) *objnambuf = yname(otmp); /* set mavenge bit so knights won't suffer an alignment penalty during retaliation; */ mtmp->mavenge = 1; freeinv(otmp); pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp)); could_petrify = (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm])); mpickobj(mtmp, otmp); /* may free otmp */ if (could_petrify && !(mtmp->misc_worn_check & W_MASK(os_armg))) { minstapetrify(mtmp, TRUE); return -1; } return (slowly || u_helpless(hm_all)) ? 0 : 1; }