From f5a1e609bfe29fa04768cfc4c36d6862143c7d03 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:48 +0000 Subject: [PATCH] *** empty log message *** --- src/detect.c | 1233 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1233 insertions(+) create mode 100644 src/detect.c diff --git a/src/detect.c b/src/detect.c new file mode 100644 index 000000000..d73a69e9c --- /dev/null +++ b/src/detect.c @@ -0,0 +1,1233 @@ +/* SCCS Id: @(#)detect.c 3.3 1999/12/06 */ +/* 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. + */ + +#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 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 and return 1st 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; +register char oclass; +unsigned material; +{ + register 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 && + ( +#ifndef GOLDOBJ + mtmp->mgold || +#endif + mtmp->minvent))))); + } else { + if (material && objects[glyph_to_obj(glyph)].oc_material == material) { + /* the 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) { + /* the object 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 */ +#ifndef GOLDOBJ + if ((mtmp = m_at(x,y)) != 0) { + if (oclass == GOLD_CLASS && mtmp->mgold) + return FALSE; + else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if (o_in(otmp, oclass)) return FALSE; + } +#else + if ((mtmp = m_at(x,y)) != 0) { + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if (o_in(otmp, oclass)) return FALSE; + } +#endif + /* 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) +register char oclass; +unsigned material; +{ + register int zx, zy; + register 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; + int uw = u.uinwater; + struct obj *temp; + boolean stale; + + known = stale = clear_stale_map(GOLD_CLASS, 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... */ +#ifndef GOLDOBJ + if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) { +#else + if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { +#endif + 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, GOLD_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, GOLD_CLASS)) { + known = TRUE; + if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap; + } + } + + if (!known) { + /* no gold found */ + if (sobj) strange_feeling(sobj, "You feel materially poor."); + return(1); + } + /* only under me - no separate display required */ + if (stale) docrt(); + You("notice some gold between your %s.", makeplural(body_part(FOOT))); + return(0); + +outgoldmap: + cls(); + + u.uinwater = 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, GOLD_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 */ +#ifndef GOLDOBJ + if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) { +#else + if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { +#endif + struct obj gold; + + 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, GOLD_CLASS))) { + temp->ox = mtmp->mx; + temp->oy = mtmp->my; + map_object(temp,1); + break; + } + } + + newsym(u.ux,u.uy); + You_feel("very greedy, and sense gold!"); + exercise(A_WIS, TRUE); + display_nhwindow(WIN_MAP, TRUE); + docrt(); + u.uinwater = uw; + 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; + const char *what = confused ? something : "food"; + int uw = u.uinwater; + + 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(); + You("sense a lack of %s nearby.", what); + if (sobj && sobj->blessed) { + if (!u.uedibility) Your("%s starts to tingle.", body_part(NOSE)); + u.uedibility = 1; + } + } else if (sobj) { + char buf[BUFSZ]; + Sprintf(buf, "Your %s twitches%s.", body_part(NOSE), + (sobj->blessed && !u.uedibility) ? " then starts to tingle" : ""); + strange_feeling(sobj, buf); + if (sobj->blessed && !u.uedibility) + u.uedibility = 1; + } + return !stale; + } else if (!ct) { + known = TRUE; + You("%s %s nearby.", sobj ? "smell" : "sense", what); + if (sobj && sobj->blessed) { + if (!u.uedibility) pline("Your %s starts to tingle.", body_part(NOSE)); + u.uedibility = 1; + } + } else { + struct obj *temp; + known = TRUE; + cls(); + u.uinwater = 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); + if (sobj) { + if (sobj->blessed) { + Your("%s %s to tingle and you smell %s.", body_part(NOSE), + u.uedibility ? "continues" : "starts", what); + u.uedibility = 1; + } else + Your("%s tingles and you smell %s.", body_part(NOSE), what); + } + else You("sense %s.", what); + display_nhwindow(WIN_MAP, TRUE); + exercise(A_WIS, TRUE); + docrt(); + u.uinwater = uw; + if (Underwater) under_water(2); + if (u.uburied) under_ground(2); + } + return(0); +} + +/* + * Used for scrolls, potions, 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; + int is_cursed = (detector && detector->cursed); + int do_dknown = + (detector && detector->oclass == POTION_CLASS && detector->blessed); + int ct = 0, ctu = 0; + register struct obj *obj, *otmp = (struct obj *)0; + register struct monst *mtmp; + int uw = u.uinwater; + const char *stuff; + + if (class < 0 || class >= MAXOCLASSES) { + impossible("object_detect: illegal class %d", class); + class = 0; + } + + if (Hallucination || (Confusion && class == SCROLL_CLASS)) + stuff = something; + else + stuff = class ? oclass_names[class] : "objects"; + + if (do_dknown) for(obj = invent; obj; obj = obj->nobj) do_dknown_of(obj); + + for (obj = fobj; 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 (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 || o_in(obj, class)) 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)) || +#ifndef GOLDOBJ + (mtmp->mgold && (!class || class == GOLD_CLASS))) { +#else + (findgold(mtmp->minvent) && (!class || class == GOLD_CLASS))) { +#endif + ct++; + break; + } + } + + if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) { + if (!ctu) { + if (detector) + strange_feeling(detector, "You feel a lack of something."); + return 1; + } + + You("sense %s nearby.", stuff); + return 0; + } + + cls(); + + u.uinwater = 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 || (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); + 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 || (otmp = o_in(obj, class))) { + if (!class) 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.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); +#ifndef GOLDOBJ + } else if (mtmp->mgold && (!class || class == GOLD_CLASS)) { +#else + } else if (findgold(mtmp->minvent) && (!class || class == GOLD_CLASS)) { +#endif + struct obj gold; + + gold.otyp = GOLD_PIECE; + gold.ox = mtmp->mx; + gold.oy = mtmp->my; + map_object(&gold, 1); + } + } + + newsym(u.ux,u.uy); + You("detect the %s of %s.", ct ? "presence" : "absence", stuff); + 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 */ + + u.uinwater = uw; + 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 ? + "You get the heebie jeebies." : + "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) + if (mtmp->mx > 0) + show_glyph(mtmp->mx,mtmp->my,mon_to_glyph(mtmp)); + if (otmp && otmp->cursed && + (mtmp->msleeping || !mtmp->mcanmove)) { + mtmp->msleeping = mtmp->mfrozen = 0; + mtmp->mcanmove = 1; + woken = TRUE; + } + } + display_self(); + You("sense the presence of monsters."); + if (woken) + pline("Monsters sense the presence of you."); + 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 */ + 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); + } + +} + +/* 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; + register struct obj *obj; + register int door; + int uw = u.uinwater; + boolean found = FALSE; + coord cc; + + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { + if (ttmp->tx != u.ux || ttmp->ty != u.uy) + goto outtrapmap; + else found = TRUE; + } + for (obj = fobj; obj; obj = obj->nobj) { + if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped) { + if (obj->ox != u.ux || obj->oy != u.uy) + goto outtrapmap; + else found = TRUE; + } + } + 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[42]; + Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE))); + strange_feeling(sobj,buf); + return(1); + } + /* traps exist, but only under me - no separate display required */ + Your("%s itch.", makeplural(body_part(TOE))); + return(0); +outtrapmap: + cls(); + + u.uinwater = 0; + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + sense_trap(ttmp, 0, 0, sobj && sobj->cursed); + + for (obj = fobj; obj; obj = obj->nobj) + if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped) + sense_trap((struct trap *)0, obj->ox, obj->oy, sobj && sobj->cursed); + + 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, sobj && sobj->cursed); + } + + newsym(u.ux,u.uy); + You_feel("%s.", sobj && sobj->cursed ? "very greedy" : "entrapped"); + display_nhwindow(WIN_MAP, TRUE); + docrt(); + u.uinwater = uw; + 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) return "far away"; + else return "far below"; + else if (ll < -1) + if (!indun) return "away below you"; + else return "below you"; + else + if (!indun) return "in the distance"; + else return "just below"; + } else if (ll > 0) { + if (ll > (8 + rn2(3))) + if (!indun) return "far away"; + else return "far above"; + else if (ll > 1) + if (!indun) return "away above you"; + else return "above you"; + else + if (!indun) return "in the distance"; + else return "just above"; + } else + if (!indun) return "in the distance"; + else return "near you"; +} + +static struct { + const char *what; + d_level *where; +} level_detects[] = { + { "Delphi", &oracle_level }, + { "Medusa's lair", &medusa_level }, + { "a castle", &stronghold_level }, + { "the Wizard of Yendor's tower", &wiz1_level }, +}; + +void +use_crystal_ball(obj) +struct obj *obj; +{ + char ch; + int oops; + const char *bname = xname(obj); + + if (Blind) { + pline("Too bad you can't see %s", the(bname)); + return; + } + oops = (rnd(20) > ACURR(A_INT) || obj->cursed); + if (oops && (obj->spe > 0)) { + switch (rnd(obj->oartifact ? 4 : 5)) { + case 1 : pline("%s is too much to comprehend!", The(bname)); + break; + case 2 : pline("%s confuses you!", The(bname)); + make_confused(HConfusion + rnd(100),FALSE); + break; + case 3 : if (!resists_blnd(&youmonst)) { + pline("%s damages your vision!", The(bname)); + make_blinded(Blinded + rnd(100),FALSE); + if (!Blind) Your(vision_clears); + } else { + pline("%s assaults your vision.", The(bname)); + You("are unaffected!"); + } + break; + case 4 : pline("%s zaps your mind!", The(bname)); + make_hallucinated(HHallucination + rnd(100),FALSE,0L); + break; + case 5 : pline("%s explodes!", The(bname)); + useup(obj); + losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN); + break; + } + check_unpaid(obj); + obj->spe--; + return; + } + + if (Hallucination) { + if (!obj->spe) { + pline("All you see is funky %s haze.", hcolor((char *)0)); + } else { + switch(rnd(6)) { + case 1 : You("grok some groovy globs of incandescent lava."); + break; + case 2 : pline("Whoa! Psychedelic colors, %s!", + poly_gender() == 1 ? "babe" : "dude"); + break; + case 3 : pline_The("crystal pulses with sinister %s light!", + hcolor((char *)0)); + break; + case 4 : You("see goldfish swimming above fluorescent rocks."); + break; + case 5 : You("see tiny snowflakes spinning around a miniature farmhouse."); + break; + default: pline("Oh wow... like a kaleidoscope!"); + break; + } + check_unpaid(obj); + obj->spe--; + } + return; + } + + /* read a single character */ + if (flags.verbose) You("may look for an object or monster symbol."); + ch = yn_function("What do you look for?", (char *)0, '\0'); + if (index(quitchars,ch)) { + if (flags.verbose) pline(Never_mind); + return; + } + You("peer into %s...", the(bname)); + nomul(-rnd(10)); + nomovemsg = ""; + if (obj->spe <= 0) + pline_The("vision is unclear."); + else { + int class; + int ret = 0; + + makeknown(CRYSTAL_BALL); + check_unpaid(obj); + obj->spe--; + + 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 switch(ch) { + case '^': + ret = trap_detect((struct obj *)0); + break; + default: + { + int i = rn2(SIZE(level_detects)); + You("see %s, %s.", + level_detects[i].what, + level_distance(level_detects[i].where)); + } + ret = 0; + break; + } + + if (ret) { + if (!rn2(100)) /* make them nervous */ + You("see the Wizard of Yendor gazing out at you."); + else pline_The("vision is unclear."); + } + } + return; +} + +STATIC_OVL void +show_map_spot(x, y) +register int x, y; +{ + register struct rm *lev; + + 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); + } + + /* if we don't remember an object or trap there, map it */ + if (lev->typ == ROOM ? + (glyph_is_cmap(lev->glyph) && !glyph_is_trap(lev->glyph) && + glyph_to_cmap(lev->glyph) != ROOM) : + (!glyph_is_object(lev->glyph) && !glyph_is_trap(lev->glyph))) { + 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 */ + } + } +} + +void +do_mapping() +{ + register int zx, zy; + int uw = u.uinwater; + + u.uinwater = 0; + for (zx = 1; zx < COLNO; zx++) + for (zy = 0; zy < ROWNO; zy++) + show_map_spot(zx, zy); + exercise(A_WIS, TRUE); + u.uinwater = uw; + if (!level.flags.hero_memory || Underwater) { + flush_screen(1); /* flush temp screen */ + display_nhwindow(WIN_MAP, TRUE); /* wait */ + docrt(); + } +} + +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; + +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) + /* rogue didn't have doors, only doorways */ + newmask = D_NODOOR; + else +#endif + /* 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 */ + newsym(zx, zy); + (*(int*)num)++; + } else if(levl[zx][zy].typ == SCORR) { + levl[zx][zy].typ = CORR; + 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; + + if(OBJ_AT(zx, zy)) { + for(otmp = level.objects[zx][zy]; + otmp; otmp = otmp->nexthere) { + if(Is_box(otmp) && otmp->olocked) { + otmp->olocked = 0; + (*(int*)num)++; + } + } + /* 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) b_trapped("door", 0); + else Norep("You %s an explosion!", + cansee(zx, zy) ? "see" : + (flags.soundok ? "hear" : + "feel the shock of")); + wake_nearto(zx, zy, 11*11); + levl[zx][zy].doormask = D_NODOOR; + } else + levl[zx][zy].doormask = D_ISOPEN; + newsym(zx, zy); + (*(int*)num)++; + } else if(levl[zx][zy].typ == SCORR) { + levl[zx][zy].typ = CORR; + 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 (find_drawbridge(&zx, &zy)) { + /* make sure it isn't an open drawbridge */ + open_drawbridge(zx, zy); + (*(int*)num)++; + } +} + +int +findit() /* returns number of things found */ +{ + int num = 0; + + if(u.uswallow) return(0); + do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num); + return(num); +} + +int +openit() /* returns number of things found and opened */ +{ + int num = 0; + + if(u.uswallow) { + if (is_animal(u.ustuck->data)) { + if (Blind) pline("Its mouth opens!"); + else pline("%s opens its mouth!", 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); +} + +void +find_trap(trap) +struct trap *trap; +{ + int tt = what_trap(trap->ttyp); + + You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation)); + trap->tseen = 1; + exercise(A_WIS, TRUE); + if (Blind) + feel_location(trap->tx, trap->ty); + else + 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 */ + tmp_at(DISP_ALWAYS, trap_to_glyph(trap)); + tmp_at(trap->tx, trap->ty); + display_nhwindow(WIN_MAP, TRUE); /* wait */ + tmp_at(DISP_END,0); + } +} + +int +dosearch0(aflag) +register int aflag; +{ +#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) + pline("What are you looking for? The exit?"); + } else { + int fund = (uwep && uwep->oartifact && + spec_ability(uwep, SPFX_SEARCH)) ? + ((uwep->spe > 5) ? 5 : uwep->spe) : 0; + 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) { + 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); + if (Blind && !aflag) + feel_location(x,y); /* make sure it shows up */ + else + newsym(x,y); + } 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); + newsym(x,y); + } else { + /* Be careful not to find anything in an SCORR or SDOOR */ + if((mtmp = m_at(x, y)) && !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 { + You_feel("an unseen monster!"); + map_invisible(x, y); + } + } else if (!sensemon(mtmp)) + You("find %s.", 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); +} + +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); + for (obj = level.objects[x][y]; obj; obj = obj->nexthere) + if (obj->otyp == BOULDER) + map_object(obj, 1); + } + + /* Map the traps */ + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { + ttmp->tseen = 1; + map_trap(ttmp, 1); + } +} + + +/*detect.c*/