/* NetHack 3.6	detect.c	$NHDT-Date: 1446369464 2015/11/01 09:17:44 $  $NHDT-Branch: master $:$NHDT-Revision: 1.61 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

/*
 * Detection routines, including crystal ball, magic mapping, and search
 * command.
 */

/* JNetHack Copyright */
/* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
/* JNetHack may be freely redistributed.  See license for details. */

#include "hack.h"
#include "artifact.h"

extern boolean known; /* from read.c */

STATIC_DCL void FDECL(do_dknown_of, (struct obj *));
STATIC_DCL boolean FDECL(check_map_spot, (int, int, CHAR_P, unsigned));
STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P, unsigned));
STATIC_DCL void FDECL(sense_trap, (struct trap *, XCHAR_P, XCHAR_P, int));
STATIC_DCL int FDECL(detect_obj_traps, (struct obj *, BOOLEAN_P, int));
STATIC_DCL void FDECL(show_map_spot, (int, int));
STATIC_PTR void FDECL(findone, (int, int, genericptr_t));
STATIC_PTR void FDECL(openone, (int, int, genericptr_t));

/* Recursively search obj for an object in class oclass and return 1st found
 */
struct obj *
o_in(obj, oclass)
struct obj *obj;
char oclass;
{
    register struct obj *otmp;
    struct obj *temp;

    if (obj->oclass == oclass)
        return obj;

    if (Has_contents(obj)) {
        for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
            if (otmp->oclass == oclass)
                return otmp;
            else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)))
                return temp;
    }
    return (struct obj *) 0;
}

/* Recursively search obj for an object made of specified material.
 * Return first found.
 */
struct obj *
o_material(obj, material)
struct obj *obj;
unsigned material;
{
    register struct obj *otmp;
    struct obj *temp;

    if (objects[obj->otyp].oc_material == material)
        return obj;

    if (Has_contents(obj)) {
        for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
            if (objects[otmp->otyp].oc_material == material)
                return otmp;
            else if (Has_contents(otmp)
                     && (temp = o_material(otmp, material)))
                return temp;
    }
    return (struct obj *) 0;
}

STATIC_OVL void
do_dknown_of(obj)
struct obj *obj;
{
    struct obj *otmp;

    obj->dknown = 1;
    if (Has_contents(obj)) {
        for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
            do_dknown_of(otmp);
    }
}

/* Check whether the location has an outdated object displayed on it. */
STATIC_OVL boolean
check_map_spot(x, y, oclass, material)
int x, y;
char oclass;
unsigned material;
{
    int glyph;
    register struct obj *otmp;
    register struct monst *mtmp;

    glyph = glyph_at(x, y);
    if (glyph_is_object(glyph)) {
        /* there's some object shown here */
        if (oclass == ALL_CLASSES) {
            return (boolean) !(level.objects[x][y] /* stale if nothing here */
                               || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent));
        } else {
            if (material
                && objects[glyph_to_obj(glyph)].oc_material == material) {
                /* object shown here is of interest because material matches */
                for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
                    if (o_material(otmp, GOLD))
                        return FALSE;
                /* didn't find it; perhaps a monster is carrying it */
                if ((mtmp = m_at(x, y)) != 0) {
                    for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
                        if (o_material(otmp, GOLD))
                            return FALSE;
                }
                /* detection indicates removal of this object from the map */
                return TRUE;
            }
            if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
                /* obj shown here is of interest because its class matches */
                for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
                    if (o_in(otmp, oclass))
                        return FALSE;
                /* didn't find it; perhaps a monster is carrying it */
                if ((mtmp = m_at(x, y)) != 0) {
                    for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
                        if (o_in(otmp, oclass))
                            return FALSE;
                }
                /* detection indicates removal of this object from the map */
                return TRUE;
            }
        }
    }
    return FALSE;
}

/*
 * When doing detection, remove stale data from the map display (corpses
 * rotted away, objects carried away by monsters, etc) so that it won't
 * reappear after the detection has completed.  Return true if noticeable
 * change occurs.
 */
STATIC_OVL boolean
clear_stale_map(oclass, material)
char oclass;
unsigned material;
{
    register int zx, zy;
    boolean change_made = FALSE;

    for (zx = 1; zx < COLNO; zx++)
        for (zy = 0; zy < ROWNO; zy++)
            if (check_map_spot(zx, zy, oclass, material)) {
                unmap_object(zx, zy);
                change_made = TRUE;
            }

    return change_made;
}

/* look for gold, on the floor or in monsters' possession */
int
gold_detect(sobj)
register struct obj *sobj;
{
    register struct obj *obj;
    register struct monst *mtmp;
    struct obj *temp;
    boolean stale;

    known = stale =
        clear_stale_map(COIN_CLASS, (unsigned) (sobj->blessed ? GOLD : 0));

    /* look for gold carried by monsters (might be in a container) */
    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
        if (DEADMONSTER(mtmp))
            continue; /* probably not needed in this case but... */
        if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
            known = TRUE;
            goto outgoldmap; /* skip further searching */
        } else
            for (obj = mtmp->minvent; obj; obj = obj->nobj)
                if (sobj->blessed && o_material(obj, GOLD)) {
                    known = TRUE;
                    goto outgoldmap;
                } else if (o_in(obj, COIN_CLASS)) {
                    known = TRUE;
                    goto outgoldmap; /* skip further searching */
                }
    }

    /* look for gold objects */
    for (obj = fobj; obj; obj = obj->nobj) {
        if (sobj->blessed && o_material(obj, GOLD)) {
            known = TRUE;
            if (obj->ox != u.ux || obj->oy != u.uy)
                goto outgoldmap;
        } else if (o_in(obj, COIN_CLASS)) {
            known = TRUE;
            if (obj->ox != u.ux || obj->oy != u.uy)
                goto outgoldmap;
        }
    }

    if (!known) {
        /* no gold found on floor or monster's inventory.
           adjust message if you have gold in your inventory */
        if (sobj) {
            char buf[BUFSZ];
            if (youmonst.data == &mons[PM_GOLD_GOLEM]) {
/*JP
                Sprintf(buf, "You feel like a million %s!", currency(2L));
*/
                Strcpy(buf, "あなたは金持ちになったように感じた！");
            } else if (hidden_gold() || money_cnt(invent))
                Strcpy(buf,
/*JP
                   "You feel worried about your future financial situation.");
*/
                   "あなたは将来の経済状況が心配になった．");
            else
/*JP
                Strcpy(buf, "You feel materially poor.");
*/
                Strcpy(buf, "あなたはひもじさを感じた．");
            strange_feeling(sobj, buf);
        }
        return 1;
    }
    /* only under me - no separate display required */
    if (stale)
        docrt();
