Beispiel #1
0
/*
 * Does falling drawbridge or portcullis miss etmp?
 */
static boolean
e_missed(struct entity *etmp, boolean chunks)
{
    int misses;

    if (automiss(etmp))
        return TRUE;

    if (flying(etmp->emon) &&
        (is_u(etmp) ? !u_helpless(hm_all)
         : (etmp->emon->mcanmove && !etmp->emon->msleeping)))
        /* flying requires mobility */
        misses = 5;     /* out of 8 */
    else if (levitates(etmp->emon) || (is_u(etmp) && Levitation))
        /* doesn't require mobility */
        misses = 3;
    else if (chunks && is_pool(level, etmp->ex, etmp->ey))
        misses = 2;     /* sitting ducks */
    else
        misses = 0;

    if (is_db_wall(etmp->ex, etmp->ey))
        misses -= 3;    /* less airspace */

    enum rng rng = is_u(etmp) ? rng_survive_dbridge : rng_main;

    return (boolean) ((misses > rn2_on_rng(8, rng)) ? TRUE : FALSE);
}
int
tele_impl(boolean wizard_tele, boolean run_next_to_u)
{
    coord cc;

    /* Disable teleportation in stronghold && Vlad's Tower */
    if (level->flags.noteleport) {
        if (!wizard_tele) {
            pline("A mysterious force prevents you from teleporting!");
            return 1;
        }
    }

    /* don't show trap if "Sorry..." */
    if (!Blinded)
        make_blinded(0L, FALSE);

    /* when it happens at all, happens too often to be worth a custom RNG */
    if ((Uhave_amulet || On_W_tower_level(&u.uz)) && !rn2(3)) {
        pline("You feel disoriented for a moment.");
        return 1;
    }
    if ((Teleport_control && !Stunned) || wizard_tele) {
        if (u_helpless(hm_unconscious)) {
            pline("Being unconscious, you cannot control your teleport.");
        } else {
            pline("To what position do you%s want to be teleported?",
                  u.usteed ? msgcat(" and ", mon_nam(u.usteed)) : "");
            cc.x = u.ux;
            cc.y = u.uy;
            if (getpos(&cc, FALSE, "the desired position", FALSE)
                == NHCR_CLIENT_CANCEL)
                return 0; /* abort */

            if (run_next_to_u) {
                if (!next_to_u()) {
                    pline("You shudder for a moment.");
                    return 1;
                }
            }

            /* possible extensions: introduce a small error if magic power is
               low; allow transfer to solid rock */
            if (u_teleok(cc.x, cc.y, FALSE, wizard_tele)) {
                teleds(cc.x, cc.y, FALSE);
                return 1;
            }
            pline("Sorry...");
        }
    }

    safe_teleds(FALSE);
    return 1;
}
void
outrumor(int truth,     /* 1=true, -1=false, 0=either 3=potter*/
         int mechanism)
{
    static const char fortune_msg[] =
        "This cookie has a scrap of paper inside.";
    const char *line;
    boolean reading = (mechanism == BY_COOKIE || mechanism == BY_PAPER);
    int truth_out;

    if (reading) {
        /* deal with various things that prevent reading */
        if (u_helpless(hm_all) && mechanism == BY_COOKIE)
            return;
        else if (Blind) {
            if (mechanism == BY_COOKIE)
                pline(fortune_msg);
            pline("What a pity that you cannot read it!");
            return;
        }
    }
    else{
        truth=3; /* We're talking to Potter, we want the Potter quotes */
    }
    line = getrumor(truth, reading ? FALSE : TRUE, &truth_out, rng_main);
    if (truth_out)
        exercise(A_WIS, truth_out == 1);
    if (!*line)
        line = "NetHack rumors file closed for renovation.";
    switch (mechanism) {
    case BY_ORACLE:
        /* Oracle delivers the rumor */
        pline("True to his word, Potter %ssays: ",
              (!rn2(4) ? "nonchalantly "
               : (!rn2(3) ? "casually " : (rn2(2) ? "excitedly " : ""))));
        verbalize("%s", line);
        return;
    case BY_COOKIE:
        pline(fortune_msg);
        /* FALLTHRU */
    case BY_PAPER:
        pline("It reads:");
        break;
    }
    pline("%s", line);
}
Beispiel #4
0
int
breamq(struct monst *mtmp, int xdef, int ydef, const struct attack *mattk)
{
    /* if new breath types are added, change AD_ACID to max type */
    int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp;

    boolean youdef = u.ux == xdef && u.uy == ydef;

    if (!youdef && distmin(mtmp->mx, mtmp->my, xdef, ydef) < 3)
        return 0;

    boolean linedup = qlined_up(mtmp, xdef, ydef, TRUE, FALSE);

    if (linedup) {
        if (mtmp->mcan) {
            if (canhear()) {
                if (mon_visible(mtmp))
                    pline("%s coughs.", Monnam(mtmp));
                else
                    You_hear("a cough.");
            }
            return 0;
        }
        if (!mtmp->mspec_used && rn2(3)) {
            if ((typ >= AD_MAGM) && (typ <= AD_ACID)) {
                if (mon_visible(mtmp)) {
                    pline("%s breathes %s!", Monnam(mtmp), breathwep[typ - 1]);
                    action_interrupted();
                }
                buzz((int)(-20 - (typ - 1)), (int)mattk->damn, mtmp->mx,
                     mtmp->my, sgn(tbx), sgn(tby), 0);
                /* breath runs out sometimes. Also, give monster some cunning;
                   don't breath if the target fell asleep. */
                if (!rn2(3))
                    mtmp->mspec_used = 10 + rn2(20);
                boolean sleeping = youdef ? u_helpless(hm_asleep) :
                    MON_AT(level, xdef, ydef) ?
                    m_at(level, xdef, ydef)->msleeping : FALSE;
                if (typ == AD_SLEE && sleeping)
                    mtmp->mspec_used += rnd(20);
            } else
                impossible("Breath weapon %d used", typ - 1);
        }
    }
    return 1;
}
Beispiel #5
0
boolean
m_helpless(const struct monst *mon, enum helpless_mask mask)
{
    if (mon == &youmonst)
        return u_helpless(mask);
    if (mon->msleeping && (mask & (1 << hr_asleep)))
        return TRUE;
    if (!mon->mcanmove && (mask & (1 << hr_paralyzed)))
        return TRUE;
    if (mon->meating && (mask & (1 << hr_busy)))
        return TRUE;
    if ((mon->m_ap_type == M_AP_OBJECT || mon->m_ap_type == M_AP_FURNITURE) &&
        (mask & (1 << hr_mimicking)))
        return TRUE;

    return FALSE;
}
Beispiel #6
0
/*
 * Can etmp jump from death?
 */
