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