/*JP
    You("notice some gold between your %s.", makeplural(body_part(FOOT)));
*/
    You("%sの間に金貨が落ちていることに気がついた．", body_part(FOOT));
    return 0;

outgoldmap:
    cls();

    iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
    u.uinwater = u.uburied = 0;
    /* Discover gold locations. */
    for (obj = fobj; obj; obj = obj->nobj) {
        if (sobj->blessed && (temp = o_material(obj, GOLD))) {
            if (temp != obj) {
                temp->ox = obj->ox;
                temp->oy = obj->oy;
            }
            map_object(temp, 1);
        } else if ((temp = o_in(obj, COIN_CLASS))) {
            if (temp != obj) {
                temp->ox = obj->ox;
                temp->oy = obj->oy;
            }
            map_object(temp, 1);
        }
    }
    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
        if (DEADMONSTER(mtmp))
            continue; /* probably overkill here */
        if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
            struct obj gold;
            gold = zeroobj; /* ensure oextra is cleared too */
            gold.otyp = GOLD_PIECE;
            gold.ox = mtmp->mx;
            gold.oy = mtmp->my;
            map_object(&gold, 1);
        } else
            for (obj = mtmp->minvent; obj; obj = obj->nobj)
                if (sobj->blessed && (temp = o_material(obj, GOLD))) {
                    temp->ox = mtmp->mx;
                    temp->oy = mtmp->my;
                    map_object(temp, 1);
                    break;
                } else if ((temp = o_in(obj, COIN_CLASS))) {
                    temp->ox = mtmp->mx;
                    temp->oy = mtmp->my;
                    map_object(temp, 1);
                    break;
                }
    }
    newsym(u.ux, u.uy);
    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
/*JP
    You_feel("very greedy, and sense gold!");
*/
    You("どん欲になったような気がした，そして金貨の位置を感知した！");
    exercise(A_WIS, TRUE);
    display_nhwindow(WIN_MAP, TRUE);
    docrt();
    if (Underwater)
        under_water(2);
    if (u.uburied)
        under_ground(2);
    return 0;
}

/* returns 1 if nothing was detected   */
/* returns 0 if something was detected */
int
food_detect(sobj)
register struct obj *sobj;
{
    register struct obj *obj;
    register struct monst *mtmp;
    register int ct = 0, ctu = 0;
    boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
    char oclass = confused ? POTION_CLASS : FOOD_CLASS;
/*JP
    const char *what = confused ? something : "food";
*/
    const char *what = confused ? "ハラヘリ" : "食べ物";

    stale = clear_stale_map(oclass, 0);

    for (obj = fobj; obj; obj = obj->nobj)
        if (o_in(obj, oclass)) {
            if (obj->ox == u.ux && obj->oy == u.uy)
                ctu++;
            else
                ct++;
        }
    for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) {
        /* no DEADMONSTER(mtmp) check needed since dmons never have inventory
         */
        for (obj = mtmp->minvent; obj; obj = obj->nobj)
            if (o_in(obj, oclass)) {
                ct++;
                break;
            }
    }

    if (!ct && !ctu) {
        known = stale && !confused;
        if (stale) {
            docrt();
/*JP
            You("sense a lack of %s nearby.", what);
*/
            You("%sが減っているのに気がついた．",what);
            if (sobj && sobj->blessed) {
                if (!u.uedibility)
/*JP
                    Your("%s starts to tingle.", body_part(NOSE));
*/
                    Your("%sがぴくっと動いた．", body_part(NOSE));
                u.uedibility = 1;
            }
        } else if (sobj) {
            char buf[BUFSZ];
#if 0 /*JP*/
            Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
                    (sobj->blessed && !u.uedibility)
                        ? " then starts to tingle"
                        : "");
#else
            Sprintf(buf, "あなたの%sがひくひくと動い%s．", body_part(NOSE),
                    (sobj->blessed && !u.uedibility)
                        ? "て，うずうずしだした"
                        : "た");
#endif
            if (sobj->blessed && !u.uedibility) {
                boolean savebeginner = flags.beginner;

                flags.beginner = FALSE; /* prevent non-delivery of message */
                strange_feeling(sobj, buf);
                flags.beginner = savebeginner;
                u.uedibility = 1;
            } else
                strange_feeling(sobj, buf);
        }
        return !stale;
    } else if (!ct) {
        known = TRUE;
/*JP
        You("%s %s nearby.", sobj ? "smell" : "sense", what);
*/
        You("近くで%s%s．", what, sobj ? "のにおいをかいだ" : "を感知した");
        if (sobj && sobj->blessed) {
            if (!u.uedibility)
/*JP
                pline("Your %s starts to tingle.", body_part(NOSE));
*/
                pline("あなたの%sがうずうずしだした．", body_part(NOSE));
            u.uedibility = 1;
        }
    } else {
        struct obj *temp;
        known = TRUE;
        cls();
        iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
        u.uinwater = u.uburied = 0;
        for (obj = fobj; obj; obj = obj->nobj)
            if ((temp = o_in(obj, oclass)) != 0) {
                if (temp != obj) {
                    temp->ox = obj->ox;
                    temp->oy = obj->oy;
                }
                map_object(temp, 1);
            }
        for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
            /* no DEADMONSTER(mtmp) check needed since dmons never have
             * inventory */
            for (obj = mtmp->minvent; obj; obj = obj->nobj)
                if ((temp = o_in(obj, oclass)) != 0) {
                    temp->ox = mtmp->mx;
                    temp->oy = mtmp->my;
                    map_object(temp, 1);
                    break; /* skip rest of this monster's inventory */
                }
        newsym(u.ux, u.uy);
        u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
        if (sobj) {
            if (sobj->blessed) {
#if 0 /*JP*/
                Your("%s %s to tingle and you smell %s.", body_part(NOSE),
                     u.uedibility ? "continues" : "starts", what);
#else
                Your("%sはうずうずし%s，%sの匂いを嗅ぎとった．", body_part(NOSE),
                     u.uedibility ? "続け" : "始め", what);
#endif
                u.uedibility = 1;
            } else
/*JP
                Your("%s tingles and you smell %s.", body_part(NOSE), what);
*/
                Your("%sはうずうずし，%sの匂いを嗅ぎとった．", body_part(NOSE), what);
        } else
/*JP
            You("sense %s.", what);
*/
            You("%sを感知した．", what);
        display_nhwindow(WIN_MAP, TRUE);
        exercise(A_WIS, TRUE);
        docrt();
        if (Underwater)
            under_water(2);
        if (u.uburied)
            under_ground(2);
    }
    return 0;
}

/*
 * Used for scrolls, potions, spells, and crystal balls.  Returns:
 *
 *      1 - nothing was detected
 *      0 - something was detected
 */
