1/* SCCS Id: @(#)detect.c 3.4 2003/08/13 */ 2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5/* 6 * Detection routines, including crystal ball, magic mapping, and search 7 * command. 8 */ 9 10#include "hack.h" 11#include "artifact.h" 12 13extern boolean known; /* from read.c */ 14 15STATIC_DCL void FDECL(do_dknown_of, (struct obj *)); 16STATIC_DCL boolean FDECL(check_map_spot, (int,int,CHAR_P,unsigned)); 17STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P,unsigned)); 18STATIC_DCL void FDECL(sense_trap, (struct trap *,XCHAR_P,XCHAR_P,int)); 19STATIC_DCL void FDECL(show_map_spot, (int,int)); 20STATIC_PTR void FDECL(findone,(int,int,genericptr_t)); 21STATIC_PTR void FDECL(openone,(int,int,genericptr_t)); 22 23/* Recursively search obj for an object in class oclass and return 1st found */ 24struct obj * 25o_in(obj, oclass) 26struct obj* obj; 27char oclass; 28{ 29 register struct obj* otmp; 30 struct obj *temp; 31 32 if (obj->oclass == oclass) return obj; 33 34 if (Has_contents(obj)) { 35 for (otmp = obj->cobj; otmp; otmp = otmp->nobj) 36 if (otmp->oclass == oclass) return otmp; 37 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass))) 38 return temp; 39 } 40 return (struct obj *) 0; 41} 42 43/* Recursively search obj for an object made of specified material and return 1st found */ 44struct obj * 45o_material(obj, material) 46struct obj* obj; 47unsigned material; 48{ 49 register struct obj* otmp; 50 struct obj *temp; 51 52 if (objects[obj->otyp].oc_material == material) return obj; 53 54 if (Has_contents(obj)) { 55 for (otmp = obj->cobj; otmp; otmp = otmp->nobj) 56 if (objects[otmp->otyp].oc_material == material) return otmp; 57 else if (Has_contents(otmp) && (temp = o_material(otmp, material))) 58 return temp; 59 } 60 return (struct obj *) 0; 61} 62 63STATIC_OVL void 64do_dknown_of(obj) 65struct obj *obj; 66{ 67 struct obj *otmp; 68 69 obj->dknown = 1; 70 if (Has_contents(obj)) { 71 for(otmp = obj->cobj; otmp; otmp = otmp->nobj) 72 do_dknown_of(otmp); 73 } 74} 75 76/* Check whether the location has an outdated object displayed on it. */ 77STATIC_OVL boolean 78check_map_spot(x, y, oclass, material) 79int x, y; 80register char oclass; 81unsigned material; 82{ 83 register int glyph; 84 register struct obj *otmp; 85 register struct monst *mtmp; 86 87 glyph = glyph_at(x,y); 88 if (glyph_is_object(glyph)) { 89 /* there's some object shown here */ 90 if (oclass == ALL_CLASSES) { 91 return((boolean)( !(level.objects[x][y] || /* stale if nothing here */ 92 ((mtmp = m_at(x,y)) != 0 && 93 ( 94#ifndef GOLDOBJ 95 mtmp->mgold || 96#endif 97 mtmp->minvent))))); 98 } else { 99 if (material && objects[glyph_to_obj(glyph)].oc_material == material) { 100 /* the object shown here is of interest because material matches */ 101 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) 102 if (o_material(otmp, GOLD)) return FALSE; 103 /* didn't find it; perhaps a monster is carrying it */ 104 if ((mtmp = m_at(x,y)) != 0) { 105 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) 106 if (o_material(otmp, GOLD)) return FALSE; 107 } 108 /* detection indicates removal of this object from the map */ 109 return TRUE; 110 } 111 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) { 112 /* the object shown here is of interest because its class matches */ 113 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) 114 if (o_in(otmp, oclass)) return FALSE; 115 /* didn't find it; perhaps a monster is carrying it */ 116#ifndef GOLDOBJ 117 if ((mtmp = m_at(x,y)) != 0) { 118 if (oclass == COIN_CLASS && mtmp->mgold) 119 return FALSE; 120 else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) 121 if (o_in(otmp, oclass)) return FALSE; 122 } 123#else 124 if ((mtmp = m_at(x,y)) != 0) { 125 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) 126 if (o_in(otmp, oclass)) return FALSE; 127 } 128#endif 129 /* detection indicates removal of this object from the map */ 130 return TRUE; 131 } 132 } 133 } 134 return FALSE; 135} 136 137/* 138 When doing detection, remove stale data from the map display (corpses 139 rotted away, objects carried away by monsters, etc) so that it won't 140 reappear after the detection has completed. Return true if noticeable 141 change occurs. 142 */ 143STATIC_OVL boolean 144clear_stale_map(oclass, material) 145register char oclass; 146unsigned material; 147{ 148 register int zx, zy; 149 register boolean change_made = FALSE; 150 151 for (zx = 1; zx < COLNO; zx++) 152 for (zy = 0; zy < ROWNO; zy++) 153 if (check_map_spot(zx, zy, oclass,material)) { 154 unmap_object(zx, zy); 155 change_made = TRUE; 156 } 157 158 return change_made; 159} 160 161/* look for gold, on the floor or in monsters' possession */ 162int 163gold_detect(sobj) 164register struct obj *sobj; 165{ 166 register struct obj *obj; 167 register struct monst *mtmp; 168 int uw = u.uinwater; 169 struct obj *temp; 170 boolean stale; 171 172 known = stale = clear_stale_map(COIN_CLASS, 173 (unsigned)(sobj->blessed ? GOLD : 0)); 174 175 /* look for gold carried by monsters (might be in a container) */ 176 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { 177 if (DEADMONSTER(mtmp)) continue; /* probably not needed in this case but... */ 178#ifndef GOLDOBJ 179 if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) { 180#else 181 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { 182#endif 183 known = TRUE; 184 goto outgoldmap; /* skip further searching */ 185 } else for (obj = mtmp->minvent; obj; obj = obj->nobj) 186 if (sobj->blessed && o_material(obj, GOLD)) { 187 known = TRUE; 188 goto outgoldmap; 189 } else if (o_in(obj, COIN_CLASS)) { 190 known = TRUE; 191 goto outgoldmap; /* skip further searching */ 192 } 193 } 194 195 /* look for gold objects */ 196 for (obj = fobj; obj; obj = obj->nobj) { 197 if (sobj->blessed && o_material(obj, GOLD)) { 198 known = TRUE; 199 if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap; 200 } else if (o_in(obj, COIN_CLASS)) { 201 known = TRUE; 202 if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap; 203 } 204 } 205 206 if (!known) { 207 /* no gold found on floor or monster's inventory. 208 adjust message if you have gold in your inventory */ 209 if (sobj) { 210 char buf[BUFSZ]; 211 if (youmonst.data == &mons[PM_GOLD_GOLEM]) { 212 Sprintf(buf, "You feel like a million %s!", 213 currency(2L)); 214 } else if (hidden_gold() || 215#ifndef GOLDOBJ 216 u.ugold) 217#else 218 money_cnt(invent)) 219#endif 220 Strcpy(buf, 221 "You feel worried about your future financial situation."); 222 else 223 Strcpy(buf, "You feel materially poor."); 224 strange_feeling(sobj, buf); 225 } 226 return(1); 227 } 228 /* only under me - no separate display required */ 229 if (stale) docrt(); 230 You("notice some gold between your %s.", makeplural(body_part(FOOT))); 231 return(0); 232 233outgoldmap: 234 cls(); 235 236 u.uinwater = 0; 237 /* Discover gold locations. */ 238 for (obj = fobj; obj; obj = obj->nobj) { 239 if (sobj->blessed && (temp = o_material(obj, GOLD))) { 240 if (temp != obj) { 241 temp->ox = obj->ox; 242 temp->oy = obj->oy; 243 } 244 map_object(temp,1); 245 } else if ((temp = o_in(obj, COIN_CLASS))) { 246 if (temp != obj) { 247 temp->ox = obj->ox; 248 temp->oy = obj->oy; 249 } 250 map_object(temp,1); 251 } 252 } 253 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { 254 if (DEADMONSTER(mtmp)) continue; /* probably overkill here */ 255#ifndef GOLDOBJ 256 if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) { 257#else 258 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { 259#endif 260 struct obj gold; 261 262 gold.otyp = GOLD_PIECE; 263 gold.ox = mtmp->mx; 264 gold.oy = mtmp->my; 265 map_object(&gold,1); 266 } else for (obj = mtmp->minvent; obj; obj = obj->nobj) 267 if (sobj->blessed && (temp = o_material(obj, GOLD))) { 268 temp->ox = mtmp->mx; 269 temp->oy = mtmp->my; 270 map_object(temp,1); 271 break; 272 } else if ((temp = o_in(obj, COIN_CLASS))) { 273 temp->ox = mtmp->mx; 274 temp->oy = mtmp->my; 275 map_object(temp,1); 276 break; 277 } 278 } 279 280 newsym(u.ux,u.uy); 281 You_feel("very greedy, and sense gold!"); 282 exercise(A_WIS, TRUE); 283 display_nhwindow(WIN_MAP, TRUE); 284 docrt(); 285 u.uinwater = uw; 286 if (Underwater) under_water(2); 287 if (u.uburied) under_ground(2); 288 return(0); 289} 290 291/* returns 1 if nothing was detected */ 292/* returns 0 if something was detected */ 293int 294food_detect(sobj) 295register struct obj *sobj; 296{ 297 register struct obj *obj; 298 register struct monst *mtmp; 299 register int ct = 0, ctu = 0; 300 boolean confused = (Confusion || (sobj && sobj->cursed)), stale; 301 char oclass = confused ? POTION_CLASS : FOOD_CLASS; 302 const char *what = confused ? something : "food"; 303 int uw = u.uinwater; 304 305 stale = clear_stale_map(oclass, 0); 306 307 for (obj = fobj; obj; obj = obj->nobj) 308 if (o_in(obj, oclass)) { 309 if (obj->ox == u.ux && obj->oy == u.uy) ctu++; 310 else ct++; 311 } 312 for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) { 313 /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */ 314 for (obj = mtmp->minvent; obj; obj = obj->nobj) 315 if (o_in(obj, oclass)) { 316 ct++; 317 break; 318 } 319 } 320 321 if (!ct && !ctu) { 322 known = stale && !confused; 323 if (stale) { 324 docrt(); 325 You("sense a lack of %s nearby.", what); 326 if (sobj && sobj->blessed) { 327 if (!u.uedibility) Your("%s starts to tingle.", body_part(NOSE)); 328 u.uedibility = 1; 329 } 330 } else if (sobj) { 331 char buf[BUFSZ]; 332 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE), 333 (sobj->blessed && !u.uedibility) ? " then starts to tingle" : ""); 334 if (sobj->blessed && !u.uedibility) { 335 boolean savebeginner = flags.beginner; /* prevent non-delivery of */ 336 flags.beginner = FALSE; /* message */ 337 strange_feeling(sobj, buf); 338 flags.beginner = savebeginner; 339 u.uedibility = 1; 340 } else 341 strange_feeling(sobj, buf); 342 } 343 return !stale; 344 } else if (!ct) { 345 known = TRUE; 346 You("%s %s nearby.", sobj ? "smell" : "sense", what); 347 if (sobj && sobj->blessed) { 348 if (!u.uedibility) pline("Your %s starts to tingle.", body_part(NOSE)); 349 u.uedibility = 1; 350 } 351 } else { 352 struct obj *temp; 353 known = TRUE; 354 cls(); 355 u.uinwater = 0; 356 for (obj = fobj; obj; obj = obj->nobj) 357 if ((temp = o_in(obj, oclass)) != 0) { 358 if (temp != obj) { 359 temp->ox = obj->ox; 360 temp->oy = obj->oy; 361 } 362 map_object(temp,1); 363 } 364 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) 365 /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */ 366 for (obj = mtmp->minvent; obj; obj = obj->nobj) 367 if ((temp = o_in(obj, oclass)) != 0) { 368 temp->ox = mtmp->mx; 369 temp->oy = mtmp->my; 370 map_object(temp,1); 371 break; /* skip rest of this monster's inventory */ 372 } 373 newsym(u.ux,u.uy); 374 if (sobj) { 375 if (sobj->blessed) { 376 Your("%s %s to tingle and you smell %s.", body_part(NOSE), 377 u.uedibility ? "continues" : "starts", what); 378 u.uedibility = 1; 379 } else 380 Your("%s tingles and you smell %s.", body_part(NOSE), what); 381 } 382 else You("sense %s.", what); 383 display_nhwindow(WIN_MAP, TRUE); 384 exercise(A_WIS, TRUE); 385 docrt(); 386 u.uinwater = uw; 387 if (Underwater) under_water(2); 388 if (u.uburied) under_ground(2); 389 } 390 return(0); 391} 392 393/* 394 * Used for scrolls, potions, spells, and crystal balls. Returns: 395 * 396 * 1 - nothing was detected 397 * 0 - something was detected 398 */ 399int 400object_detect(detector, class) 401struct obj *detector; /* object doing the detecting */ 402int class; /* an object class, 0 for all */ 403{ 404 register int x, y; 405 char stuff[BUFSZ]; 406 int is_cursed = (detector && detector->cursed); 407 int do_dknown = (detector && (detector->oclass == POTION_CLASS || 408 detector->oclass == SPBOOK_CLASS) && 409 detector->blessed); 410 int ct = 0, ctu = 0; 411 register struct obj *obj, *otmp = (struct obj *)0; 412 register struct monst *mtmp; 413 int uw = u.uinwater; 414 int sym, boulder = 0; 415 416 if (class < 0 || class >= MAXOCLASSES) { 417 impossible("object_detect: illegal class %d", class); 418 class = 0; 419 } 420 421 /* Special boulder symbol check - does the class symbol happen 422 * to match iflags.bouldersym which is a user-defined? 423 * If so, that means we aren't sure what they really wanted to 424 * detect. Rather than trump anything, show both possibilities. 425 * We can exclude checking the buried obj chain for boulders below. 426 */ 427 sym = class ? def_oc_syms[class] : 0; 428 if (sym && iflags.bouldersym && sym == iflags.bouldersym) 429 boulder = ROCK_CLASS; 430 431 if (Hallucination || (Confusion && class == SCROLL_CLASS)) 432 Strcpy(stuff, something); 433 else 434 Strcpy(stuff, class ? oclass_names[class] : "objects"); 435 if (boulder && class != ROCK_CLASS) Strcat(stuff, " and/or large stones"); 436 437 if (do_dknown) for(obj = invent; obj; obj = obj->nobj) do_dknown_of(obj); 438 439 for (obj = fobj; obj; obj = obj->nobj) { 440 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) { 441 if (obj->ox == u.ux && obj->oy == u.uy) ctu++; 442 else ct++; 443 } 444 if (do_dknown) do_dknown_of(obj); 445 } 446 447 for (obj = level.buriedobjlist; obj; obj = obj->nobj) { 448 if (!class || o_in(obj, class)) { 449 if (obj->ox == u.ux && obj->oy == u.uy) ctu++; 450 else ct++; 451 } 452 if (do_dknown) do_dknown_of(obj); 453 } 454 455 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { 456 if (DEADMONSTER(mtmp)) continue; 457 for (obj = mtmp->minvent; obj; obj = obj->nobj) { 458 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) ct++; 459 if (do_dknown) do_dknown_of(obj); 460 } 461 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT && 462 (!class || class == objects[mtmp->mappearance].oc_class)) || 463#ifndef GOLDOBJ 464 (mtmp->mgold && (!class || class == COIN_CLASS))) { 465#else 466 (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) { 467#endif 468 ct++; 469 break; 470 } 471 } 472 473 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) { 474 if (!ctu) { 475 if (detector) 476 strange_feeling(detector, "You feel a lack of something."); 477 return 1; 478 } 479 480 You("sense %s nearby.", stuff); 481 return 0; 482 } 483 484 cls(); 485 486 u.uinwater = 0; 487/* 488 * Map all buried objects first. 489 */ 490 for (obj = level.buriedobjlist; obj; obj = obj->nobj) 491 if (!class || (otmp = o_in(obj, class))) { 492 if (class) { 493 if (otmp != obj) { 494 otmp->ox = obj->ox; 495 otmp->oy = obj->oy; 496 } 497 map_object(otmp, 1); 498 } else 499 map_object(obj, 1); 500 } 501 /* 502 * If we are mapping all objects, map only the top object of a pile or 503 * the first object in a monster's inventory. Otherwise, go looking 504 * for a matching object class and display the first one encountered 505 * at each location. 506 * 507 * Objects on the floor override buried objects. 508 */ 509 for (x = 1; x < COLNO; x++) 510 for (y = 0; y < ROWNO; y++) 511 for (obj = level.objects[x][y]; obj; obj = obj->nexthere) 512 if ((!class && !boulder) || 513 (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) { 514 if (class || boulder) { 515 if (otmp != obj) { 516 otmp->ox = obj->ox; 517 otmp->oy = obj->oy; 518 } 519 map_object(otmp, 1); 520 } else 521 map_object(obj, 1); 522 break; 523 } 524 525 /* Objects in the monster's inventory override floor objects. */ 526 for (mtmp = fmon ; mtmp ; mtmp = mtmp->nmon) { 527 if (DEADMONSTER(mtmp)) continue; 528 for (obj = mtmp->minvent; obj; obj = obj->nobj) 529 if ((!class && !boulder) || 530 (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) { 531 if (!class && !boulder) otmp = obj; 532 otmp->ox = mtmp->mx; /* at monster location */ 533 otmp->oy = mtmp->my; 534 map_object(otmp, 1); 535 break; 536 } 537 /* Allow a mimic to override the detected objects it is carrying. */ 538 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT && 539 (!class || class == objects[mtmp->mappearance].oc_class)) { 540 struct obj temp; 541 542 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */ 543 temp.ox = mtmp->mx; 544 temp.oy = mtmp->my; 545 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */ 546 map_object(&temp, 1); 547#ifndef GOLDOBJ 548 } else if (mtmp->mgold && (!class || class == COIN_CLASS)) { 549#else 550 } else if (findgold(mtmp->minvent) && (!class || class == COIN_CLASS)) { 551#endif 552 struct obj gold; 553 554 gold.otyp = GOLD_PIECE; 555 gold.ox = mtmp->mx; 556 gold.oy = mtmp->my; 557 map_object(&gold, 1); 558 } 559 } 560 561 newsym(u.ux,u.uy); 562 You("detect the %s of %s.", ct ? "presence" : "absence", stuff); 563 display_nhwindow(WIN_MAP, TRUE); 564 /* 565 * What are we going to do when the hero does an object detect while blind 566 * and the detected object covers a known pool? 567 */ 568 docrt(); /* this will correctly reset vision */ 569 570 u.uinwater = uw; 571 if (Underwater) under_water(2); 572 if (u.uburied) under_ground(2); 573 return 0; 574} 575 576/* 577 * Used by: crystal balls, potions, fountains 578 * 579 * Returns 1 if nothing was detected. 580 * Returns 0 if something was detected. 581 */ 582int 583monster_detect(otmp, mclass) 584register struct obj *otmp; /* detecting object (if any) */ 585int mclass; /* monster class, 0 for all */ 586{ 587 register struct monst *mtmp; 588 int mcnt = 0; 589 590 591 /* Note: This used to just check fmon for a non-zero value 592 * but in versions since 3.3.0 fmon can test TRUE due to the 593 * presence of dmons, so we have to find at least one 594 * with positive hit-points to know for sure. 595 */ 596 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) 597 if (!DEADMONSTER(mtmp)) { 598 mcnt++; 599 break; 600 } 601 602 if (!mcnt) { 603 if (otmp) 604 strange_feeling(otmp, Hallucination ? 605 "You get the heebie jeebies." : 606 "You feel threatened."); 607 return 1; 608 } else { 609 boolean woken = FALSE; 610 611 cls(); 612 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { 613 if (DEADMONSTER(mtmp)) continue; 614 if (!mclass || mtmp->data->mlet == mclass || 615 (mtmp->data == &mons[PM_LONG_WORM] && mclass == S_WORM_TAIL)) 616 if (mtmp->mx > 0) { 617 if (mclass && def_monsyms[mclass] == ' ') 618 show_glyph(mtmp->mx,mtmp->my, 619 detected_mon_to_glyph(mtmp)); 620 else 621 show_glyph(mtmp->mx,mtmp->my,mon_to_glyph(mtmp)); 622 /* don't be stingy - display entire worm */ 623 if (mtmp->data == &mons[PM_LONG_WORM]) detect_wsegs(mtmp,0); 624 } 625 if (otmp && otmp->cursed && 626 (mtmp->msleeping || !mtmp->mcanmove)) { 627 mtmp->msleeping = mtmp->mfrozen = 0; 628 mtmp->mcanmove = 1; 629 woken = TRUE; 630 } 631 } 632 display_self(); 633 You("sense the presence of monsters."); 634 if (woken) 635 pline("Monsters sense the presence of you."); 636 display_nhwindow(WIN_MAP, TRUE); 637 docrt(); 638 if (Underwater) under_water(2); 639 if (u.uburied) under_ground(2); 640 } 641 return 0; 642} 643 644STATIC_OVL void 645sense_trap(trap, x, y, src_cursed) 646struct trap *trap; 647xchar x, y; 648int src_cursed; 649{ 650 if (Hallucination || src_cursed) { 651 struct obj obj; /* fake object */ 652 if (trap) { 653 obj.ox = trap->tx; 654 obj.oy = trap->ty; 655 } else { 656 obj.ox = x; 657 obj.oy = y; 658 } 659 obj.otyp = (src_cursed) ? GOLD_PIECE : random_object(); 660 obj.corpsenm = random_monster(); /* if otyp == CORPSE */ 661 map_object(&obj,1); 662 } else if (trap) { 663 map_trap(trap,1); 664 trap->tseen = 1; 665 } else { 666 struct trap temp_trap; /* fake trap */ 667 temp_trap.tx = x; 668 temp_trap.ty = y; 669 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */ 670 map_trap(&temp_trap,1); 671 } 672 673} 674 675/* the detections are pulled out so they can */ 676/* also be used in the crystal ball routine */ 677/* returns 1 if nothing was detected */ 678/* returns 0 if something was detected */ 679int 680trap_detect(sobj) 681register struct obj *sobj; 682/* sobj is null if crystal ball, *scroll if gold detection scroll */ 683{ 684 register struct trap *ttmp; 685 register struct obj *obj; 686 register int door; 687 int uw = u.uinwater; 688 boolean found = FALSE; 689 coord cc; 690 691 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { 692 if (ttmp->tx != u.ux || ttmp->ty != u.uy) 693 goto outtrapmap; 694 else found = TRUE; 695 } 696 for (obj = fobj; obj; obj = obj->nobj) { 697 if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped) { 698 if (obj->ox != u.ux || obj->oy != u.uy) 699 goto outtrapmap; 700 else found = TRUE; 701 } 702 } 703 for (door = 0; door < doorindex; door++) { 704 cc = doors[door]; 705 if (levl[cc.x][cc.y].doormask & D_TRAPPED) { 706 if (cc.x != u.ux || cc.y != u.uy) 707 goto outtrapmap; 708 else found = TRUE; 709 } 710 } 711 if (!found) { 712 char buf[42]; 713 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE))); 714 strange_feeling(sobj,buf); 715 return(1); 716 } 717 /* traps exist, but only under me - no separate display required */ 718 Your("%s itch.", makeplural(body_part(TOE))); 719 return(0); 720outtrapmap: 721 cls(); 722 723 u.uinwater = 0; 724 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) 725 sense_trap(ttmp, 0, 0, sobj && sobj->cursed); 726 727 for (obj = fobj; obj; obj = obj->nobj) 728 if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped) 729 sense_trap((struct trap *)0, obj->ox, obj->oy, sobj && sobj->cursed); 730 731 for (door = 0; door < doorindex; door++) { 732 cc = doors[door]; 733 if (levl[cc.x][cc.y].doormask & D_TRAPPED) 734 sense_trap((struct trap *)0, cc.x, cc.y, sobj && sobj->cursed); 735 } 736 737 newsym(u.ux,u.uy); 738 You_feel("%s.", sobj && sobj->cursed ? "very greedy" : "entrapped"); 739 display_nhwindow(WIN_MAP, TRUE); 740 docrt(); 741 u.uinwater = uw; 742 if (Underwater) under_water(2); 743 if (u.uburied) under_ground(2); 744 return(0); 745} 746 747const char * 748level_distance(where) 749d_level *where; 750{ 751 register schar ll = depth(&u.uz) - depth(where); 752 register boolean indun = (u.uz.dnum == where->dnum); 753 754 if (ll < 0) { 755 if (ll < (-8 - rn2(3))) 756 if (!indun) return "far away"; 757 else return "far below"; 758 else if (ll < -1) 759 if (!indun) return "away below you"; 760 else return "below you"; 761 else 762 if (!indun) return "in the distance"; 763 else return "just below"; 764 } else if (ll > 0) { 765 if (ll > (8 + rn2(3))) 766 if (!indun) return "far away"; 767 else return "far above"; 768 else if (ll > 1) 769 if (!indun) return "away above you"; 770 else return "above you"; 771 else 772 if (!indun) return "in the distance"; 773 else return "just above"; 774 } else 775 if (!indun) return "in the distance"; 776 else return "near you"; 777} 778 779static const struct { 780 const char *what; 781 d_level *where; 782} level_detects[] = { 783 { "Delphi", &oracle_level }, 784 { "Medusa's lair", &medusa_level }, 785 { "a castle", &stronghold_level }, 786 { "the Wizard of Yendor's tower", &wiz1_level }, 787}; 788 789void 790use_crystal_ball(obj) 791struct obj *obj; 792{ 793 char ch; 794 int oops; 795 796 if (Blind) { 797 pline("Too bad you can't see %s.", the(xname(obj))); 798 return; 799 } 800 oops = (rnd(20) > ACURR(A_INT) || obj->cursed); 801 if (oops && (obj->spe > 0)) { 802 switch (rnd(obj->oartifact ? 4 : 5)) { 803 case 1 : pline("%s too much to comprehend!", Tobjnam(obj, "are")); 804 break; 805 case 2 : pline("%s you!", Tobjnam(obj, "confuse")); 806 make_confused(HConfusion + rnd(100),FALSE); 807 break; 808 case 3 : if (!resists_blnd(&youmonst)) { 809 pline("%s your vision!", Tobjnam(obj, "damage")); 810 make_blinded(Blinded + rnd(100),FALSE); 811 if (!Blind) Your(vision_clears); 812 } else { 813 pline("%s your vision.", Tobjnam(obj, "assault")); 814 You("are unaffected!"); 815 } 816 break; 817 case 4 : pline("%s your mind!", Tobjnam(obj, "zap")); 818 (void) make_hallucinated(HHallucination + rnd(100),FALSE,0L); 819 break; 820 case 5 : pline("%s!", Tobjnam(obj, "explode")); 821 useup(obj); 822 obj = 0; /* it's gone */ 823 losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN); 824 break; 825 } 826 if (obj) consume_obj_charge(obj, TRUE); 827 return; 828 } 829 830 if (Hallucination) { 831 if (!obj->spe) { 832 pline("All you see is funky %s haze.", hcolor((char *)0)); 833 } else { 834 switch(rnd(6)) { 835 case 1 : You("grok some groovy globs of incandescent lava."); 836 break; 837 case 2 : pline("Whoa! Psychedelic colors, %s!", 838 poly_gender() == 1 ? "babe" : "dude"); 839 break; 840 case 3 : pline_The("crystal pulses with sinister %s light!", 841 hcolor((char *)0)); 842 break; 843 case 4 : You("see goldfish swimming above fluorescent rocks."); 844 break; 845 case 5 : You("see tiny snowflakes spinning around a miniature farmhouse."); 846 break; 847 default: pline("Oh wow... like a kaleidoscope!"); 848 break; 849 } 850 consume_obj_charge(obj, TRUE); 851 } 852 return; 853 } 854 855 /* read a single character */ 856 if (flags.verbose) You("may look for an object or monster symbol."); 857 ch = yn_function("What do you look for?", (char *)0, '\0'); 858 /* Don't filter out ' ' here; it has a use */ 859 if ((ch != def_monsyms[S_GHOST]) && index(quitchars,ch)) { 860 if (flags.verbose) pline(Never_mind); 861 return; 862 } 863 You("peer into %s...", the(xname(obj))); 864 nomul(-rnd(10)); 865 nomovemsg = ""; 866 if (obj->spe <= 0) 867 pline_The("vision is unclear."); 868 else { 869 int class; 870 int ret = 0; 871 872 makeknown(CRYSTAL_BALL); 873 consume_obj_charge(obj, TRUE); 874 875 /* special case: accept ']' as synonym for mimic 876 * we have to do this before the def_char_to_objclass check 877 */ 878 if (ch == DEF_MIMIC_DEF) ch = DEF_MIMIC; 879 880 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES) 881 ret = object_detect((struct obj *)0, class); 882 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES) 883 ret = monster_detect((struct obj *)0, class); 884 else if (iflags.bouldersym && (ch == iflags.bouldersym)) 885 ret = object_detect((struct obj *)0, ROCK_CLASS); 886 else switch(ch) { 887 case '^': 888 ret = trap_detect((struct obj *)0); 889 break; 890 default: 891 { 892 int i = rn2(SIZE(level_detects)); 893 You("see %s, %s.", 894 level_detects[i].what, 895 level_distance(level_detects[i].where)); 896 } 897 ret = 0; 898 break; 899 } 900 901 if (ret) { 902 if (!rn2(100)) /* make them nervous */ 903 You("see the Wizard of Yendor gazing out at you."); 904 else pline_The("vision is unclear."); 905 } 906 } 907 return; 908} 909 910STATIC_OVL void 911show_map_spot(x, y) 912register int x, y; 913{ 914 register struct rm *lev; 915 916 if (Confusion && rn2(7)) return; 917 lev = &levl[x][y]; 918 919 lev->seenv = SVALL; 920 921 /* Secret corridors are found, but not secret doors. */ 922 if (lev->typ == SCORR) { 923 lev->typ = CORR; 924 unblock_point(x,y); 925 } 926 927 /* if we don't remember an object or trap there, map it */ 928 if (lev->typ == ROOM ? 929 (glyph_is_cmap(lev->glyph) && !glyph_is_trap(lev->glyph) && 930 glyph_to_cmap(lev->glyph) != ROOM) : 931 (!glyph_is_object(lev->glyph) && !glyph_is_trap(lev->glyph))) { 932 if (level.flags.hero_memory) { 933 magic_map_background(x,y,0); 934 newsym(x,y); /* show it, if not blocked */ 935 } else { 936 magic_map_background(x,y,1); /* display it */ 937 } 938 } 939} 940 941void 942do_mapping() 943{ 944 register int zx, zy; 945 int uw = u.uinwater; 946 947 u.uinwater = 0; 948 for (zx = 1; zx < COLNO; zx++) 949 for (zy = 0; zy < ROWNO; zy++) 950 show_map_spot(zx, zy); 951 exercise(A_WIS, TRUE); 952 u.uinwater = uw; 953 if (!level.flags.hero_memory || Underwater) { 954 flush_screen(1); /* flush temp screen */ 955 display_nhwindow(WIN_MAP, TRUE); /* wait */ 956 docrt(); 957 } 958} 959 960void 961do_vicinity_map() 962{ 963 register int zx, zy; 964 int lo_y = (u.uy-5 < 0 ? 0 : u.uy-5), 965 hi_y = (u.uy+6 > ROWNO ? ROWNO : u.uy+6), 966 lo_x = (u.ux-9 < 1 ? 1 : u.ux-9), /* avoid column 0 */ 967 hi_x = (u.ux+10 > COLNO ? COLNO : u.ux+10); 968 969 for (zx = lo_x; zx < hi_x; zx++) 970 for (zy = lo_y; zy < hi_y; zy++) 971 show_map_spot(zx, zy); 972 973 if (!level.flags.hero_memory || Underwater) { 974 flush_screen(1); /* flush temp screen */ 975 display_nhwindow(WIN_MAP, TRUE); /* wait */ 976 docrt(); 977 } 978} 979 980/* convert a secret door into a normal door */ 981void 982cvt_sdoor_to_door(lev) 983struct rm *lev; 984{ 985 int newmask = lev->doormask & ~WM_MASK; 986 987#ifdef REINCARNATION 988 if (Is_rogue_level(&u.uz)) 989 /* rogue didn't have doors, only doorways */ 990 newmask = D_NODOOR; 991 else 992#endif 993 /* newly exposed door is closed */ 994 if (!(newmask & D_LOCKED)) newmask |= D_CLOSED; 995 996 lev->typ = DOOR; 997 lev->doormask = newmask; 998} 999 1000 1001STATIC_PTR void 1002findone(zx,zy,num) 1003int zx,zy; 1004genericptr_t num; 1005{ 1006 register struct trap *ttmp; 1007 register struct monst *mtmp; 1008 1009 if(levl[zx][zy].typ == SDOOR) { 1010 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */ 1011 magic_map_background(zx, zy, 0); 1012 newsym(zx, zy); 1013 (*(int*)num)++; 1014 } else if(levl[zx][zy].typ == SCORR) { 1015 levl[zx][zy].typ = CORR; 1016 unblock_point(zx,zy); 1017 magic_map_background(zx, zy, 0); 1018 newsym(zx, zy); 1019 (*(int*)num)++; 1020 } else if ((ttmp = t_at(zx, zy)) != 0) { 1021 if(!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) { 1022 ttmp->tseen = 1; 1023 newsym(zx,zy); 1024 (*(int*)num)++; 1025 } 1026 } else if ((mtmp = m_at(zx, zy)) != 0) { 1027 if(mtmp->m_ap_type) { 1028 seemimic(mtmp); 1029 (*(int*)num)++; 1030 } 1031 if (mtmp->mundetected && 1032 (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) { 1033 mtmp->mundetected = 0; 1034 newsym(zx, zy); 1035 (*(int*)num)++; 1036 } 1037 if (!canspotmon(mtmp) && 1038 !glyph_is_invisible(levl[zx][zy].glyph)) 1039 map_invisible(zx, zy); 1040 } else if (glyph_is_invisible(levl[zx][zy].glyph)) { 1041 unmap_object(zx, zy); 1042 newsym(zx, zy); 1043 (*(int*)num)++; 1044 } 1045} 1046 1047STATIC_PTR void 1048openone(zx,zy,num) 1049int zx,zy; 1050genericptr_t num; 1051{ 1052 register struct trap *ttmp; 1053 register struct obj *otmp; 1054 1055 if(OBJ_AT(zx, zy)) { 1056 for(otmp = level.objects[zx][zy]; 1057 otmp; otmp = otmp->nexthere) { 1058 if(Is_box(otmp) && otmp->olocked) { 1059 otmp->olocked = 0; 1060 (*(int*)num)++; 1061 } 1062 } 1063 /* let it fall to the next cases. could be on trap. */ 1064 } 1065 if(levl[zx][zy].typ == SDOOR || (levl[zx][zy].typ == DOOR && 1066 (levl[zx][zy].doormask & (D_CLOSED|D_LOCKED)))) { 1067 if(levl[zx][zy].typ == SDOOR) 1068 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */ 1069 if(levl[zx][zy].doormask & D_TRAPPED) { 1070 if(distu(zx, zy) < 3) b_trapped("door", 0); 1071 else Norep("You %s an explosion!", 1072 cansee(zx, zy) ? "see" : 1073 (flags.soundok ? "hear" : 1074 "feel the shock of")); 1075 wake_nearto(zx, zy, 11*11); 1076 levl[zx][zy].doormask = D_NODOOR; 1077 } else 1078 levl[zx][zy].doormask = D_ISOPEN; 1079 unblock_point(zx, zy); 1080 newsym(zx, zy); 1081 (*(int*)num)++; 1082 } else if(levl[zx][zy].typ == SCORR) { 1083 levl[zx][zy].typ = CORR; 1084 unblock_point(zx, zy); 1085 newsym(zx, zy); 1086 (*(int*)num)++; 1087 } else if ((ttmp = t_at(zx, zy)) != 0) { 1088 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) { 1089 ttmp->tseen = 1; 1090 newsym(zx,zy); 1091 (*(int*)num)++; 1092 } 1093 } else if (find_drawbridge(&zx, &zy)) { 1094 /* make sure it isn't an open drawbridge */ 1095 open_drawbridge(zx, zy); 1096 (*(int*)num)++; 1097 } 1098} 1099 1100int 1101findit() /* returns number of things found */ 1102{ 1103 int num = 0; 1104 1105 if(u.uswallow) return(0); 1106 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num); 1107 return(num); 1108} 1109 1110int 1111openit() /* returns number of things found and opened */ 1112{ 1113 int num = 0; 1114 1115 if(u.uswallow) { 1116 if (is_animal(u.ustuck->data)) { 1117 if (Blind) pline("Its mouth opens!"); 1118 else pline("%s opens its mouth!", Monnam(u.ustuck)); 1119 } 1120 expels(u.ustuck, u.ustuck->data, TRUE); 1121 return(-1); 1122 } 1123 1124 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num); 1125 return(num); 1126} 1127 1128void 1129find_trap(trap) 1130struct trap *trap; 1131{ 1132 int tt = what_trap(trap->ttyp); 1133 boolean cleared = FALSE; 1134 1135 trap->tseen = 1; 1136 exercise(A_WIS, TRUE); 1137 if (Blind) 1138 feel_location(trap->tx, trap->ty); 1139 else 1140 newsym(trap->tx, trap->ty); 1141 1142 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) { 1143 /* There's too much clutter to see your find otherwise */ 1144 cls(); 1145 map_trap(trap, 1); 1146 display_self(); 1147 cleared = TRUE; 1148 } 1149 1150 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation)); 1151 1152 if (cleared) { 1153 display_nhwindow(WIN_MAP, TRUE); /* wait */ 1154 docrt(); 1155 } 1156} 1157 1158int 1159dosearch0(aflag) 1160register int aflag; 1161{ 1162#ifdef GCC_BUG 1163/* some versions of gcc seriously muck up nested loops. if you get strange 1164 crashes while searching in a version compiled with gcc, try putting 1165 #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the 1166 makefile). 1167 */ 1168 volatile xchar x, y; 1169#else 1170 register xchar x, y; 1171#endif 1172 register struct trap *trap; 1173 register struct monst *mtmp; 1174 1175 if(u.uswallow) { 1176 if (!aflag) 1177 pline("What are you looking for? The exit?"); 1178 } else { 1179 int fund = (uwep && uwep->oartifact && 1180 spec_ability(uwep, SPFX_SEARCH)) ? 1181 uwep->spe : 0; 1182 if (ublindf && ublindf->otyp == LENSES && !Blind) 1183 fund += 2; /* JDS: lenses help searching */ 1184 if (fund > 5) fund = 5; 1185 for(x = u.ux-1; x < u.ux+2; x++) 1186 for(y = u.uy-1; y < u.uy+2; y++) { 1187 if(!isok(x,y)) continue; 1188 if(x != u.ux || y != u.uy) { 1189 if (Blind && !aflag) feel_location(x,y); 1190 if(levl[x][y].typ == SDOOR) { 1191 if(rnl(7-fund)) continue; 1192 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */ 1193 exercise(A_WIS, TRUE); 1194 nomul(0); 1195 if (Blind && !aflag) 1196 feel_location(x,y); /* make sure it shows up */ 1197 else 1198 newsym(x,y); 1199 } else if(levl[x][y].typ == SCORR) { 1200 if(rnl(7-fund)) continue; 1201 levl[x][y].typ = CORR; 1202 unblock_point(x,y); /* vision */ 1203 exercise(A_WIS, TRUE); 1204 nomul(0); 1205 newsym(x,y); 1206 } else { 1207 /* Be careful not to find anything in an SCORR or SDOOR */ 1208 if((mtmp = m_at(x, y)) && !aflag) { 1209 if(mtmp->m_ap_type) { 1210 seemimic(mtmp); 1211 find: exercise(A_WIS, TRUE); 1212 if (!canspotmon(mtmp)) { 1213 if (glyph_is_invisible(levl[x][y].glyph)) { 1214 /* found invisible monster in a square 1215 * which already has an 'I' in it. 1216 * Logically, this should still take 1217 * time and lead to a return(1), but if 1218 * we did that the player would keep 1219 * finding the same monster every turn. 1220 */ 1221 continue; 1222 } else { 1223 You_feel("an unseen monster!"); 1224 map_invisible(x, y); 1225 } 1226 } else if (!sensemon(mtmp)) 1227 You("find %s.", a_monnam(mtmp)); 1228 return(1); 1229 } 1230 if(!canspotmon(mtmp)) { 1231 if (mtmp->mundetected && 1232 (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) 1233 mtmp->mundetected = 0; 1234 newsym(x,y); 1235 goto find; 1236 } 1237 } 1238 1239 /* see if an invisible monster has moved--if Blind, 1240 * feel_location() already did it 1241 */ 1242 if (!aflag && !mtmp && !Blind && 1243 glyph_is_invisible(levl[x][y].glyph)) { 1244 unmap_object(x,y); 1245 newsym(x,y); 1246 } 1247 1248 if ((trap = t_at(x,y)) && !trap->tseen && !rnl(8)) { 1249 nomul(0); 1250 1251 if (trap->ttyp == STATUE_TRAP) { 1252 if (activate_statue_trap(trap, x, y, FALSE)) 1253 exercise(A_WIS, TRUE); 1254 return(1); 1255 } else { 1256 find_trap(trap); 1257 } 1258 } 1259 } 1260 } 1261 } 1262 } 1263 return(1); 1264} 1265 1266int 1267dosearch() 1268{ 1269 return(dosearch0(0)); 1270} 1271 1272/* Pre-map the sokoban levels */ 1273void 1274sokoban_detect() 1275{ 1276 register int x, y; 1277 register struct trap *ttmp; 1278 register struct obj *obj; 1279 1280 /* Map the background and boulders */ 1281 for (x = 1; x < COLNO; x++) 1282 for (y = 0; y < ROWNO; y++) { 1283 levl[x][y].seenv = SVALL; 1284 levl[x][y].waslit = TRUE; 1285 map_background(x, y, 1); 1286 for (obj = level.objects[x][y]; obj; obj = obj->nexthere) 1287 if (obj->otyp == BOULDER) 1288 map_object(obj, 1); 1289 } 1290 1291 /* Map the traps */ 1292 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { 1293 ttmp->tseen = 1; 1294 map_trap(ttmp, 1); 1295 } 1296} 1297 1298 1299/*detect.c*/ 1300