static boolean
e_jumps(struct entity *etmp)
{
    int tmp = 4;        /* out of 10 */

    if (fumbling(etmp->emon) ||
        (is_u(etmp) ? u_helpless(hm_all) :
         (etmp->emon->mcanmove || etmp->emon->msleeping ||
          !etmp->edata->mmove || etmp->emon->wormno)))
        return FALSE;

    if (confused(etmp->emon))
        tmp -= 2;

    if (stunned(etmp->emon))
        tmp -= 3;

    if (is_db_wall(etmp->ex, etmp->ey))
        tmp -= 2;       /* less room to maneuver */

    enum rng rng = is_u(etmp) ? rng_survive_dbridge : rng_main;

    return (boolean) ((tmp > rn2_on_rng(10, rng)) ? TRUE : FALSE);
}
/* 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;
}
Beispiel #8
0
void
m_throw(struct monst *mon, int x, int y, int dx, int dy, int range,
        struct obj *obj, boolean verbose)
{
    struct monst *mtmp;
    struct obj *singleobj;
    struct tmp_sym *tsym = 0;
    int hitu, blindinc = 0;

    bhitpos.x = x;
    bhitpos.y = y;

    if (obj->quan == 1L) {
        /*
         * Remove object from minvent.  This cannot be done later on;
         * what if the player dies before then, leaving the monster
         * with 0 daggers?  (This caused the infamous 2^32-1 orcish
         * dagger bug).
         *
         * VENOM is not in minvent - it should already be OBJ_FREE.
         * The extract below does nothing.
         */

        /* not possibly_unwield, which checks the object's */
        /* location, not its existence */
        if (MON_WEP(mon) == obj) {
            setmnotwielded(mon, obj);
            MON_NOWEP(mon);
        }
        obj_extract_self(obj);
        singleobj = obj;
        obj = NULL;
    } else {
        singleobj = splitobj(obj, 1L);
        obj_extract_self(singleobj);
    }

    singleobj->owornmask = 0;   /* threw one of multiple weapons in hand? */
    singleobj->olev = level;    /* object is on the same level as monster */

    if ((singleobj->cursed || singleobj->greased) && (dx || dy) && !rn2(7)) {
        if (canseemon(mon) && flags.verbose) {
            if (is_ammo(singleobj))
                pline("%s misfires!", Monnam(mon));
            else
                pline("%s as %s throws it!", Tobjnam(singleobj, "slip"),
                      mon_nam(mon));
        }
        dx = rn2(3) - 1;
        dy = rn2(3) - 1;
        /* check validity of new direction */
        if (!dx && !dy) {
            drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
            return;
        }
    }

    /* pre-check for doors, walls and boundaries. Also need to pre-check for
       bars regardless of direction; the random chance for small objects
       hitting bars is skipped when reaching them at point blank range */
    if (!isok(bhitpos.x + dx, bhitpos.y + dy)
        || IS_ROCK(level->locations[bhitpos.x + dx][bhitpos.y + dy].typ)
        || closed_door(level, bhitpos.x + dx, bhitpos.y + dy)
        || (level->locations[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS &&
            hits_bars(&singleobj, bhitpos.x, bhitpos.y, 0, 0))) {
        drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
        return;
    }

    /* Note: drop_throw may destroy singleobj.  Since obj must be destroyed
       early to avoid the dagger bug, anyone who modifies this code should be
       careful not to use either one after it's been freed. */
    tsym = tmpsym_initobj(singleobj);

    while (range-- > 0) {      /* Actually the loop is always exited by break */
        bhitpos.x += dx;
        bhitpos.y += dy;
        if ((mtmp = m_at(level, bhitpos.x, bhitpos.y)) != 0) {
            if (ohitmon(mtmp, singleobj, range, verbose))
                break;
        } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
            action_interrupted();

            if (singleobj->oclass == GEM_CLASS &&
                singleobj->otyp <= LAST_GEM + 9
                /* 9 glass colors */
                && is_unicorn(youmonst.data) && !u_helpless(hm_all)) {
                if (singleobj->otyp > LAST_GEM) {
                    pline("You catch the %s.", xname(singleobj));
                    pline("You are not interested in %s junk.",
                          s_suffix(mon_nam(mon)));
                    makeknown(singleobj->otyp);
                    dropy(singleobj);
                } else {
                    pline("You accept %s gift in the spirit in which it was "
                          "intended.", s_suffix(mon_nam(mon)));
                    hold_another_object(singleobj, "You catch, but drop, %s.",
                                        xname(singleobj), "You catch:");
                }
                break;
            }
            if (singleobj->oclass == POTION_CLASS) {
                if (!Blind)
                    singleobj->dknown = 1;
                potionhit(&youmonst, singleobj, FALSE);
                break;
            }
            switch (singleobj->otyp) {
                int dam, hitv;

            case EGG:
                if (!touch_petrifies(&mons[singleobj->corpsenm])) {
                    impossible("monster throwing egg type %d",
                               singleobj->corpsenm);
                    hitu = 0;
                    break;
                }
                /* fall through */
            case CREAM_PIE:
            case BLINDING_VENOM:
                hitu = thitu(8, 0, singleobj, NULL);
                break;
            default:
                dam = dmgval(singleobj, &youmonst);
                hitv = 3 - distmin(u.ux, u.uy, mon->mx, mon->my);
                if (hitv < -4)
                    hitv = -4;
                if (is_elf(mon->data) &&
                    objects[singleobj->otyp].oc_skill == P_BOW) {
                    hitv++;
                    if (MON_WEP(mon) && MON_WEP(mon)->otyp == ELVEN_BOW)
                        hitv++;
                    if (singleobj->otyp == ELVEN_ARROW)
                        dam++;
                }
                if (bigmonst(youmonst.data))
                    hitv++;
                hitv += 8 + singleobj->spe;
                if (dam < 1)
                    dam = 1;
                if (objects[singleobj->otyp].oc_class == WEAPON_CLASS ||
                    objects[singleobj->otyp].oc_class == VENOM_CLASS) {
                    hitv += objects[singleobj->otyp].oc_hitbon;
                }
                hitu = thitu(hitv, dam, singleobj, NULL);
            }
            if (hitu && singleobj->opoisoned && is_poisonable(singleobj)) {
                poisoned(xname(singleobj), A_STR,
                         killer_msg_obj(POISONING, singleobj), -10);
            }
            if (hitu &&
                can_blnd(NULL, &youmonst,
                         (uchar) (singleobj->otyp ==
                                  BLINDING_VENOM ? AT_SPIT : AT_WEAP),
                         singleobj)) {
                blindinc = rnd(25);
                if (singleobj->otyp == CREAM_PIE) {
                    if (!Blind)
                        pline("Yecch!  You've been creamed.");
                    else
                        pline("There's something sticky all over your %s.",
                              body_part(FACE));
                } else if (singleobj->otyp == BLINDING_VENOM) {
                    int num_eyes = eyecount(youmonst.data);

                    /* venom in the eyes */
                    if (!Blind)
                        pline("The venom blinds you.");
                    else
                        pline("Your %s sting%s.",
                              (num_eyes ==
                               1) ? body_part(EYE) : makeplural(body_part(EYE)),
                              (num_eyes == 1) ? "s" : "");
                }
            }
            if (hitu && singleobj->otyp == VAMPIRE_BLOOD) {
                if (!Drain_resistance) {
                    losexp("vampire blood", FALSE);
                }
            }
            if (hitu && singleobj->otyp == EGG) {
                if (touched_monster(singleobj->corpsenm))
                    Stoned = 5;
            }
            action_interrupted();
            if (hitu || !range) {
                drop_throw(singleobj, hitu, u.ux, u.uy);
                break;
            }
        } else if (!range       /* reached end of path */
                   /* missile hits edge of screen */
                   || !isok(bhitpos.x + dx, bhitpos.y + dy)
                   /* missile hits the wall */
                   || IS_ROCK(level->
                              locations[bhitpos.x + dx][bhitpos.y + dy].typ)
                   /* missile hit closed door */
                   || closed_door(level, bhitpos.x + dx, bhitpos.y + dy)
                   /* missile might hit iron bars */
                   || (level->locations[bhitpos.x + dx][bhitpos.y + dy].typ ==
                       IRONBARS &&
                       hits_bars(&singleobj, bhitpos.x, bhitpos.y, !rn2(5), 0))
                   /* Thrown objects "sink" */
                   || IS_SINK(level->locations[bhitpos.x][bhitpos.y].typ)) {
            if (singleobj)      /* hits_bars might have destroyed it */
                drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
            break;
        }
        tmpsym_at(tsym, bhitpos.x, bhitpos.y);
        win_delay_output();
    }
    tmpsym_at(tsym, bhitpos.x, bhitpos.y);
    win_delay_output();
    tmpsym_end(tsym);

    if (blindinc) {
        u.ucreamed += blindinc;
        make_blinded(Blinded + (long)blindinc, FALSE);
        if (!Blind)
            pline("Your vision quickly clears.");
        else if (flags.verbose)
            pline("Use the command #wipe to clean your %s.", body_part(FACE));
    }
}
void
exerchk(void)
{
    int i, mod_val;

    /* Check out the periodic accumulations */
    exerper();

    /* Are we ready for a test? */
    if (moves >= u.next_attr_check && !u_helpless(hm_all)) {
        /*
         *  Law of diminishing returns (Part II):
         *
         *  The effects of "exercise" and "abuse" wear
         *  off over time.  Even if you *don't* get an
         *  increase/decrease, you lose some of the
         *  accumulated effects.
         */
        for (i = 0; i < A_MAX; AEXE(i++) /= 2) {

            if (ABASE(i) >= 18 || !AEXE(i))
                continue;
            if (i == A_INT || i == A_CHA)
                continue;       /* can't exercise these */

            /*
             *      Law of diminishing returns (Part III):
             *
             *      You don't *always* gain by exercising.
             *      [MRS 92/10/28 - Treat Wisdom specially for balance.]
             */
            if (rn2(AVAL) >
                ((i != A_WIS) ? abs(AEXE(i) * 2 / 3) : abs(AEXE(i))))
                continue;
            mod_val = sgn(AEXE(i));

            if (adjattrib(i, mod_val, -1)) {
                /* if you actually changed an attrib - zero accumulation */
                AEXE(i) = 0;
                /* then print an explanation */
                switch (i) {
                case A_STR:
                    pline((mod_val >
                           0) ? "You must have been exercising." :
                          "You must have been abusing your body.");
                    break;
                case A_WIS:
                    pline((mod_val >
                           0) ? "You must have been very observant." :
                          "You haven't been paying attention.");
                    break;
                case A_DEX:
                    pline((mod_val >
                           0) ? "You must have been working on your reflexes." :
                          "You haven't been working on reflexes lately.");
                    break;
                case A_CON:
                    pline((mod_val >
                           0) ? "You must be leading a healthy life-style." :
                          "You haven't been watching your health.");
                    break;
                }
            }
        }
        u.next_attr_check += rn1(200, 800);
    }
}
Beispiel #10
0
/* 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;
}
Beispiel #11
0
/*
 * return  1: guard moved,  0: guard didn't,  -1: let m_move do it,  -2: died
 */