int
object_detect(detector, class)
struct obj *detector; /* object doing the detecting */
int class;            /* an object class, 0 for all */
{
    register int x, y;
    char stuff[BUFSZ];
    int is_cursed = (detector && detector->cursed);
    int do_dknown = (detector && (detector->oclass == POTION_CLASS
                                  || detector->oclass == SPBOOK_CLASS)
                     && detector->blessed);
    int ct = 0, ctu = 0;
    register struct obj *obj, *otmp = (struct obj *) 0;
    register struct monst *mtmp;
    int sym, boulder = 0;

    if (class < 0 || class >= MAXOCLASSES) {
        impossible("object_detect:  illegal class %d", class);
        class = 0;
    }

    /* Special boulder symbol check - does the class symbol happen
     * to match iflags.bouldersym which is a user-defined?
     * If so, that means we aren't sure what they really wanted to
     * detect. Rather than trump anything, show both possibilities.
     * We can exclude checking the buried obj chain for boulders below.
     */
    sym = class ? def_oc_syms[class].sym : 0;
    if (sym && iflags.bouldersym && sym == iflags.bouldersym)
        boulder = ROCK_CLASS;

    if (Hallucination || (Confusion && class == SCROLL_CLASS))
        Strcpy(stuff, something);
    else
/*JP
        Strcpy(stuff, class ? def_oc_syms[class].name : "objects");
*/
        Strcpy(stuff, class ? def_oc_syms[class].name : "物体");
    if (boulder && class != ROCK_CLASS)
/*JP
        Strcat(stuff, " and/or large stones");
*/
        Strcat(stuff, "と巨岩");

    if (do_dknown)
        for (obj = invent; obj; obj = obj->nobj)
            do_dknown_of(obj);

    for (obj = fobj; obj; obj = obj->nobj) {
        if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
            if (obj->ox == u.ux && obj->oy == u.uy)
                ctu++;
            else
                ct++;
        }
        if (do_dknown)
            do_dknown_of(obj);
    }

    for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
        if (!class || o_in(obj, class)) {
            if (obj->ox == u.ux && obj->oy == u.uy)
                ctu++;
            else
                ct++;
        }
        if (do_dknown)
            do_dknown_of(obj);
    }

    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
        if (DEADMONSTER(mtmp))
            continue;
        for (obj = mtmp->minvent; obj; obj = obj->nobj) {
            if ((!class && !boulder) || o_in(obj, class)
                || o_in(obj, boulder))
                ct++;
            if (do_dknown)
                do_dknown_of(obj);
        }
        if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT
             && (!class || class == objects[mtmp->mappearance].oc_class))
            || (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
            ct++;
            break;
        }
    }

    if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
        if (!ctu) {
            if (detector)
/*JP
                strange_feeling(detector, "You feel a lack of something.");
*/
                strange_feeling(detector, "あなたは何かが欠乏しているような気がした．");
            return 1;
        }

/*JP
        You("sense %s nearby.", stuff);
*/
        You("近くの%sを感知した．", stuff);
        return 0;
    }

    cls();

    iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
    u.uinwater = u.uburied = 0;
    /*
     *  Map all buried objects first.
     */
    for (obj = level.buriedobjlist; obj; obj = obj->nobj)
        if (!class || (otmp = o_in(obj, class))) {
            if (class) {
                if (otmp != obj) {
                    otmp->ox = obj->ox;
                    otmp->oy = obj->oy;
                }
                map_object(otmp, 1);
            } else
                map_object(obj, 1);
        }
    /*
     * If we are mapping all objects, map only the top object of a pile or
     * the first object in a monster's inventory.  Otherwise, go looking
     * for a matching object class and display the first one encountered
     * at each location.
     *
     * Objects on the floor override buried objects.
     */
    for (x = 1; x < COLNO; x++)
        for (y = 0; y < ROWNO; y++)
            for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
                if ((!class && !boulder) || (otmp = o_in(obj, class))
                    || (otmp = o_in(obj, boulder))) {
                    if (class || boulder) {
                        if (otmp != obj) {
                            otmp->ox = obj->ox;
                            otmp->oy = obj->oy;
                        }
                        map_object(otmp, 1);
                    } else
                        map_object(obj, 1);
                    break;
                }

    /* Objects in the monster's inventory override floor objects. */
    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
        if (DEADMONSTER(mtmp))
            continue;
        for (obj = mtmp->minvent; obj; obj = obj->nobj)
            if ((!class && !boulder) || (otmp = o_in(obj, class))
                || (otmp = o_in(obj, boulder))) {
                if (!class && !boulder)
                    otmp = obj;
                otmp->ox = mtmp->mx; /* at monster location */
                otmp->oy = mtmp->my;
                map_object(otmp, 1);
                break;
            }
        /* Allow a mimic to override the detected objects it is carrying. */
        if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT
            && (!class || class == objects[mtmp->mappearance].oc_class)) {
            struct obj temp;

            temp.oextra = (struct oextra *) 0;
            temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
            temp.ox = mtmp->mx;
            temp.oy = mtmp->my;
            temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
            map_object(&temp, 1);
        } else if (findgold(mtmp->minvent)
                   && (!class || class == COIN_CLASS)) {
            struct obj gold;
            gold = zeroobj; /* ensure oextra is cleared too */
            gold.otyp = GOLD_PIECE;
            gold.ox = mtmp->mx;
            gold.oy = mtmp->my;
            map_object(&gold, 1);
        }
    }

    newsym(u.ux, u.uy);
    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
/*JP
    You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
*/
    You("%s%s．", stuff, ct ? "を発見した" : "は何もないことがわかった" );
    display_nhwindow(WIN_MAP, TRUE);
    /*
     * What are we going to do when the hero does an object detect while blind
     * and the detected object covers a known pool?
     */
    docrt(); /* this will correctly reset vision */

    if (Underwater)
        under_water(2);
    if (u.uburied)
        under_ground(2);
    return 0;
}

/*
 * Used by: crystal balls, potions, fountains
 *
 * Returns 1 if nothing was detected.
 * Returns 0 if something was detected.
 */
