Пример #1
0
/* Wake every monster in range... */
void
awaken_monsters(struct monst *mon, int distance)
{
    struct monst *mtmp;
    int distm;

    for (mtmp = level->monlist; mtmp; mtmp = mtmp->nmon) {
        if (DEADMONSTER(mtmp) || mon == mtmp)
            continue;

        distm = dist2(m_mx(mon), m_my(mon), mtmp->mx, mtmp->my);
        if (distm >= distance || mon == mtmp)
            continue;

        mtmp->msleeping = 0;
        mtmp->mcanmove = 1;
        mtmp->mfrozen = 0;

        /* May scare some monsters */
        if (distm >= distance / 3 ||
            resist(mon, mtmp, TOOL_CLASS, NOTELL, 0))
            continue;

        monflee(mtmp, 0, FALSE, TRUE);
    }

    /* affect the player */
    distm = dist2(m_mx(mon), m_my(mon), youmonst.mx, youmonst.my);
    if (distm >= distance || mon == &youmonst)
        return;

    cancel_helplessness(hm_unconscious,
                        "Surprised, you are jolted into full consciousness!");

    if (distm >= distance / 3)
        return;

    /* there is no real player scare effect, so make it work similar to potion
       ghosts, except only 2 turns. Until an actual player scared effect can
       be made, monsters utilizing the paralyzation "intelligently" isn't
       fair game -- it currently only happens if a monster is about to discover
       that a magical instrument horn is out of charges */
    if (mon != &youmonst) {
        pline(msgc_statusbad,
              "The loud noise frightens you momentarily and you are unable to move!");
        helpless(2, hr_afraid, "being frightened",
                 "You regain your composure.");
    }
}
Пример #2
0
static int
domonnoise(struct monst *mtmp)
{
    const char *pline_msg = 0,  /* Monnam(mtmp) will be prepended */
        *verbl_msg = 0; /* verbalize() */
    const struct permonst *ptr = mtmp->data;

    /* presumably nearness checks have already been made */
    if (!canhear())
        return 0;
    if (is_silent(ptr))
        return 0;

    /* Make sure its your role's quest quardian; adjust if not */
    if (ptr->msound == MS_GUARDIAN && ptr != &pm_guardian) {
        int mndx = monsndx(ptr);

        ptr = &mons[genus(mndx, 1)];
    }

    /* be sure to do this before talking; the monster might teleport away, in
       which case we want to check its pre-teleport position */
    if (!canspotmon(mtmp))
        map_invisible(mtmp->mx, mtmp->my);

    switch (ptr->msound) {
    case MS_ORACLE:
        return doconsult(mtmp);
    case MS_PRIEST:
        priest_talk(mtmp);
        break;
    case MS_LEADER:
    case MS_NEMESIS:
    case MS_GUARDIAN:
        quest_chat(mtmp);
        break;
    case MS_SELL:      /* pitch, pay, total */
        shk_chat(mtmp);
        break;
    case MS_VAMPIRE:
        {
            /* vampire messages are varied by tameness, peacefulness, and time
               of night */
            boolean isnight = night();
            boolean kindred = (Upolyd &&
                               (u.umonnum == PM_VAMPIRE ||
                                u.umonnum == PM_VAMPIRE_LORD));
            boolean nightchild = (Upolyd &&
                                  (u.umonnum == PM_WOLF ||
                                   u.umonnum == PM_WINTER_WOLF ||
                                   u.umonnum == PM_WINTER_WOLF_CUB));
            const char *racenoun = (u.ufemale &&
                                    urace.individual.f) ? urace.
                individual.f : (urace.individual.m) ? urace.individual.
                m : urace.noun;

            if (mtmp->mtame) {
                if (kindred)
                    verbl_msg = msgprintf("Good %s to you Master%s",
                                          isnight ? "evening" : "day",
                                          isnight ? "!" :
                                          ".  Why do we not rest?");
                else
                    verbl_msg = msgcat(
                        nightchild ? "Child of the night, " : "",
                        midnight()? "I can stand this craving no longer!" :
                        isnight ?
                        "I beg you, help me satisfy this growing craving!" :
                        "I find myself growing a little weary.");
            } else if (mtmp->mpeaceful) {
                if (kindred && isnight)
                    verbl_msg = msgprintf("Good feeding %s!",
                                          u.ufemale ? "sister" : "brother");
                else if (nightchild && isnight)
                    verbl_msg = "How nice to hear you, child of the night!";
                else
                    verbl_msg = "I only drink... potions.";
            } else {
                int vampindex;

                static const char *const vampmsg[] = {
                    /* These first two (0 and 1) are specially handled below */
                    "I vant to suck your %s!",
                    "I vill come after %s without regret!",
                    /* other famous vampire quotes can follow here if desired */
                };
                if (kindred)
                    verbl_msg =
                        "This is my hunting ground that you dare to prowl!";
                else if (youmonst.data == &mons[PM_SILVER_DRAGON] ||
                         youmonst.data == &mons[PM_BABY_SILVER_DRAGON]) {
                    /* Silver dragons are silver in color, not made of silver */
                    verbl_msg = msgprintf(
                        "%s! Your silver sheen does not frighten me!",
                        youmonst.data ==
                        &mons[PM_SILVER_DRAGON] ? "Fool" : "Young Fool");
                } else {
                    vampindex = rn2(SIZE(vampmsg));
                    if (vampindex == 0) {
                        verbl_msg = msgprintf(
                            vampmsg[vampindex], body_part(BLOOD));
                    } else if (vampindex == 1) {
                        verbl_msg = msgprintf(
                            vampmsg[vampindex],
                            Upolyd ? an(mons[u.umonnum].mname) : an(racenoun));
                    } else
                        verbl_msg = vampmsg[vampindex];
                }
            }
        }
        break;
    case MS_WERE:
        if (flags.moonphase == FULL_MOON && (night() ^ !rn2(13))) {
            pline("%s throws back %s head and lets out a blood curdling %s!",
                  Monnam(mtmp), mhis(mtmp),
                  ptr == &mons[PM_HUMAN_WERERAT] ? "shriek" : "howl");
            wake_nearto(mtmp->mx, mtmp->my, 11 * 11);
        } else
            pline_msg =
                "whispers inaudibly.  All you can make out is \"moon\".";
        break;
    case MS_BARK:
        if (flags.moonphase == FULL_MOON && night()) {
            pline_msg = "howls.";
        } else if (mtmp->mpeaceful) {
            if (mtmp->mtame &&
                (mtmp->mconf || mtmp->mflee || mtmp->mtrapped ||
                 moves > EDOG(mtmp)->hungrytime || mtmp->mtame < 5))
                pline_msg = "whines.";
            else if (mtmp->mtame && EDOG(mtmp)->hungrytime > moves + 1000)
                pline_msg = "yips.";
            else {
                if (mtmp->data == &mons[PM_FOX])
                    pline_msg = whatthefoxsays();
                else if (mtmp->data != &mons[PM_DINGO])  /* dingos do not
                                                           actually bark */
                    pline_msg = "barks.";
            }
        } else {
            if (mtmp->data == &mons[PM_FOX])
                pline_msg = whatthefoxsays();
            else
                pline_msg = "growls.";
        }
        break;
    case MS_MEW:
        if (mtmp->mtame) {
            if (mtmp->mconf || mtmp->mflee || mtmp->mtrapped || mtmp->mtame < 5)
                pline_msg = "yowls.";
            else if (moves > EDOG(mtmp)->hungrytime)
                pline_msg = "meows.";
            else if (EDOG(mtmp)->hungrytime > moves + 1000)
                pline_msg = "purrs.";
            else
                pline_msg = "mews.";
            break;
        }       /* else FALLTHRU */
    case MS_GROWL:
        pline_msg = mtmp->mpeaceful ? "snarls." : "growls!";
        break;
    case MS_ROAR:
        pline_msg = mtmp->mpeaceful ? "snarls." : "roars!";
        break;
    case MS_SQEEK:
        pline_msg = "squeaks.";
        break;
    case MS_SQAWK:
        if (ptr == &mons[PM_RAVEN] && !mtmp->mpeaceful)
            verbl_msg = "Nevermore!";
        else
            pline_msg = "squawks.";
        break;
    case MS_HISS:
        if (!mtmp->mpeaceful)
            pline_msg = "hisses!";
        else
            return 0;   /* no sound */
        break;
    case MS_BUZZ:
        pline_msg = mtmp->mpeaceful ? "drones." : "buzzes angrily.";
        break;
    case MS_GRUNT:
        pline_msg = "grunts.";
        break;
    case MS_NEIGH:
        if (mtmp->mtame < 5)
            pline_msg = "neighs.";
        else if (moves > EDOG(mtmp)->hungrytime)
            pline_msg = "whinnies.";
        else
            pline_msg = "whickers.";
        break;
    case MS_WAIL:
        pline_msg = "wails mournfully.";
        break;
    case MS_GURGLE:
        pline_msg = "gurgles.";
        break;
    case MS_BURBLE:
        pline_msg = "burbles.";
        break;
    case MS_SHRIEK:
        pline_msg = "shrieks.";
        aggravate();
        break;
    case MS_IMITATE:
        pline_msg = "imitates you.";
        break;
    case MS_BONES:
        pline("%s rattles noisily.", Monnam(mtmp));
        pline("You freeze for a moment.");
        helpless(2, hr_afraid, "scared by rattling", NULL);
        break;
    case MS_LAUGH:
        {
            static const char *const laugh_msg[4] = {
                "giggles.", "chuckles.", "snickers.", "laughs.",
            };
            pline_msg = laugh_msg[rn2(4)];
        }
        break;
    case MS_MUMBLE:
        pline_msg = "mumbles incomprehensibly.";
        break;
    case MS_WISHGIVER:
        if (mtmp->mtame) {
            verbl_msg = "Sorry, I'm all out of wishes.";
        } else if (mtmp->mpeaceful) {
            if (ptr == &mons[PM_WATER_DEMON])
                pline_msg = "gurgles.";
            else
                verbl_msg = "I'm free!";
        } else
            verbl_msg = "This will teach you not to disturb me!";
        break;
    case MS_BOAST:     /* giants */
        if (!mtmp->mpeaceful) {
            switch (rn2(4)) {
            case 0:
                pline("%s boasts about %s gem collection.", Monnam(mtmp),
                      mhis(mtmp));
                break;
            case 1:
                pline_msg = "complains about a diet of mutton.";
                break;
            default:
                pline_msg = "shouts \"Fee Fie Foe Foo!\" and guffaws.";
                wake_nearto(mtmp->mx, mtmp->my, 7 * 7);
                break;
            }
            break;
        }
        /* else FALLTHRU */
    case MS_HUMANOID:
        if (!mtmp->mpeaceful) {
            if (In_endgame(&u.uz) && is_mplayer(ptr)) {
                mplayer_talk(mtmp);
                break;
            } else
                return 0;       /* no sound */
        }
        /* Generic peaceful humanoid behaviour. */
        if (mtmp->mflee)
            pline_msg = "wants nothing to do with you.";
        else if (mtmp->mhp < mtmp->mhpmax / 4)
            pline_msg = "moans.";
        else if (mtmp->mconf || mtmp->mstun)
            verbl_msg = !rn2(3) ? "Huh?" : rn2(2) ? "What?" : "Eh?";
        else if (!mtmp->mcansee)
            verbl_msg = "I can't see!";
        else if (mtmp->mtrapped) {
            struct trap *t = t_at(level, mtmp->mx, mtmp->my);

            if (t)
                t->tseen = 1;
            verbl_msg = "I'm trapped!";
        } else if (mtmp->mhp < mtmp->mhpmax / 2)
            pline_msg = "asks for a potion of healing.";
        else if (mtmp->mtame && !mtmp->isminion &&
                 moves > EDOG(mtmp)->hungrytime)
            verbl_msg = "I'm hungry.";
        /* Specific monsters' interests */
        else if (is_elf(ptr))
            pline_msg = "curses orcs.";
        else if (is_dwarf(ptr))
            pline_msg = "talks about mining.";
        else if (likes_magic(ptr))
            pline_msg = "talks about spellcraft.";
        else if (ptr->mlet == S_CENTAUR)
            pline_msg = "discusses hunting.";
        else
            switch (monsndx(ptr)) {
            case PM_HOBBIT:
                pline_msg =
                    (mtmp->mhpmax - mtmp->mhp >=
                     10) ? "complains about unpleasant dungeon conditions." :
                    "asks you about the One Ring.";
                break;
            case PM_ARCHEOLOGIST:
                pline_msg =
                    "describes a recent article in \"Spelunker Today\" "
                    "magazine.";
                break;
            case PM_TOURIST:
                verbl_msg = "Aloha.";
                break;
            case PM_PRISONER:
                verbl_msg = "Thank you for freeing me!";
                break;
            default:
                pline_msg = "discusses dungeon exploration.";
                break;
            }
        break;
    case MS_SEDUCE:
        if (ptr->mlet != S_NYMPH && flags.seduce_enabled &&
            could_seduce(mtmp, &youmonst, NULL) == 1) {
            doseduce(mtmp);
            break;
        }

        switch ((poly_gender() != (int)mtmp->female) ? rn2(3) : 0) {
        case 2:
            verbl_msg = "Hello, sailor.";
            break;
        case 1:
            pline_msg = "comes on to you.";
            break;
        default:
            pline_msg = "cajoles you.";
        }
        break;
    case MS_ARREST:
        if (mtmp->mpeaceful)
            verbalize("Just the facts, %s.", u.ufemale ? "Ma'am" : "Sir");
        else {
            static const char *const arrest_msg[3] = {
                "Anything you say can be used against you.",
                "You're under arrest!",
                "Stop in the name of the Law!",
            };
            verbl_msg = arrest_msg[rn2(3)];
        }
        break;
    case MS_BRIBE:
        if (mtmp->mpeaceful && !mtmp->mtame) {
            demon_talk(mtmp);
            break;
        }
        /* fall through */
    case MS_CUSS:
        if (!mtmp->mpeaceful)
            cuss(mtmp);
        break;
    case MS_SPELL:
        /* deliberately vague, since it's not actually casting any spell */
        pline_msg = "seems to mutter a cantrip.";
        break;
    case MS_NURSE:
        if (uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)))
            verbl_msg = "Put that weapon away before you hurt someone!";
        else if (uarmc || (uarm && !uskin()) ||
                 uarmh || uarms || uarmg || uarmf)
            verbl_msg =
                Role_if(PM_HEALER) ?
                "Doc, I can't help you unless you cooperate." :
                "Please undress so I can examine you.";
        else if (uarmu)
            verbl_msg = "Take off your shirt, please.";
        else
            verbl_msg = "Relax, this won't hurt a bit.";
        break;
    case MS_GUARD:
        if (money_cnt(invent))
            verbl_msg = "Please drop that gold and follow me.";
        else
            verbl_msg = "Please follow me.";
        break;
    case MS_SOLDIER:
        {
            static const char *const soldier_foe_msg[3] = {
                "Resistance is useless!",
                "You're dog meat!",
                "Surrender!",
            }, *const soldier_pax_msg[3] = {
                "What lousy pay we're getting here!",
                "The food's not fit for Orcs!",
                "My feet hurt, I've been on them all day!",
            };
            verbl_msg = mtmp->mpeaceful ? soldier_pax_msg[rn2(3)]
                : soldier_foe_msg[rn2(3)];
        }
        break;
    case MS_RIDER:
        if (ptr == &mons[PM_DEATH] && !rn2(10))
            pline_msg = "is busy reading a copy of Sandman #8.";
        else
            verbl_msg = "Who do you think you are, War?";
        break;
    }

    if (pline_msg)
        pline("%s %s", Monnam(mtmp), pline_msg);
    else if (verbl_msg)
        verbalize("%s", verbl_msg);
    return 1;
}
Пример #3
0
/* called from check_special_room() when the player enters the temple room */
void
intemple(int roomno)
{
    struct monst *priest = findpriest((char)roomno);
    boolean tended = (priest != NULL);
    boolean sanctum, can_speak;
    xchar shrined;
    const char *msg1, *msg2;

    if (In_mines(&u.uz) && !historysearch("entered the Minetown temple", TRUE))
        historic_event(FALSE, TRUE, "entered the Minetown temple");

    if (!temple_occupied(u.urooms0)) {
        if (tended) {
            shrined = has_shrine(priest);
            sanctum = (priest->data == &mons[PM_HIGH_PRIEST] &&
                       (shrined & AM_SANCTUM));
            can_speak = (priest->mcanmove && !priest->msleeping &&
                         canhear());
            if (can_speak) {
                unsigned save_priest = priest->ispriest;

                /* don't reveal the altar's owner upon temple entry in the
                   endgame; for the Sanctum, the next message names Moloch so
                   suppress the "of Moloch" for him here too */
                if (sanctum && !Hallucination)
                    priest->ispriest = 0;
                pline("%s intones:",
                      canseemon(priest) ? Monnam(priest) : "A nearby voice");
                priest->ispriest = save_priest;
            }
            msg2 = 0;
            if (sanctum && CONST_EPRI(priest)->shralign == A_NONE) {
                if (priest->mpeaceful) {
                    msg1 = "Infidel, you have entered Moloch's Sanctum!";
                    msg2 = "Be gone!";
                    msethostility(priest, TRUE, TRUE);
                } else
                    msg1 = "You desecrate this place by your presence!";
            } else {
                msg1 = msgprintf("Pilgrim, you enter a %s place!",
                                 !shrined ? "desecrated" : "sacred");
            }
            if (can_speak) {
                verbalize("%s", msg1);
                if (msg2)
                    verbalize("%s", msg2);
            }
            if (!sanctum) {
                /* !tended -> !shrined */
                if (!shrined || !p_coaligned(priest) ||
                    u.ualign.record <= ALGN_SINNED)
                    pline("You have a%s forbidding feeling...",
                          (!shrined) ? "" : " strange");
                else
                    pline("You experience a strange sense of peace.");
            }
        } else {
            switch (rn2(3)) {
            case 0:
                pline("You have an eerie feeling...");
                break;
            case 1:
                pline("You feel like you are being watched.");
                break;
            default:
                pline("A shiver runs down your %s.", body_part(SPINE));
                break;
            }
            if (!rn2(5)) {
                struct monst *mtmp;

                if (!((mtmp = makemon(&mons[PM_GHOST], level, 
                                      u.ux, u.uy, NO_MM_FLAGS))))
                    return;
                if (!Blind || sensemon(mtmp))
                    pline("An enormous ghost appears next to you!");
                else
                    pline("You sense a presence close by!");
                msethostility(mtmp, TRUE, TRUE);
                if (flags.verbose)
                    pline("You are frightened to death, and unable to move.");
                helpless(3, hr_afraid, "frightened to death",
                         "You regain your composure.");
            }
        }
    }
}
Пример #4
0
int
study_book(struct obj *spellbook, const struct nh_cmd_arg *arg)
{
    int booktype = spellbook->otyp;
    boolean confused = (Confusion != 0);
    boolean too_hard = FALSE;

    if (u.uoccupation_progress[tos_book] && !confused &&
        spellbook == u.utracked[tos_book] &&
        /* handle the sequence: start reading, get interrupted, have book
           become erased somehow, resume reading it */
        booktype != SPE_BLANK_PAPER) {
        if (turnstate.continue_message)
            pline("You continue your efforts to memorize the spell.");
    } else {
        /* Restarting reading the book */

        /* KMH -- Simplified this code */
        if (booktype == SPE_BLANK_PAPER) {
            pline("This spellbook is all blank.");
            makeknown(booktype);
            return 1;
        }
        switch (objects[booktype].oc_level) {
        case 1:
        case 2:
            u.uoccupation_progress[tos_book] = -objects[booktype].oc_delay;
            break;
        case 3:
        case 4:
            u.uoccupation_progress[tos_book] =
                -(objects[booktype].oc_level - 1) * objects[booktype].oc_delay;
            break;
        case 5:
        case 6:
            u.uoccupation_progress[tos_book] =
                -objects[booktype].oc_level * objects[booktype].oc_delay;
            break;
        case 7:
            u.uoccupation_progress[tos_book] = -8 * objects[booktype].oc_delay;
            break;
        default:
            impossible("Unknown spellbook level %d, book %d;",
                       objects[booktype].oc_level, booktype);
            return 0;
        }

        /* Books are often wiser than their readers (Rus.) */
        spellbook->in_use = TRUE;
        if (!spellbook->blessed && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
            if (spellbook->cursed) {
                too_hard = TRUE;
            } else {
                /* uncursed - chance to fail */
                int read_ability =
                    ACURR(A_INT) + 4 + u.ulevel / 2 -
                    2 * objects[booktype].oc_level +
                    ((ublindf && ublindf->otyp == LENSES) ? 2 : 0);
                /* only wizards know if a spell is too difficult */
                if (Role_if(PM_WIZARD) && read_ability < 20 && !confused) {
                    const char *qbuf;

                    qbuf = msgprintf("This spellbook is %sdifficult to "
                                     "comprehend. Continue?",
                                     (read_ability < 12 ? "very " : ""));
                    if (yn(qbuf) != 'y') {
                        spellbook->in_use = FALSE;
                        return 1;
                    }
                }
                /* it's up to random luck now */
                if (rnd(20) > read_ability) {
                    too_hard = TRUE;
                }
            }
        }

        if (too_hard) {
            boolean gone = cursed_book(spellbook);

            helpless(-u.uoccupation_progress[tos_book], hr_paralyzed,
                     "frozen by a spellbook", NULL);
            u.uoccupation_progress[tos_book] = 0;
            if (gone || !rn2(3)) {
                if (!gone)
                    pline("The spellbook crumbles to dust!");
                if (!objects[spellbook->otyp].oc_name_known &&
                    !objects[spellbook->otyp].oc_uname)
                    docall(spellbook);
                useup(spellbook);
            } else
                spellbook->in_use = FALSE;
            return 1;
        } else if (confused) {
            if (!confused_book(spellbook)) {
                spellbook->in_use = FALSE;
            }
            helpless(-u.uoccupation_progress[tos_book], hr_busy,
                     "absorbed in a spellbook",
                     "You're finally able to put the book down.");
            u.uoccupation_progress[tos_book] = 0;
            u.utracked[tos_book] = 0;
            return 1;
        }
        spellbook->in_use = FALSE;

        pline("You begin to %s the runes.",
              spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" : "memorize");
    }

    u.utracked[tos_book] = spellbook;

    one_occupation_turn(learn, "studying", occ_book);

    return 1;
}
Пример #5
0
/* Handles one turn of book reading. Returns 1 if unfinished, 0 if finshed. */
static int
learn(void)
{
    int i;
    short booktype;
    boolean costly = TRUE;
    boolean already_known = FALSE;
    int first_unknown = MAXSPELL;
    int known_spells = 0;
    const char *splname;

    /* JDS: lenses give 50% faster reading; 33% smaller read time */
    if (u.uoccupation_progress[tos_book] &&
        ublindf && ublindf->otyp == LENSES && rn2(2))
        u.uoccupation_progress[tos_book]++;
    if (Confusion) {    /* became confused while learning */
        confused_book(u.utracked[tos_book]);
        u.utracked[tos_book] = 0;       /* no longer studying */
        helpless(-u.uoccupation_progress[tos_book], hr_busy,
                 "absorbed in a spellbook",
                 "You're finally able to put the book down.");
        u.uoccupation_progress[tos_book] = 0;
        return 0;
    }

    booktype = u.utracked[tos_book]->otyp;
    if (booktype == SPE_BOOK_OF_THE_DEAD) {
        deadbook(u.utracked[tos_book], FALSE);
        u.utracked[tos_book] = 0;
        u.uoccupation_progress[tos_book] = 0;
        return 0;
    }

    /* The book might get cursed while we're reading it. In this case,
       immediately stop reading it, cancel progress, and apply a few turns of
       helplessness. (3.4.3 applies negative spellbook effects but lets you
       memorize the spell anyway; this makes no sense. It destroys the spellbook
       on the "contact poison" result, which makes even less sense.) */
    if (u.utracked[tos_book]->cursed) {
        pline("This book isn't making sense any more.");
        helpless(rn1(5,5), hr_busy,
                 "making sense of a spellbook",
                 "You give up trying to make sense of the spellbook.");
        u.uoccupation_progress[tos_book] = 0;
        u.utracked[tos_book] = 0;
        return 0;
    }

    if (++u.uoccupation_progress[tos_book] < 0)
        return 1;       /* still busy */

    if (ACURR(A_WIS) < 12)
        exercise(A_WIS, TRUE); /* you're studying. */

    splname = msgprintf(objects[booktype].oc_name_known ?
                        "\"%s\"" : "the \"%s\" spell",
                        OBJ_NAME(objects[booktype]));
    for (i = 0; i < MAXSPELL; i++) {
        if (spellid(i) == booktype) {
            already_known = TRUE;
            if (u.utracked[tos_book]->spestudied > MAX_SPELL_STUDY) {
                pline("This spellbook is too faint to be read any more.");
                u.utracked[tos_book]->otyp = booktype = SPE_BLANK_PAPER;
            } else if (spellknow(i) <= 1000) {
                pline("Your knowledge of %s is keener.", splname);
                incrnknow(i);
                u.utracked[tos_book]->spestudied++;
                if (ACURR(A_WIS) < 12)
                    exercise(A_WIS, TRUE); /* extra study */
            } else {    /* 1000 < spellknow(i) <= MAX_SPELL_STUDY */
                pline("You know %s quite well already.", splname);
                if (yn("Do you want to read the book anyway?") == 'y') {
                    pline("You refresh your knowledge of %s.", splname);
                    incrnknow(i);
                    u.utracked[tos_book]->spestudied++;
                } else
                    costly = FALSE;
            }
            /* make book become known even when spell is already known, in case
               amnesia made you forget the book */
            makeknown((int)booktype);
            break;
        } else if (spellid(i) == NO_SPELL &&
                   (i < first_unknown ||
                    i == spellno_from_let(objects[booktype].oc_defletter)))
            first_unknown = i;
        else
            known_spells++;
    }

    if (first_unknown == MAXSPELL && !already_known)
        panic("Too many spells memorized!");

    if (!already_known) {
        spl_book[first_unknown].sp_id = booktype;
        spl_book[first_unknown].sp_lev = objects[booktype].oc_level;
        incrnknow(first_unknown);
        u.utracked[tos_book]->spestudied++;
        pline(known_spells > 0 ? "You add %s to your repertoire." :
              "You learn %s.", splname);
        makeknown((int)booktype);
    }

    if (costly)
        check_unpaid(u.utracked[tos_book]);
    u.utracked[tos_book] = 0;
    return 0;
}
Пример #6
0
/* Note: I had to choose one of three possible kinds of "type" when writing
 * this function: a wand type (like in zap.c), an adtyp, or an object type.
 * Wand types get complex because they must be converted to adtyps for
 * determining such things as fire resistance.  Adtyps get complex in that
 * they don't supply enough information--was it a player or a monster that
 * did it, and with a wand, spell, or breath weapon?  Object types share both
 * these disadvantages....
 *
 * The descr argument should be used to describe the explosion. It should be
 * a string suitable for use with an().
 * raylevel is used for explosions caused by skilled wand usage (0=no wand)
 */
void
explode(int x, int y, int type, /* the same as in zap.c */
        int dam, char olet, int expltype, const char *descr, int raylevel)
{
    int i, j, k, damu = dam;
    boolean visible, any_shield, resist_death;
    resist_death = FALSE;
    int uhurt = 0;      /* 0=unhurt, 1=items damaged, 2=you and items damaged */
    const char *str;
    const char *dispbuf = "";   /* lint suppression; I think the code's OK */
    boolean expl_needs_the = TRUE;
    int idamres, idamnonres;
    struct monst *mtmp;
    uchar adtyp;
    int explmask[3][3];

    /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
    boolean shopdamage = FALSE;

#if 0
    /* Damage reduction from wand explosions */
    if (olet == WAND_CLASS)     /* retributive strike */
        switch (Role_switch) {
        case PM_PRIEST:
        case PM_MONK:
        case PM_WIZARD:
            damu /= 5;
            break;
        case PM_HEALER:
        case PM_KNIGHT:
            damu /= 2;
            break;
        default:
            break;
        }
#endif
    if (olet == MON_EXPLODE) {
        str = descr;
        adtyp = AD_PHYS;
        if (Hallucination) {
            int name = rndmonidx();

            dispbuf = msgcat(s_suffix(monnam_for_index(name)), " explosion");
            expl_needs_the = !monnam_is_pname(name);
        } else {
            dispbuf = str;
        }
    } else {
        int whattype = abs(type) % 10;

        adtyp = whattype + 1;
        boolean done = FALSE, hallu = Hallucination;

        if (hallu) {
            do {
                whattype = rn2(8);
            } while (whattype == 3);
        }
tryagain:
        switch (whattype) {
        case 0:
            str = "magical blast";
            break;
        case 1:
            str =
                olet == BURNING_OIL ? "burning oil" : olet ==
                SCROLL_CLASS ? "tower of flame" : "fireball";
            break;
        case 2:
            str = "ball of cold";
            break;
        case 3:
            str = "sleeping gas";
            break;
        case 4:
            str = (olet == WAND_CLASS) ? "death field" : "disintegration field";
            break;
        case 5:
            str = "ball of lightning";
            break;
        case 6:
            str = "poison gas cloud";
            break;
        case 7:
            str = "splash of acid";
            break;
        default:
            impossible("explosion base type %d?", type);
            return;
        }
        if (!done) {
            dispbuf = str;
            done = TRUE;
            if (hallu) {
                whattype = adtyp - 1;
                goto tryagain;
            }
        }
    }

    any_shield = visible = FALSE;
    for (i = 0; i < 3; i++)
        for (j = 0; j < 3; j++) {
            if (!isok(i + x - 1, j + y - 1)) {
                explmask[i][j] = 2;
                continue;
            } else
                explmask[i][j] = 0;

            if (i + x - 1 == u.ux && j + y - 1 == u.uy) {
                switch (adtyp) {
                case AD_PHYS:
                    explmask[i][j] = 0;
                    break;
                case AD_MAGM:
                    explmask[i][j] = !!(raylevel >= P_EXPERT || Antimagic);
                    break;
                case AD_FIRE:
                    explmask[i][j] = !!Fire_resistance;
                    break;
                case AD_COLD:
                    explmask[i][j] = !!Cold_resistance;
                    break;
                case AD_SLEE:
                    explmask[i][j] = !!Sleep_resistance;
                    break;
                case AD_DISN:
                    if (raylevel == P_UNSKILLED && Drain_resistance)
                        resist_death = TRUE;
                    /* why MR doesn't resist general deathfields is beyond me, but... */
                    if (nonliving(youmonst.data) ||
                            is_demon(youmonst.data))
                        resist_death = TRUE;
                    if (raylevel && Antimagic)
                        resist_death = TRUE;
                    if (raylevel >= P_EXPERT && !Drain_resistance)
                        resist_death = FALSE;
                    explmask[i][j] =
                        (olet == WAND_CLASS) ? !!resist_death :
                        !!Disint_resistance;
                    break;
                case AD_ELEC:
                    explmask[i][j] = !!Shock_resistance;
                    break;
                case AD_DRST:
                    explmask[i][j] = !!Poison_resistance;
                    break;
                case AD_ACID:
                    explmask[i][j] = !!Acid_resistance;
                    break;
                default:
                    impossible("explosion type %d?", adtyp);
                    break;
                }
            }
            /* can be both you and mtmp if you're swallowed */
            mtmp = m_at(level, i + x - 1, j + y - 1);
            if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy)
                mtmp = u.usteed;
            if (mtmp) {
                if (mtmp->mhp < 1)
                    explmask[i][j] = 2;
                else
                    switch (adtyp) {
                    case AD_PHYS:
                        break;
                    case AD_MAGM:
                        explmask[i][j] |= (raylevel >= 4 || resists_magm(mtmp));
                        break;
                    case AD_FIRE:
                        explmask[i][j] |= resists_fire(mtmp);
                        break;
                    case AD_COLD:
                        explmask[i][j] |= resists_cold(mtmp);
                        break;
                    case AD_SLEE:
                        explmask[i][j] |= resists_sleep(mtmp);
                    case AD_DISN:
                        if (raylevel == P_UNSKILLED && resists_drli(mtmp))
                            resist_death = TRUE;
                        if (nonliving(mtmp->data) ||
                                is_demon(mtmp->data))
                            resist_death = TRUE;
                        if (raylevel && resists_magm(mtmp))
                            resist_death = TRUE;
                        if (raylevel >= P_EXPERT && !resists_drli(mtmp))
                            resist_death = FALSE;
                        explmask[i][j] |=
                            (olet == WAND_CLASS) ? resist_death :
                            resists_disint(mtmp);
                        break;
                    case AD_ELEC:
                        explmask[i][j] |= resists_elec(mtmp);
                        break;
                    case AD_DRST:
                        explmask[i][j] |= resists_poison(mtmp);
                        break;
                    case AD_ACID:
                        explmask[i][j] |= resists_acid(mtmp);
                        break;
                    default:
                        impossible("explosion type %d?", adtyp);
                        break;
                    }
            }
            reveal_monster_at(i + x - 1, j + y - 1, TRUE);

            if (cansee(i + x - 1, j + y - 1))
                visible = TRUE;
            if (explmask[i][j] == 1)
                any_shield = TRUE;
        }

    if (visible) {
        struct tmp_sym *tsym = tmpsym_init(DISP_BEAM, 0);

        /* Start the explosion */
        for (i = 0; i < 3; i++)
            for (j = 0; j < 3; j++) {
                if (explmask[i][j] == 2)
                    continue;
                tmpsym_change(tsym, dbuf_explosion(expltype, explosion[i][j]));
                tmpsym_at(tsym, i + x - 1, j + y - 1);
            }
        flush_screen(); /* will flush screen and output */

        if (any_shield && flags.sparkle) {      /* simulate shield effect */
            for (k = 0; k < SHIELD_COUNT; k++) {
                for (i = 0; i < 3; i++)
                    for (j = 0; j < 3; j++) {
                        if (explmask[i][j] == 1)
                            /*
                             * Bypass tmpsym_at() and send the shield glyphs
                             * directly to the buffered screen.  tmpsym_at()
                             * will clean up the location for us later.
                             */
                            dbuf_set_effect(i + x - 1, j + y - 1,
                                            dbuf_effect(E_MISC,
                                                        shield_static[k]));
                    }
                flush_screen(); /* will flush screen and output */
                win_delay_output();
            }

            /* Cover last shield glyph with blast symbol. */
            for (i = 0; i < 3; i++)
                for (j = 0; j < 3; j++) {
                    if (explmask[i][j] == 1)
                        dbuf_set_effect(i + x - 1, j + y - 1,
                                        dbuf_explosion(expltype,
                                                       explosion[i][j]));
                }

        } else {        /* delay a little bit. */
            win_delay_output();
            win_delay_output();
        }

        tmpsym_end(tsym);       /* clear the explosion */
    } else {
        if (olet == MON_EXPLODE) {
            str = "explosion";
        }
        You_hear("a blast.");
    }

    if (dam)
        for (i = 0; i < 3; i++)
            for (j = 0; j < 3; j++) {
                if (explmask[i][j] == 2)
                    continue;
                if (i + x - 1 == u.ux && j + y - 1 == u.uy)
                    uhurt = (explmask[i][j] == 1) ? 1 : 2;
                idamres = idamnonres = 0;
                if (type >= 0)
                    zap_over_floor((xchar) (i + x - 1), (xchar) (j + y - 1),
                                   type, &shopdamage);

                mtmp = m_at(level, i + x - 1, j + y - 1);
                if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy)
                    mtmp = u.usteed;
                if (!mtmp)
                    continue;
                if (Engulfed && mtmp == u.ustuck) {
                    if (is_animal(u.ustuck->data))
                        pline("%s gets %s!", Monnam(u.ustuck),
                              (adtyp == AD_FIRE) ? "heartburn" :
                              (adtyp == AD_COLD) ? "chilly" :
                              (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ?
                                                    "irradiated by pure energy"
                                                    : "perforated") :
                              (adtyp == AD_ELEC) ? "shocked" :
                              (adtyp == AD_DRST) ? "poisoned" :
                              (adtyp == AD_ACID) ? "an upset stomach" :
                              "fried");
                    else
                        pline("%s gets slightly %s!", Monnam(u.ustuck),
                              (adtyp == AD_FIRE) ? "toasted" :
                              (adtyp == AD_COLD) ? "chilly" :
                              (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ?
                                                    "overwhelmed by pure energy"
                                                    : "perforated") :
                              (adtyp == AD_ELEC) ? "shocked" :
                              (adtyp == AD_DRST) ? "intoxicated" :
                              (adtyp == AD_ACID) ? "burned" : "fried");
                } else if (cansee(i + x - 1, j + y - 1)) {
                    if (mtmp->m_ap_type) seemimic(mtmp);
                    pline("%s is caught in %s%s!", Monnam(mtmp),
                          expl_needs_the ? "the " : "", dispbuf);
                }

                idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int)adtyp);
                idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int)adtyp);
                idamnonres += destroy_mitem(mtmp, POTION_CLASS, (int)adtyp);
                idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int)adtyp);
                idamnonres += destroy_mitem(mtmp, RING_CLASS, (int)adtyp);

                if (explmask[i][j] == 1) {
                    golemeffects(mtmp, (int)adtyp, dam + idamres);
                    mtmp->mhp -= idamnonres;
                } else {
                    /* call resist with 0 and do damage manually so 1) we can
                       get out the message before doing the damage, and 2) we
                       can call mondied, not killed, if it's not your blast */
                    int mdam = dam;

                    if (resist(mtmp, olet, 0, FALSE)) {
                        if (cansee(i + x - 1, j + y - 1))
                            pline("%s resists %s%s!", Monnam(mtmp),
                                  expl_needs_the ? "the " : "", dispbuf);
                        mdam = dam / 2;
                    }
                    if (mtmp == u.ustuck)
                        mdam *= 2;
                    if (resists_cold(mtmp) && adtyp == AD_FIRE)
                        mdam *= 2;
                    else if (resists_fire(mtmp) && adtyp == AD_COLD)
                        mdam *= 2;
                    if (adtyp == AD_MAGM && raylevel >= P_EXPERT && resists_magm(mtmp))
                        mdam = (mdam + 1) / 2;
                    if (adtyp == AD_SLEE && raylevel) {
                        sleep_monst(mtmp, mdam, WAND_CLASS);
                        mdam = 0;
                    }
                    if (adtyp == AD_DISN && raylevel) {
                        if (nonliving(mtmp->data) ||
                                is_demon(mtmp->data) ||
                                resists_magm(mtmp) ||
                                raylevel == P_UNSKILLED) {
                            /* monster is deathresistant or raylevel==unskilled,
                               since monster apparently failed to resist earlier,
                               monster must be vulnerable to drli */
                            /* FIXME: make a generic losexp() for monsters */
                            mdam = dice(2, 6);
                            if (cansee(i + x - 1, j + y - 1))
                                pline("%s suddenly seems weaker!", Monnam(mtmp));
                            mtmp->mhpmax -= mdam;
                            if (mtmp->m_lev == 0)
                                mdam = mtmp->mhp;
                            else
                                mtmp->m_lev--;
                        } else
                            mdam = mtmp->mhp; /* instadeath */
                    }
                    mtmp->mhp -= mdam;
                    mtmp->mhp -= (idamres + idamnonres);
                }
                if (mtmp->mhp <= 0) {
                    /* KMH -- Don't blame the player for pets killing gas
                       spores */
                    if (!flags.mon_moving)
                        killed(mtmp);
                    else
                        monkilled(mtmp, "", (int)adtyp);
                } else if (!flags.mon_moving)
                    setmangry(mtmp);
            }

    /* Do your injury last */
    if (uhurt) {
        if ((type >= 0 || adtyp == AD_PHYS) &&  /* gas spores */
                flags.verbose && olet != SCROLL_CLASS)
            pline("You are caught in %s%s!", expl_needs_the ? "the " : "",
                  dispbuf);
        /* do property damage first, in case we end up leaving bones */
        if (adtyp == AD_FIRE)
            burn_away_slime();
        if (u.uinvulnerable) {
            damu = 0;
            pline("You are unharmed!");
        } else if (Half_physical_damage && adtyp == AD_PHYS)
            damu = (damu + 1) / 2;
        else if (raylevel) {
            if (adtyp == AD_MAGM && Antimagic)
                damu = (damu + 1) / 2;
            if (adtyp == AD_SLEE) {
                helpless(damu, hr_asleep, "sleeping", NULL);
                damu = 0;
            }
            if (adtyp == AD_DISN) {
                if (nonliving(youmonst.data) ||
                        is_demon(youmonst.data) ||
                        Antimagic ||
                        raylevel == P_UNSKILLED) {
                    losexp("drained by a death field",FALSE);
                    damu = 0;
                } else {
                    done(DIED, "killed by a death field");
                    damu = 0; /* lifesaved */
                }
            }
        }
        if (adtyp == AD_FIRE) {
            burnarmor(&youmonst);
            set_candles_afire();
        }
        destroy_item(SCROLL_CLASS, (int)adtyp);
        destroy_item(SPBOOK_CLASS, (int)adtyp);
        destroy_item(POTION_CLASS, (int)adtyp);
        destroy_item(RING_CLASS, (int)adtyp);
        destroy_item(WAND_CLASS, (int)adtyp);

        ugolemeffects((int)adtyp, damu);
        if (uhurt == 2) {
            if (Upolyd)
                u.mh -= damu;
            else
                u.uhp -= damu;
        }

        if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
            int death = adtyp == AD_FIRE ? BURNING : DIED;
            const char *killer;

            if (olet == MON_EXPLODE) {
                killer = killer_msg(death, an(str));
            } else if (type >= 0 && olet != SCROLL_CLASS) {
                /* check whether or not we were the source of the explosion */
                if (!flags.mon_moving)
                    killer = msgprintf("caught %sself in %s own %s", uhim(),
                                       uhis(), str);
                else
                    killer = msgprintf("killed by a %s", str);
            } else if (!strcmp(str, "burning oil")) {
                /* This manual check hack really sucks */
                killer = killer_msg(death, str);
            } else {
                killer = killer_msg(death, an(str));
            }
            /* Known BUG: BURNING suppresses corpse in bones data, but done
               does not handle killer reason correctly */
            if (Upolyd) {
                rehumanize(death, killer);
            } else {
                done(death, killer);
            }
        }
        exercise(A_STR, FALSE);
    }

    if (shopdamage) {
        pay_for_damage(adtyp == AD_FIRE ? "burn away" : adtyp ==
                       AD_COLD ? "shatter" : adtyp ==
                       AD_DISN ? "disintegrate" : "destroy", FALSE);
    }

    /* explosions are noisy */
    i = dam * dam;
    if (i < 50)
        i = 50; /* in case random damage is very small */
    wake_nearto(x, y, i);
}
Пример #7
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;
}