int
gd_move(struct monst *grd)
{
    int x, y, nx, ny, m, n;
    int dx, dy, gx = 0, gy = 0, fci;
    uchar typ;
    struct fakecorridor *fcp;
    struct egd *egrd = EGD(grd);
    struct rm *crm;
    boolean goldincorridor = FALSE, u_in_vault =
        vault_occupied(u.urooms) ? TRUE : FALSE, grd_in_vault =
        *in_rooms(level, grd->mx, grd->my, VAULT) ? TRUE : FALSE;
    boolean disappear_msg_seen = FALSE, semi_dead = (grd->mhp <= 0);
    long umoney = money_cnt(invent);
    boolean u_carry_gold = ((umoney + hidden_gold()) > 0L);
    boolean see_guard;

    if (!on_level(&(egrd->gdlevel), &u.uz))
        return -1;
    nx = ny = m = n = 0;
    if (!u_in_vault && !grd_in_vault)
        wallify_vault(grd);
    if (!grd->mpeaceful) {
        if (semi_dead) {
            egrd->gddone = 1;
            goto newpos;
        }
        if (!u_in_vault &&
            (grd_in_vault ||
             (in_fcorridor(grd, grd->mx, grd->my) &&
              !in_fcorridor(grd, u.ux, u.uy)))) {
            rloc(grd, FALSE);
            wallify_vault(grd);
            clear_fcorr(grd, TRUE);
            goto letknow;
        }
        if (!in_fcorridor(grd, grd->mx, grd->my))
            clear_fcorr(grd, TRUE);
        return -1;
    }
    if (abs(egrd->ogx - grd->mx) > 1 || abs(egrd->ogy - grd->my) > 1)
        return -1;      /* teleported guard - treat as monster */
    if (egrd->fcend == 1) {
        if (u_in_vault && (u_carry_gold || um_dist(grd->mx, grd->my, 1))) {
            if (egrd->warncnt == 3)
                verbalize("I repeat, %sfollow me!",
                          u_carry_gold ? (!umoney ?
                                          "drop that hidden money and " :
                                          "drop that money and ") : "");
            if (egrd->warncnt == 7) {
                m = grd->mx;
                n = grd->my;
                verbalize("You've been warned, knave!");
                mnexto(grd);
                level->locations[m][n].typ = egrd->fakecorr[0].ftyp;
                newsym(m, n);
                msethostility(grd, TRUE, FALSE);
                return -1;
            }
            /* not fair to get mad when (s)he's fainted or paralyzed */
            if (!u_helpless(hm_all))
                egrd->warncnt++;
            return 0;
        }

        if (!u_in_vault) {
            if (u_carry_gold) { /* player teleported */
                m = grd->mx;
                n = grd->my;
                rloc(grd, FALSE);
                level->locations[m][n].typ = egrd->fakecorr[0].ftyp;
                newsym(m, n);
                msethostility(grd, TRUE, FALSE);
            letknow:
                if (!cansee(grd->mx, grd->my) || !mon_visible(grd))
                    You_hear("the shrill sound of a guard's whistle.");
                else
                    pline(um_dist(grd->mx, grd->my, 2) ?
                          "You see an angry guard approaching." :
                          "You are confronted by an angry guard.");
                return -1;
            } else {
                verbalize("Well, begone.");
                wallify_vault(grd);
                egrd->gddone = 1;
                goto cleanup;
            }
        }
    }

    if (egrd->fcend > 1) {
        if (egrd->fcend > 2 && in_fcorridor(grd, grd->mx, grd->my) &&
            !egrd->gddone && !in_fcorridor(grd, u.ux, u.uy) &&
            level->locations[egrd->fakecorr[0].fx][egrd->fakecorr[0].fy].typ ==
            egrd->fakecorr[0].ftyp) {
            if (canseemon(grd)) {
                pline("%s, confused, disappears.", Monnam(grd));
                disappear_msg_seen = TRUE;
            }
            goto cleanup;
        }
        if (u_carry_gold && (in_fcorridor(grd, u.ux, u.uy) ||
                             /* cover a 'blind' spot */
                             (egrd->fcend > 1 && u_in_vault))) {
            if (!grd->mx) {
                restfakecorr(grd);
                return -2;
            }
            if (egrd->warncnt < 6) {
                egrd->warncnt = 6;
                verbalize("Drop all your gold, scoundrel!");
                return 0;
            } else {
                verbalize("So be it, rogue!");
                msethostility(grd, TRUE, FALSE);
                return -1;
            }
        }
    }
    for (fci = egrd->fcbeg; fci < egrd->fcend; fci++)
        if (gold_at(level, egrd->fakecorr[fci].fx, egrd->fakecorr[fci].fy)) {
            m = egrd->fakecorr[fci].fx;
            n = egrd->fakecorr[fci].fy;
            goldincorridor = TRUE;
        }
    if (goldincorridor && !egrd->gddone) {
        boolean yours = FALSE;

        x = grd->mx;
        y = grd->my;
        if (m == u.ux && n == u.uy) {
            struct obj *gold = gold_at(level, m, n);

            yours = TRUE;
            /* Grab the gold from between the hero's feet.  */
            obj_extract_self(gold);
            add_to_minv(grd, gold);
            newsym(m, n);
        } else if (m == x && n == y) {
            mpickgold(grd);     /* does a newsym */
        } else {
            /* just for insurance... */
            if (MON_AT(level, m, n) && m != grd->mx && n != grd->my) {
                verbalize("Out of my way, scum!");
                rloc(m_at(level, m, n), FALSE);
            }
            remove_monster(level, grd->mx, grd->my);
            newsym(grd->mx, grd->my);
            place_monster(grd, m, n);
            mpickgold(grd);     /* does a newsym */
        }
        if (cansee(m, n)) {
            if (yours) {
                pline("%s%s picks up the gold.", Monnam(grd),
                      grd->mpeaceful ? " calms down and" : "");
            } else {
                pline("%s picks up some gold.", Monnam(grd));
            }
        }
        if (x != grd->mx || y != grd->my) {
            remove_monster(level, grd->mx, grd->my);
            newsym(grd->mx, grd->my);
            place_monster(grd, x, y);
            newsym(x, y);
        }
        if (!grd->mpeaceful)
            return -1;
        else {
            egrd->warncnt = 5;
            return 0;
        }
    }
    if (um_dist(grd->mx, grd->my, 1) || egrd->gddone) {
        if (!egrd->gddone && !rn2(10))
            verbalize("Move along!");
        restfakecorr(grd);
        return 0;       /* didn't move */
    }
    x = grd->mx;
    y = grd->my;

    if (u_in_vault)
        goto nextpos;

    /* look around (hor & vert only) for accessible places */
    for (nx = x - 1; nx <= x + 1; nx++)
        for (ny = y - 1; ny <= y + 1; ny++) {
            if ((nx == x || ny == y) && (nx != x || ny != y) && isok(nx, ny)) {

                typ = (crm = &level->locations[nx][ny])->typ;
                if (!IS_STWALL(typ) && !IS_POOL(typ)) {

                    if (in_fcorridor(grd, nx, ny))
                        goto nextnxy;

                    if (*in_rooms(level, nx, ny, VAULT))
                        continue;

                    /* seems we found a good place to leave him alone */
                    egrd->gddone = 1;
                    if (ACCESSIBLE(typ))
                        goto newpos;
                    crm->typ = (typ == SCORR) ? CORR : DOOR;
                    if (crm->typ == DOOR)
                        crm->doormask = D_NODOOR;
                    goto proceed;
                }
            }
        nextnxy:;
        }
nextpos:
    nx = x;
    ny = y;
    gx = egrd->gdx;
    gy = egrd->gdy;
    dx = (gx > x) ? 1 : (gx < x) ? -1 : 0;
    dy = (gy > y) ? 1 : (gy < y) ? -1 : 0;
    if (abs(gx - x) >= abs(gy - y))
        nx += dx;
    else
        ny += dy;

    while ((typ = (crm = &level->locations[nx][ny])->typ) != 0) {
        /* in view of the above we must have IS_WALL(typ) or typ == POOL */
        /* must be a wall here */
        if (isok(nx + nx - x, ny + ny - y) && !IS_POOL(typ) &&
            IS_ROOM(level->locations[nx + nx - x][ny + ny - y].typ)) {
            crm->typ = DOOR;
            crm->doormask = D_NODOOR;
            goto proceed;
        }
        if (dy && nx != x) {
            nx = x;
            ny = y + dy;
            continue;
        }
        if (dx && ny != y) {
            ny = y;
            nx = x + dx;
            dy = 0;
            continue;
        }
        /* I don't like this, but ... */
        if (IS_ROOM(typ)) {
            crm->typ = DOOR;
            crm->doormask = D_NODOOR;
            goto proceed;
        }
        break;
    }
    crm->typ = CORR;
proceed:
    unblock_point(nx, ny);      /* doesn't block light */
    if (cansee(nx, ny))
        newsym(nx, ny);

    if ((nx != gx || ny != gy) || (grd->mx != gx || grd->my != gy)) {
        fcp = &(egrd->fakecorr[egrd->fcend]);
        if (egrd->fcend++ == FCSIZ)
            panic("fakecorr overflow");
        fcp->fx = nx;
        fcp->fy = ny;
        fcp->ftyp = typ;
    } else if (!egrd->gddone) {
        /* We're stuck, so try to find a new destination. */
        if (!find_guard_dest(grd, &egrd->gdx, &egrd->gdy) ||
            (egrd->gdx == gx && egrd->gdy == gy)) {
            pline("%s, confused, disappears.", Monnam(grd));
            disappear_msg_seen = TRUE;
            goto cleanup;
        } else
            goto nextpos;
    }
newpos:
    if (egrd->gddone) {
        /* The following is a kludge.  We need to keep the guard around in order
           to be able to make the fake corridor disappear as the player moves
           out of it, but we also need the guard out of the way.  We send the
           guard to never-never land.  We set ogx ogy to mx my in order to avoid
           a check at the top of this function.  At the end of the process, the
           guard is killed in restfakecorr().  */
    cleanup:
        x = grd->mx;
        y = grd->my;

        see_guard = canspotmon(grd);
        wallify_vault(grd);
        remove_monster(level, grd->mx, grd->my);
        newsym(grd->mx, grd->my);
        grd->mx = COLNO;
        grd->my = ROWNO;
        egrd->ogx = grd->mx;
        egrd->ogy = grd->my;
        restfakecorr(grd);
        if (!semi_dead && (in_fcorridor(grd, u.ux, u.uy) || cansee(x, y))) {
            if (!disappear_msg_seen && see_guard)
                pline("Suddenly, %s disappears.", noit_mon_nam(grd));
            return 1;
        }
        return -2;
    }
    egrd->ogx = grd->mx;        /* update old positions */
    egrd->ogy = grd->my;
    remove_monster(level, grd->mx, grd->my);
    place_monster(grd, nx, ny);
    newsym(grd->mx, grd->my);
    restfakecorr(grd);
    return 1;
}
Beispiel #12
0
void
invault(void)
{
    struct monst *guard;
    int trycount, vaultroom = (int)vault_occupied(u.urooms);
    boolean messages = TRUE;

    if (!vaultroom) {
        u.uinvault = 0;
        return;
    }

    vaultroom -= ROOMOFFSET;

    guard = findgd();
    if (++u.uinvault % 30 == 0 && !guard) {  /* if time ok and no guard now. */
        int x, y, gx, gy;
        xchar rx, ry;
        long umoney;
        const char *buf;

        /* first find the goal for the guard */
        if (!find_guard_dest(NULL, &rx, &ry))
            return;
        gx = rx;
        gy = ry;

        /* next find a good place for a door in the wall */
        x = u.ux;
        y = u.uy;
        if (level->locations[x][y].typ != ROOM) {       /* player dug a door
                                                           and is in it */
            if (level->locations[x + 1][y].typ == ROOM)
                x = x + 1;
            else if (level->locations[x][y + 1].typ == ROOM)
                y = y + 1;
            else if (level->locations[x - 1][y].typ == ROOM)
                x = x - 1;
            else if (level->locations[x][y - 1].typ == ROOM)
                y = y - 1;
            else if (level->locations[x + 1][y + 1].typ == ROOM) {
                x = x + 1;
                y = y + 1;
            } else if (level->locations[x - 1][y - 1].typ == ROOM) {
                x = x - 1;
                y = y - 1;
            } else if (level->locations[x + 1][y - 1].typ == ROOM) {
                x = x + 1;
                y = y - 1;
            } else if (level->locations[x - 1][y + 1].typ == ROOM) {
                x = x - 1;
                y = y + 1;
            }
        }
        while (level->locations[x][y].typ == ROOM) {
            int dx, dy;

            dx = (gx > x) ? 1 : (gx < x) ? -1 : 0;
            dy = (gy > y) ? 1 : (gy < y) ? -1 : 0;
            if (abs(gx - x) >= abs(gy - y))
                x += dx;
            else
                y += dy;
        }
        if (x == u.ux && y == u.uy) {
            if (level->locations[x + 1][y].typ == HWALL ||
                level->locations[x + 1][y].typ == DOOR)
                x = x + 1;
            else if (level->locations[x - 1][y].typ == HWALL ||
                     level->locations[x - 1][y].typ == DOOR)
                x = x - 1;
            else if (level->locations[x][y + 1].typ == VWALL ||
                     level->locations[x][y + 1].typ == DOOR)
                y = y + 1;
            else if (level->locations[x][y - 1].typ == VWALL ||
                     level->locations[x][y - 1].typ == DOOR)
                y = y - 1;
            else
                return;
        }

        /* make something interesting happen */
        if (!(guard = makemon(&mons[PM_GUARD], level, x, y, NO_MM_FLAGS)))
            return;
        guard->isgd = 1;
        msethostility(guard, FALSE, TRUE);
        EGD(guard)->gddone = 0;
        EGD(guard)->ogx = x;
        EGD(guard)->ogy = y;
        assign_level(&(EGD(guard)->gdlevel), &u.uz);
        EGD(guard)->vroom = vaultroom;
        EGD(guard)->warncnt = 0;

        /* We used to reset fainted status here, but that doesn't really make
           sense; instead, that's treated like normal helplessness */
        if (canspotmon(guard))
            pline("Suddenly one of the Vault's guards enters!");
        else if (canhear())
            You_hear("someone else enter the Vault.");
        else
            messages = FALSE;

        newsym(guard->mx, guard->my);
        if (youmonst.m_ap_type == M_AP_OBJECT || u.uundetected || u.uburied) {
            if (youmonst.m_ap_type == M_AP_OBJECT &&
                youmonst.mappearance != GOLD_PIECE)
                verbalize("Hey! Who left that %s in here?",
                          mimic_obj_name(&youmonst));
            /* You're mimicking some object or you're hidden. */
            if (messages)
                pline("Puzzled, %s turns around and leaves.", mhe(guard));
            mongone(guard);
            return;
        }
        if (Engulfed) {
            if ((!u.ustuck->minvis || perceives(guard->data)))
                verbalize("How did that %s get in here?", m_monnam(u.ustuck));
            if (messages)
                pline("Puzzled, %s turns around and leaves.", mhe(guard));
            mongone(guard);
            return;
        }
        if (Strangled || is_silent(youmonst.data) || u_helpless(hm_all)) {
            /* [we ought to record whether this this message has already been
               given in order to vary it upon repeat visits, but discarding the 
               monster and its egd data renders that hard] */
            verbalize("I'll be back when you're ready to speak to me!");
            mongone(guard);
            return;
        }

        action_interrupted();

        trycount = 5;
        do {
            buf = getlin("\"Hello stranger, who are you?\" -", FALSE);
            buf = msgmungspaces(buf);
        } while (!letter(buf[0]) && --trycount > 0);

        if (u.ualign.type == A_LAWFUL &&
            /* ignore trailing text, in case player includes character's rank */
            strncmpi(buf, u.uplname, (int)strlen(u.uplname)) != 0) {
            adjalign(-1);       /* Liar! */
        }

        if (!strcmpi(buf, "Croesus") || !strcmpi(buf, "Kroisos") ||
            !strcmpi(buf, "Creosote")) {
            if (!mvitals[PM_CROESUS].died) {
                verbalize("Oh, yes, of course.  Sorry to have disturbed you.");
                mongone(guard);
            } else {
                setmangry(guard);
                verbalize("Back from the dead, are you?  I'll remedy that!");
                /* don't want guard to waste next turn wielding a weapon */
                if (!MON_WEP(guard)) {
                    guard->weapon_check = NEED_HTH_WEAPON;
                    mon_wield_item(guard);
                }
            }
            return;
        }
        verbalize("I don't know you.");
        umoney = money_cnt(invent);
        if (!umoney && !hidden_gold())
            verbalize("Please follow me.");
        else {
            if (!umoney)
                verbalize("You have hidden money.");
            verbalize("Most likely all your money was stolen from this vault.");
            verbalize("Please drop that money and follow me.");
        }

        EGD(guard)->gdx = gx;
        EGD(guard)->gdy = gy;
        EGD(guard)->fcbeg = 0;
        EGD(guard)->fakecorr[0].fx = x;
        EGD(guard)->fakecorr[0].fy = y;
        if (IS_WALL(level->locations[x][y].typ))
            EGD(guard)->fakecorr[0].ftyp = level->locations[x][y].typ;
        else {  /* the initial guard location is a dug door */
            int vlt = EGD(guard)->vroom;
            xchar lowx = level->rooms[vlt].lx, hix = level->rooms[vlt].hx;
            xchar lowy = level->rooms[vlt].ly, hiy = level->rooms[vlt].hy;

            if (x == lowx - 1 && y == lowy - 1)
                EGD(guard)->fakecorr[0].ftyp = TLCORNER;
            else if (x == hix + 1 && y == lowy - 1)
                EGD(guard)->fakecorr[0].ftyp = TRCORNER;
            else if (x == lowx - 1 && y == hiy + 1)
                EGD(guard)->fakecorr[0].ftyp = BLCORNER;
            else if (x == hix + 1 && y == hiy + 1)
                EGD(guard)->fakecorr[0].ftyp = BRCORNER;
            else if (y == lowy - 1 || y == hiy + 1)
                EGD(guard)->fakecorr[0].ftyp = HWALL;
            else if (x == lowx - 1 || x == hix + 1)
                EGD(guard)->fakecorr[0].ftyp = VWALL;
        }
        level->locations[x][y].typ = DOOR;
        level->locations[x][y].doormask = D_NODOOR;
        unblock_point(x, y);    /* doesn't block light */
        EGD(guard)->fcend = 1;
        EGD(guard)->warncnt = 1;
    }
}
Beispiel #13
0
/* 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;
}