int
monster_detect(otmp, mclass)
register struct obj *otmp; /* detecting object (if any) */
int mclass;                /* monster class, 0 for all */
{
    register struct monst *mtmp;
    int mcnt = 0;

    /* Note: This used to just check fmon for a non-zero value
     * but in versions since 3.3.0 fmon can test TRUE due to the
     * presence of dmons, so we have to find at least one
     * with positive hit-points to know for sure.
     */
    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
        if (!DEADMONSTER(mtmp)) {
            mcnt++;
            break;
        }

    if (!mcnt) {
        if (otmp)
            strange_feeling(otmp, Hallucination
/*JP
                                      ? "You get the heebie jeebies."
*/
                                      ? "あなたは金鳥の夏でキンチョーした．"
/*JP
                                      : "You feel threatened.");
*/
                                      : "あなたは恐怖でぞくっとした．");
        return 1;
    } else {
        boolean woken = FALSE;

        cls();
        for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
            if (DEADMONSTER(mtmp))
                continue;
            if (!mclass || mtmp->data->mlet == mclass
                || (mtmp->data == &mons[PM_LONG_WORM]
                    && mclass == S_WORM_TAIL))
                if (mtmp->mx > 0) {
                    if (mclass && def_monsyms[mclass].sym == ' ')
                        show_glyph(mtmp->mx, mtmp->my,
                                   detected_mon_to_glyph(mtmp));
                    else
                        show_glyph(mtmp->mx, mtmp->my,
                                   mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
                    /* don't be stingy - display entire worm */
                    if (mtmp->data == &mons[PM_LONG_WORM])
                        detect_wsegs(mtmp, 0);
                }
            if (otmp && otmp->cursed
                && (mtmp->msleeping || !mtmp->mcanmove)) {
                mtmp->msleeping = mtmp->mfrozen = 0;
                mtmp->mcanmove = 1;
                woken = TRUE;
            }
        }
        display_self();
/*JP
        You("sense the presence of monsters.");
*/
        You("怪物の存在を嗅ぎつけた．");
        if (woken)
/*JP
            pline("Monsters sense the presence of you.");
*/
            pline("怪物はあなたの存在を嗅ぎつけた．");
        display_nhwindow(WIN_MAP, TRUE);
        docrt();
        if (Underwater)
            under_water(2);
        if (u.uburied)
            under_ground(2);
    }
    return 0;
}

STATIC_OVL void
sense_trap(trap, x, y, src_cursed)
struct trap *trap;
xchar x, y;
int src_cursed;
{
    if (Hallucination || src_cursed) {
        struct obj obj; /* fake object */

        obj.oextra = (struct oextra *) 0;
        if (trap) {
            obj.ox = trap->tx;
            obj.oy = trap->ty;
        } else {
            obj.ox = x;
            obj.oy = y;
        }
        obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
        obj.corpsenm = random_monster(); /* if otyp == CORPSE */
        map_object(&obj, 1);
    } else if (trap) {
        map_trap(trap, 1);
        trap->tseen = 1;
    } else {
        struct trap temp_trap; /* fake trap */
        temp_trap.tx = x;
        temp_trap.ty = y;
        temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
        map_trap(&temp_trap, 1);
    }
}

#define OTRAP_NONE 0  /* nothing found */
#define OTRAP_HERE 1  /* found at hero's location */
#define OTRAP_THERE 2 /* found at any other location */

/* check a list of objects for chest traps; return 1 if found at <ux,uy>,
   2 if found at some other spot, 3 if both, 0 otherwise; optionally
   update the map to show where such traps were found */
STATIC_OVL int
detect_obj_traps(objlist, show_them, how)
struct obj *objlist;
boolean show_them;
int how; /* 1 for misleading map feedback */
{
    struct obj *otmp;
    xchar x, y;
    int result = OTRAP_NONE;

    for (otmp = objlist; otmp; otmp = otmp->nobj) {
        if (Is_box(otmp) && otmp->otrapped
            && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
            result |= (x == u.ux && y == u.uy) ? OTRAP_HERE : OTRAP_THERE;
            if (show_them)
                sense_trap((struct trap *) 0, x, y, how);
        }
        if (Has_contents(otmp))
            result |= detect_obj_traps(otmp->cobj, show_them, how);
    }
    return result;
}

/* the detections are pulled out so they can
 * also be used in the crystal ball routine
 * returns 1 if nothing was detected
 * returns 0 if something was detected
 */
int
trap_detect(sobj)
register struct obj *sobj;
/* sobj is null if crystal ball, *scroll if gold detection scroll */
{
    register struct trap *ttmp;
    struct monst *mon;
    int door, glyph, tr;
    int cursed_src = sobj && sobj->cursed;
    boolean found = FALSE;
    coord cc;

    /* floor/ceiling traps */
    for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
        if (ttmp->tx != u.ux || ttmp->ty != u.uy)
            goto outtrapmap;
        else
            found = TRUE;
    }
    /* chest traps (might be buried or carried) */
    if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) {
        if (tr & OTRAP_THERE)
            goto outtrapmap;
        else
            found = TRUE;
    }
    if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0))
        != OTRAP_NONE) {
        if (tr & OTRAP_THERE)
            goto outtrapmap;
        else
            found = TRUE;
    }
    for (mon = fmon; mon; mon = mon->nmon) {
        if (DEADMONSTER(mon))
            continue;
        if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) {
            if (tr & OTRAP_THERE)
                goto outtrapmap;
            else
                found = TRUE;
        }
    }
    if (detect_obj_traps(invent, FALSE, 0) != OTRAP_NONE)
        found = TRUE;
    /* door traps */
    for (door = 0; door < doorindex; door++) {
        cc = doors[door];
        if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
            if (cc.x != u.ux || cc.y != u.uy)
                goto outtrapmap;
            else
                found = TRUE;
        }
    }
    if (!found) {
        char buf[BUFSZ];

/*JP
        Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
*/
        Sprintf(buf, "あなたの%sのむずむずはおさまった．", makeplural(body_part(TOE)));
        strange_feeling(sobj, buf);
        return 1;
    }
    /* traps exist, but only under me - no separate display required */
/*JP
    Your("%s itch.", makeplural(body_part(TOE)));
*/
    Your("%sはむずむずした．", makeplural(body_part(TOE)));
    return 0;
outtrapmap:
    cls();

    iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
    u.uinwater = u.uburied = 0;

    /* show chest traps first, so that subsequent floor trap display
       will override if both types are present at the same location */
    (void) detect_obj_traps(fobj, TRUE, cursed_src);
    (void) detect_obj_traps(level.buriedobjlist, TRUE, cursed_src);
    for (mon = fmon; mon; mon = mon->nmon) {
        if (DEADMONSTER(mon))
            continue;
        (void) detect_obj_traps(mon->minvent, TRUE, cursed_src);
    }
    (void) detect_obj_traps(invent, TRUE, cursed_src);

    for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
        sense_trap(ttmp, 0, 0, cursed_src);

    for (door = 0; door < doorindex; door++) {
        cc = doors[door];
        if (levl[cc.x][cc.y].doormask & D_TRAPPED)
            sense_trap((struct trap *) 0, cc.x, cc.y, cursed_src);
    }

    /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
    glyph = glyph_at(u.ux, u.uy);
    if (!(glyph_is_trap(glyph) || glyph_is_object(glyph)))
        newsym(u.ux, u.uy);
    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;

/*JP
    You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
*/
    You("%s気持になった．", cursed_src ? "とてもどん欲な" : "だまされているような");
    /* wait for user to respond, then reset map display to normal */
    display_nhwindow(WIN_MAP, TRUE);
    docrt();
    if (Underwater)
        under_water(2);
    if (u.uburied)
        under_ground(2);
    return 0;
}

const char *
level_distance(where)
d_level *where;
{
    register schar ll = depth(&u.uz) - depth(where);
    register boolean indun = (u.uz.dnum == where->dnum);

    if (ll < 0) {
        if (ll < (-8 - rn2(3)))
            if (!indun)
/*JP
                return "far away";
*/
              return "はるか彼方に";
            else
/*JP
                return "far below";
*/
        return "はるか下方に";
        else if (ll < -1)
            if (!indun)
/*JP
                return "away below you";
*/
              return "ずっと下方に";
            else
/*JP
                return "below you";
*/
              return "下方に";
        else if (!indun)
/*JP
            return "in the distance";
*/
          return "遠くに";
        else
/*JP
            return "just below";
*/
          return "真下に";
    } else if (ll > 0) {
        if (ll > (8 + rn2(3)))
            if (!indun)
/*JP
                return "far away";
*/
              return "はるか彼方に";
            else
/*JP
                return "far above";
*/
              return "はるか上方に";
        else if (ll > 1)
            if (!indun)
/*JP
                return "away above you";
*/
              return "ずっと上方に";
            else
/*JP
                return "above you";
*/
              return "上方に";
        else if (!indun)
/*JP
            return "in the distance";
*/
          return "遠くに";
        else
/*JP
            return "just above";
*/
          return "真上に";
    } else if (!indun)
/*JP
        return "in the distance";
*/
      return "遠くに";
    else
/*JP
        return "near you";
*/
      return "近くに";
}

static const struct {
    const char *what;
    d_level *where;
} level_detects[] = {
/*JP
    { "Delphi", &oracle_level },
*/
    { "デルファイ", &oracle_level },
/*JP
    { "Medusa's lair", &medusa_level },
*/
    { "メデューサの住みか", &medusa_level },
/*JP
    { "a castle", &stronghold_level },
*/
    { "城", &stronghold_level },
/*JP
    { "the Wizard of Yendor's tower", &wiz1_level },
*/
    { "イェンダーの魔法使いの塔", &wiz1_level },
};

void
use_crystal_ball(optr)
struct obj **optr;
{
    char ch;
    int oops;
    struct obj *obj = *optr;

    if (Blind) {
/*JP
        pline("Too bad you can't see %s.", the(xname(obj)));
*/
        pline("なんてことだ．%sを見ることができない．", the(xname(obj)));
        return;
    }
    oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
    if (oops && (obj->spe > 0)) {
        switch (rnd(obj->oartifact ? 4 : 5)) {
        case 1:
/*JP
            pline("%s too much to comprehend!", Tobjnam(obj, "are"));
*/
            pline("%sを覗いたが何のことだかさっぱりわからなかった！", xname(obj));
            break;
        case 2:
/*JP
            pline("%s you!", Tobjnam(obj, "confuse"));
*/
            pline("%sを覗いてるとふらついてきた！", xname(obj));
            make_confused((HConfusion & TIMEOUT) + (long) rnd(100), FALSE);
            break;
        case 3:
            if (!resists_blnd(&youmonst)) {
/*JP
                pline("%s your vision!", Tobjnam(obj, "damage"));
*/
                pline("%sを覗いていると視覚がおかしくなってきた！", xname(obj));
                make_blinded((Blinded & TIMEOUT) + (long) rnd(100), FALSE);
                if (!Blind)
                    Your1(vision_clears);
            } else {
/*JP
                pline("%s your vision.", Tobjnam(obj, "assault"));
*/
                pline("%sがあなたの視界に迫ってきた．", xname(obj));
/*JP
                You("are unaffected!");
*/
                pline("しかし，あなたは影響を受けなかった！");
            }
            break;
        case 4:
/*JP
            pline("%s your mind!", Tobjnam(obj, "zap"));
*/
            pline("%sを覗いていると五感がおかしくなってきた！", xname(obj));
            (void) make_hallucinated(
                (HHallucination & TIMEOUT) + (long) rnd(100), FALSE, 0L);
            break;
        case 5:
/*JP
            pline("%s!", Tobjnam(obj, "explode"));
*/
            pline("%sは爆発した！", xname(obj));
            useup(obj);
            *optr = obj = 0; /* it's gone */
            /* physical damage cause by the shards and force */
/*JP
            losehp(Maybe_Half_Phys(rnd(30)), "exploding crystal ball",
*/
            losehp(Maybe_Half_Phys(rnd(30)), "水晶玉の爆発で",
                   KILLED_BY_AN);
            break;
        }
        if (obj)
            consume_obj_charge(obj, TRUE);
        return;
    }

    if (Hallucination) {
        if (!obj->spe) {
/*JP
            pline("All you see is funky %s haze.", hcolor((char *) 0));
*/
            pline("おお！ファンキーモンキーな%sもやが見える．", hcolor((char *)0));
        } else {
            switch (rnd(6)) {
            case 1:
/*JP
                You("grok some groovy globs of incandescent lava.");
*/
                You("水門の鍵をもった水戸黄門が壁の影に隠れているのが見えた．");
                break;
            case 2:
#if 0 /*JP:T*/
                pline("Whoa!  Psychedelic colors, %s!",
                      poly_gender() == 1 ? "babe" : "dude");
#else
                pline("ワーオ！ラリってるかい？%s！",
                      poly_gender() == 1 ? "ベイビー" : "ユー");
#endif
                break;
            case 3:
/*JP
                pline_The("crystal pulses with sinister %s light!",
*/
                pline("水晶は不吉な%sパルスを発した！", 
                          hcolor((char *) 0));
                break;
            case 4:
/*JP
                You_see("goldfish swimming above fluorescent rocks.");
*/
                You("蛍光岩の上を金魚が泳いでいるのを見た．");
                break;
            case 5:
#if 0 /*JP*/
                You_see(
                    "tiny snowflakes spinning around a miniature farmhouse.");
#else
                    You("小さい雪片がミニチュアの農家の家のまわりを舞ってるのを見た．");
#endif
                break;
            default:
/*JP
                pline("Oh wow... like a kaleidoscope!");
*/
                pline("ワーオ．万華鏡のようだ！");
                break;
            }
            consume_obj_charge(obj, TRUE);
        }
        return;
    }

    /* read a single character */
    if (flags.verbose)
/*JP
        You("may look for an object or monster symbol.");
*/
        You("物体や怪物の記号を探せる．");
/*JP
    ch = yn_function("What do you look for?", (char *) 0, '\0');
*/
    ch = yn_function("何を探しますか？", (char *)0, '\0');
    /* Don't filter out ' ' here; it has a use */
    if ((ch != def_monsyms[S_GHOST].sym) && index(quitchars, ch)) {
        if (flags.verbose)
            pline1(Never_mind);
        return;
    }
/*JP
    You("peer into %s...", the(xname(obj)));
*/
    You("%sを覗きこんだ．．．", the(xname(obj)));
    nomul(-rnd(10));
/*JP
    multi_reason = "gazing into a crystal ball";
*/
    multi_reason = "水晶球を覗き込んでいる時に";
    nomovemsg = "";
    if (obj->spe <= 0)
/*JP
        pline_The("vision is unclear.");
*/
        pline("映像は不鮮明だった．");
    else {
        int class;
        int ret = 0;

        makeknown(CRYSTAL_BALL);
        consume_obj_charge(obj, TRUE);

        /* special case: accept ']' as synonym for mimic
         * we have to do this before the def_char_to_objclass check
         */
        if (ch == DEF_MIMIC_DEF)
            ch = DEF_MIMIC;

        if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
            ret = object_detect((struct obj *) 0, class);
        else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
            ret = monster_detect((struct obj *) 0, class);
        else if (iflags.bouldersym && (ch == iflags.bouldersym))
            ret = object_detect((struct obj *) 0, ROCK_CLASS);
        else
            switch (ch) {
            case '^':
                ret = trap_detect((struct obj *) 0);
                break;
            default: {
                int i = rn2(SIZE(level_detects));
#if 0 /*JP*/
                You_see("%s, %s.", level_detects[i].what,
                        level_distance(level_detects[i].where));
#else
                You_see("%sを%s見た．", level_detects[i].what,
                        level_distance(level_detects[i].where));
#endif
            }
                ret = 0;
                break;
            }

        if (ret) {
            if (!rn2(100)) /* make them nervous */
/*JP
                You_see("the Wizard of Yendor gazing out at you.");
*/
                You("イェンダーの魔法使いがあなたをにらんでいるのを見た．");
            else
/*JP
                pline_The("vision is unclear.");
*/
                pline("映像は不鮮明になった．");
        }
    }
    return;
}

STATIC_OVL void
show_map_spot(x, y)
register int x, y;
{
    struct rm *lev;
    struct trap *t;
    int oldglyph;

    if (Confusion && rn2(7))
        return;
    lev = &levl[x][y];

    lev->seenv = SVALL;

    /* Secret corridors are found, but not secret doors. */
    if (lev->typ == SCORR) {
        lev->typ = CORR;
        unblock_point(x, y);
    }

    /*
     * Force the real background, then if it's not furniture and there's
     * a known trap there, display the trap, else if there was an object
     * shown there, redisplay the object.  So during mapping, furniture
     * takes precedence over traps, which take precedence over objects,
     * opposite to how normal vision behaves.
     */
    oldglyph = glyph_at(x, y);
    if (level.flags.hero_memory) {
        magic_map_background(x, y, 0);
        newsym(x, y); /* show it, if not blocked */
    } else {
        magic_map_background(x, y, 1); /* display it */
    }
    if (!IS_FURNITURE(lev->typ)) {
        if ((t = t_at(x, y)) != 0 && t->tseen) {
            map_trap(t, 1);
        } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
            show_glyph(x, y, oldglyph);
            if (level.flags.hero_memory)
                lev->glyph = oldglyph;
        }
    }
}

void
do_mapping()
{
    register int zx, zy;

    iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
    u.uinwater = u.uburied = 0;
    for (zx = 1; zx < COLNO; zx++)
        for (zy = 0; zy < ROWNO; zy++)
            show_map_spot(zx, zy);
    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
    if (!level.flags.hero_memory || Underwater) {
        flush_screen(1);                 /* flush temp screen */
        display_nhwindow(WIN_MAP, TRUE); /* wait */
        docrt();
    }
    exercise(A_WIS, TRUE);
}

void
do_vicinity_map()
{
    register int zx, zy;
    int lo_y = (u.uy - 5 < 0 ? 0 : u.uy - 5),
        hi_y = (u.uy + 6 > ROWNO ? ROWNO : u.uy + 6),
        lo_x = (u.ux - 9 < 1 ? 1 : u.ux - 9), /* avoid column 0 */
        hi_x = (u.ux + 10 > COLNO ? COLNO : u.ux + 10);

    for (zx = lo_x; zx < hi_x; zx++)
        for (zy = lo_y; zy < hi_y; zy++)
            show_map_spot(zx, zy);

    if (!level.flags.hero_memory || Underwater) {
        flush_screen(1);                 /* flush temp screen */
        display_nhwindow(WIN_MAP, TRUE); /* wait */
        docrt();
    }
}

/* convert a secret door into a normal door */
void
cvt_sdoor_to_door(lev)
struct rm *lev;
{
    int newmask = lev->doormask & ~WM_MASK;

    if (Is_rogue_level(&u.uz))
        /* rogue didn't have doors, only doorways */
        newmask = D_NODOOR;
    else
        /* newly exposed door is closed */
        if (!(newmask & D_LOCKED))
        newmask |= D_CLOSED;

    lev->typ = DOOR;
    lev->doormask = newmask;
}

STATIC_PTR void
findone(zx, zy, num)
int zx, zy;
genericptr_t num;
{
    register struct trap *ttmp;
    register struct monst *mtmp;

    if (levl[zx][zy].typ == SDOOR) {
        cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
        magic_map_background(zx, zy, 0);
        newsym(zx, zy);
        (*(int *) num)++;
    } else if (levl[zx][zy].typ == SCORR) {
        levl[zx][zy].typ = CORR;
        unblock_point(zx, zy);
        magic_map_background(zx, zy, 0);
        newsym(zx, zy);
        (*(int *) num)++;
    } else if ((ttmp = t_at(zx, zy)) != 0) {
        if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
            ttmp->tseen = 1;
            newsym(zx, zy);
            (*(int *) num)++;
        }
    } else if ((mtmp = m_at(zx, zy)) != 0) {
        if (mtmp->m_ap_type) {
            seemimic(mtmp);
            (*(int *) num)++;
        }
        if (mtmp->mundetected
            && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
            mtmp->mundetected = 0;
            newsym(zx, zy);
            (*(int *) num)++;
        }
        if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
            map_invisible(zx, zy);
    } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
        unmap_object(zx, zy);
        newsym(zx, zy);
        (*(int *) num)++;
    }
}

STATIC_PTR void
openone(zx, zy, num)
int zx, zy;
genericptr_t num;
{
    register struct trap *ttmp;
    register struct obj *otmp;
    int *num_p = (int *) num;

    if (OBJ_AT(zx, zy)) {
        for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
            if (Is_box(otmp) && otmp->olocked) {
                otmp->olocked = 0;
                (*num_p)++;
            }
        }
        /* let it fall to the next cases. could be on trap. */
    }
    if (levl[zx][zy].typ == SDOOR
        || (levl[zx][zy].typ == DOOR
            && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
        if (levl[zx][zy].typ == SDOOR)
            cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
        if (levl[zx][zy].doormask & D_TRAPPED) {
            if (distu(zx, zy) < 3)
/*JP
                b_trapped("door", 0);
*/
                b_trapped("扉", 0);
            else
#if 0 /*JP*/
                Norep("You %s an explosion!",
                      cansee(zx, zy) ? "see" : (!Deaf ? "hear"
                                                      : "feel the shock of"));
#else
                Norep("あなたは爆発%s！",
                      cansee(zx, zy) ? "を見た" : (!Deaf ? "音を聞いた"
                                                         : "の衝撃を感じた"));
#endif
            wake_nearto(zx, zy, 11 * 11);
            levl[zx][zy].doormask = D_NODOOR;
        } else
            levl[zx][zy].doormask = D_ISOPEN;
        unblock_point(zx, zy);
        newsym(zx, zy);
        (*num_p)++;
    } else if (levl[zx][zy].typ == SCORR) {
        levl[zx][zy].typ = CORR;
        unblock_point(zx, zy);
        newsym(zx, zy);
        (*num_p)++;
    } else if ((ttmp = t_at(zx, zy)) != 0) {
        struct monst *mon;
        boolean dummy; /* unneeded "you notice it arg" */

        if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
            ttmp->tseen = 1;
            newsym(zx, zy);
            (*num_p)++;
        }
        mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
        if (openholdingtrap(mon, &dummy)
            || openfallingtrap(mon, TRUE, &dummy))
            (*num_p)++;
    } else if (find_drawbridge(&zx, &zy)) {
        /* make sure it isn't an open drawbridge */
        open_drawbridge(zx, zy);
        (*num_p)++;
    }
}

/* returns number of things found */
int
findit()
{
    int num = 0;

    if (u.uswallow)
        return 0;
    do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
    return num;
}

/* returns number of things found and opened */
int
openit()
{
    int num = 0;

    if (u.uswallow) {
        if (is_animal(u.ustuck->data)) {
            if (Blind)
/*JP
                pline("Its mouth opens!");
*/
                pline("何者かの口が開いた！");
            else
/*JP
                pline("%s opens its mouth!", Monnam(u.ustuck));
*/
                pline("%sは口を開いた！", Monnam(u.ustuck));
        }
        expels(u.ustuck, u.ustuck->data, TRUE);
        return -1;
    }

    do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
    return num;
}

/* callback hack for overriding vision in do_clear_area() */
boolean
detecting(func)
void FDECL((*func), (int, int, genericptr_t));
{
    return (func == findone || func == openone);
}

void
find_trap(trap)
struct trap *trap;
{
    int tt = what_trap(trap->ttyp);
    boolean cleared = FALSE;

    trap->tseen = 1;
    exercise(A_WIS, TRUE);
    feel_newsym(trap->tx, trap->ty);

    if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
        /* There's too much clutter to see your find otherwise */
        cls();
        map_trap(trap, 1);
        display_self();
        cleared = TRUE;
    }

/*JP
    You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
*/
    You("%sを見つけた．", defsyms[trap_to_defsym(tt)].explanation);

    if (cleared) {
        display_nhwindow(WIN_MAP, TRUE); /* wait */
        docrt();
    }
}

int
dosearch0(aflag)
register int aflag; /* intrinsic autosearch vs explicit searching */
{
#ifdef GCC_BUG
    /* some versions of gcc seriously muck up nested loops. if you get strange
       crashes while searching in a version compiled with gcc, try putting
       #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the
       makefile).
     */
    volatile xchar x, y;
#else
    register xchar x, y;
#endif
    register struct trap *trap;
    register struct monst *mtmp;

    if (u.uswallow) {
        if (!aflag)
/*JP
            pline("What are you looking for?  The exit?");
*/
            pline("何を探すんだい？非常口？");
    } else {
        int fund = (uwep && uwep->oartifact
                    && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;

        if (ublindf && ublindf->otyp == LENSES && !Blind)
            fund += 2; /* JDS: lenses help searching */
        if (fund > 5)
            fund = 5;
        for (x = u.ux - 1; x < u.ux + 2; x++)
            for (y = u.uy - 1; y < u.uy + 2; y++) {
                if (!isok(x, y))
                    continue;
                if (x == u.ux && y == u.uy)
                    continue;

                if (Blind && !aflag)
                    feel_location(x, y);
                if (levl[x][y].typ == SDOOR) {
                    if (rnl(7 - fund))
                        continue;
                    cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
                    exercise(A_WIS, TRUE);
                    nomul(0);
                    feel_location(x, y); /* make sure it shows up */
/*JP
                    You("find a hidden door.");
*/
                    You("隠された扉を見つけた．");
                } else if (levl[x][y].typ == SCORR) {
                    if (rnl(7 - fund))
                        continue;
                    levl[x][y].typ = CORR;
                    unblock_point(x, y); /* vision */
                    exercise(A_WIS, TRUE);
                    nomul(0);
                    feel_location(x, y); /* make sure it shows up */
/*JP
                    You("find a hidden passage.");
*/
                    You("隠された通路を見つけた．");
                } else {
                    /* Be careful not to find anything in an SCORR or SDOOR */
                    if ((mtmp = m_at(x, y)) != 0 && !aflag) {
                        if (mtmp->m_ap_type) {
                            seemimic(mtmp);
                        find:
                            exercise(A_WIS, TRUE);
                            if (!canspotmon(mtmp)) {
                                if (glyph_is_invisible(levl[x][y].glyph)) {
                                    /* found invisible monster in a square
                                     * which already has an 'I' in it.
                                     * Logically, this should still take
                                     * time and lead to a return(1), but
                                     * if we did that the player would keep
                                     * finding the same monster every turn.
                                     */
                                    continue;
                                } else {
/*JP
                                    You_feel("an unseen monster!");
*/
                                    You("見えない怪物の気配を感じた！");
                                    map_invisible(x, y);
                                }
                            } else if (!sensemon(mtmp))
/*JP
                                You("find %s.", mtmp->mtame
*/
                                You("%sを見つけた．", mtmp->mtame
                                                   ? y_monnam(mtmp)
                                                   : a_monnam(mtmp));
                            return 1;
                        }
                        if (!canspotmon(mtmp)) {
                            if (mtmp->mundetected
                                && (is_hider(mtmp->data)
                                    || mtmp->data->mlet == S_EEL))
                                mtmp->mundetected = 0;
                            newsym(x, y);
                            goto find;
                        }
                    }

                    /* see if an invisible monster has moved--if Blind,
                     * feel_location() already did it
                     */
                    if (!aflag && !mtmp && !Blind
                        && glyph_is_invisible(levl[x][y].glyph)) {
                        unmap_object(x, y);
                        newsym(x, y);
                    }

                    if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
                        nomul(0);
                        if (trap->ttyp == STATUE_TRAP) {
                            if (activate_statue_trap(trap, x, y, FALSE))
                                exercise(A_WIS, TRUE);
                            return 1;
                        } else {
                            find_trap(trap);
                        }
                    }
                }
            }
    }
    return 1;
}

/* the 's' command -- explicit searching */
int
dosearch()
{
    return dosearch0(0);
}

/* Pre-map the sokoban levels */
void
sokoban_detect()
{
    register int x, y;
    register struct trap *ttmp;
    register struct obj *obj;

    /* Map the background and boulders */
    for (x = 1; x < COLNO; x++)
        for (y = 0; y < ROWNO; y++) {
            levl[x][y].seenv = SVALL;
            levl[x][y].waslit = TRUE;
            map_background(x, y, 1);
            if ((obj = sobj_at(BOULDER, x, y)) != 0)
                map_object(obj, 1);
        }

    /* Map the traps */
    for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
        ttmp->tseen = 1;
        map_trap(ttmp, 1);
        /* set sokoban_rules when there is at least one pit or hole */
        if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
            Sokoban = 1;
    }
}

/* idea from crawl; show known portion of map without any monsters,
   objects, or traps occluding the view of the underlying terrain */
void
reveal_terrain(full, which_subset)
int full; /* wizard|explore modes allow player to request full map */
int which_subset; /* when not full, whether to suppress objs and/or traps */
{
    if ((Hallucination || Stunned || Confusion) && !full) {
/*JP
        You("are too disoriented for this.");
*/
        You("混乱しているのでそれはできない．");
    } else {
        int x, y, glyph, levl_glyph, default_glyph;
        uchar seenv;
        unsigned save_swallowed;
        struct monst *mtmp;
        struct trap *t;
        char buf[BUFSZ];
        boolean keep_traps = (which_subset & 1) !=0,
                keep_objs = (which_subset & 2) != 0,
                keep_mons = (which_subset & 4) != 0; /* actually always 0 */

        save_swallowed = u.uswallow;
        iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
        u.uinwater = u.uburied = 0;
        u.uswallow = 0;
        default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
        /* for 'full', show the actual terrain for the entire level,
           otherwise what the hero remembers for seen locations with
           monsters, objects, and/or traps removed as caller dictates */
        for (x = 1; x < COLNO; x++)
            for (y = 0; y < ROWNO; y++) {
                seenv = (full || level.flags.hero_memory)
                           ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
                if (full) {
                    levl[x][y].seenv = SVALL;
                    glyph = back_to_glyph(x, y);
                    levl[x][y].seenv = seenv;
                } else {
                    levl_glyph = level.flags.hero_memory
                                    ? levl[x][y].glyph
                                    : seenv
                                       ? back_to_glyph(x, y)
                                       : default_glyph;
                    /* glyph_at() returns the displayed glyph, which might
                       be a monster.  levl[][].glyph contains the remembered
                       glyph, which will never be a monster (unless it is
                       the invisible monster glyph, which is handled like
                       an object, replacing any object or trap at its spot) */
                    glyph = !save_swallowed ? glyph_at(x, y) : levl_glyph;
                    if (keep_mons && x == u.ux && y == u.uy && save_swallowed)
                        glyph = mon_to_glyph(u.ustuck);
                    else if (((glyph_is_monster(glyph)
                               || glyph_is_warning(glyph)) && !keep_mons)
                             || glyph_is_swallow(glyph))
                        glyph = levl_glyph;
                    if (((glyph_is_object(glyph) && !keep_objs)
                         || glyph_is_invisible(glyph))
                        && keep_traps && !covers_traps(x, y)) {
                        if ((t = t_at(x, y)) != 0 && t->tseen)
                            glyph = trap_to_glyph(t);
                    }
                    if ((glyph_is_object(glyph) && !keep_objs)
                        || (glyph_is_trap(glyph) && !keep_traps)
                        || glyph_is_invisible(glyph)) {
                        if (!seenv) {
                            glyph = default_glyph;
                        } else if (lastseentyp[x][y] == levl[x][y].typ) {
                            glyph = back_to_glyph(x, y);
                        } else {
                            /* look for a mimic here posing as furniture;
                               if we don't find one, we'll have to fake it */
                            if ((mtmp = m_at(x, y)) != 0
                                && mtmp->m_ap_type == M_AP_FURNITURE) {
                                glyph = cmap_to_glyph(mtmp->mappearance);
                            } else {
                                /* we have a topology type but we want a
                                   screen symbol in order to derive a glyph;
                                   some screen symbols need the flags field
                                   of levl[][] in addition to the type
                                   (to disambiguate STAIRS to S_upstair or
                                   S_dnstair, for example; current flags
                                   might not be intended for remembered
                                   type, but we've got no other choice) */
                                schar save_typ = levl[x][y].typ;

                                levl[x][y].typ = lastseentyp[x][y];
                                glyph = back_to_glyph(x, y);
                                levl[x][y].typ = save_typ;
                            }
                        }
                    }
                }
                show_glyph(x, y, glyph);
            }

        /* [TODO: highlight hero's location somehow] */
        u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
        if (save_swallowed)
            u.uswallow = 1;
        flush_screen(1);
        if (full) {
/*JP
            Strcpy(buf, "underlying terrain");
*/
            Strcpy(buf, "下にある地形");
        } else {
/*JP
            Strcpy(buf, "known terrain");
*/
            Strcpy(buf, "知っている地形");
            if (keep_traps)
#if 0 /*JP*/
                Sprintf(eos(buf), "%s traps",
                        (keep_objs || keep_mons) ? "," : " and");
#else
                Strcat(buf, "と罠");
#endif
            if (keep_objs)
#if 0 /*JP*/
                Sprintf(eos(buf), "%s%s objects",
                        (keep_traps || keep_mons) ? "," : "",
                        keep_mons ? "" : " and");
#else
                Strcat(buf, "と物体");
#endif
            if (keep_mons)
#if 0 /*JP*/
                Sprintf(eos(buf), "%s and monsters",
                        (keep_traps || keep_objs) ? "," : "");
#else
                Strcat(buf, "と怪物");
#endif
        }
/*JP
        pline("Showing %s only...", buf);
*/
        pline("%sだけを見る．．．", buf);
        display_nhwindow(WIN_MAP, TRUE); /* give "--More--" prompt */
        docrt(); /* redraw the screen, restoring regular map */
        if (Underwater)
            under_water(2);
        if (u.uburied)
            under_ground(2);
    }
    return;
}

/*detect.c*/
