1 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 /* hack.zap.c - version 1.0.3 */ 3 /* $FreeBSD: src/games/hack/hack.zap.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */ 4 /* $DragonFly: src/games/hack/hack.zap.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */ 5 6 #include "hack.h" 7 8 extern struct monst youmonst; 9 10 static const char *fl[]= { 11 "magic missile", 12 "bolt of fire", 13 "sleep ray", 14 "bolt of cold", 15 "death ray" 16 }; 17 18 static void bhitm(struct monst *, struct obj *); 19 static bool bhito(struct obj *, struct obj *); 20 static char dirlet(int, int); 21 static int zhit(struct monst *, int); 22 static bool revive(struct obj *); 23 static void rloco(struct obj *); 24 static void burn_scrolls(void); 25 26 /* Routines for IMMEDIATE wands. */ 27 /* bhitm: monster mtmp was hit by the effect of wand otmp */ 28 static void 29 bhitm(struct monst *mtmp, struct obj *otmp) 30 { 31 wakeup(mtmp); 32 switch (otmp->otyp) { 33 case WAN_STRIKING: 34 if (u.uswallow || rnd(20) < 10 + mtmp->data->ac) { 35 int tmp = d(2, 12); 36 hit("wand", mtmp, exclam(tmp)); 37 mtmp->mhp -= tmp; 38 if (mtmp->mhp < 1) 39 killed(mtmp); 40 } else 41 miss("wand", mtmp); 42 break; 43 case WAN_SLOW_MONSTER: 44 mtmp->mspeed = MSLOW; 45 break; 46 case WAN_SPEED_MONSTER: 47 mtmp->mspeed = MFAST; 48 break; 49 case WAN_UNDEAD_TURNING: 50 if (strchr(UNDEAD, mtmp->data->mlet)) { 51 mtmp->mhp -= rnd(8); 52 if (mtmp->mhp < 1) 53 killed(mtmp); 54 else 55 mtmp->mflee = 1; 56 } 57 break; 58 case WAN_POLYMORPH: 59 if (newcham(mtmp, &mons[rn2(CMNUM)])) 60 objects[otmp->otyp].oc_name_known = 1; 61 break; 62 case WAN_CANCELLATION: 63 mtmp->mcan = 1; 64 break; 65 case WAN_TELEPORTATION: 66 rloc(mtmp); 67 break; 68 case WAN_MAKE_INVISIBLE: 69 mtmp->minvis = 1; 70 break; 71 #ifdef WAN_PROBING 72 case WAN_PROBING: 73 mstatusline(mtmp); 74 break; 75 #endif /* WAN_PROBING */ 76 default: 77 impossible("What an interesting wand (%u)", otmp->otyp); 78 } 79 } 80 81 /* 82 * object obj was hit by the effect of wand otmp 83 * returns TRUE if sth was done 84 */ 85 static bool 86 bhito(struct obj *obj, struct obj *otmp) 87 { 88 int res = TRUE; 89 90 if (obj == uball || obj == uchain) 91 res = FALSE; 92 else 93 switch (otmp->otyp) { 94 case WAN_POLYMORPH: 95 /* preserve symbol and quantity, but turn rocks into gems */ 96 mkobj_at((obj->otyp == ROCK || obj->otyp == ENORMOUS_ROCK) 97 ? GEM_SYM : obj->olet, 98 obj->ox, obj->oy)->quan = obj->quan; 99 delobj(obj); 100 break; 101 case WAN_STRIKING: 102 if (obj->otyp == ENORMOUS_ROCK) 103 fracture_rock(obj); 104 else 105 res = FALSE; 106 break; 107 case WAN_CANCELLATION: 108 if (obj->spe && obj->olet != AMULET_SYM) { 109 obj->known = 0; 110 obj->spe = 0; 111 } 112 break; 113 case WAN_TELEPORTATION: 114 rloco(obj); 115 break; 116 case WAN_MAKE_INVISIBLE: 117 obj->oinvis = 1; 118 break; 119 case WAN_UNDEAD_TURNING: 120 res = revive(obj); 121 break; 122 case WAN_SLOW_MONSTER: /* no effect on objects */ 123 case WAN_SPEED_MONSTER: 124 #ifdef WAN_PROBING 125 case WAN_PROBING: 126 #endif /* WAN_PROBING */ 127 res = FALSE; 128 break; 129 default: 130 impossible("What an interesting wand (%u)", otmp->otyp); 131 } 132 return (res); 133 } 134 135 int 136 dozap(void) 137 { 138 struct obj *obj; 139 xchar zx, zy; 140 141 obj = getobj("/", "zap"); 142 if (!obj) 143 return (0); 144 if (obj->spe < 0 || (obj->spe == 0 && rn2(121))) { 145 pline("Nothing Happens."); 146 return (1); 147 } 148 if (obj->spe == 0) 149 pline("You wrest one more spell from the worn-out wand."); 150 if (!(objects[obj->otyp].bits & NODIR) && !getdir(1)) 151 return (1); /* make him pay for knowing !NODIR */ 152 obj->spe--; 153 if (objects[obj->otyp].bits & IMMEDIATE) { 154 if (u.uswallow) 155 bhitm(u.ustuck, obj); 156 else if (u.dz) { 157 if (u.dz > 0) { 158 struct obj *otmp = o_at(u.ux, u.uy); 159 if (otmp) 160 bhito(otmp, obj); 161 } 162 } else 163 bhit(u.dx, u.dy, rn1(8, 6), 0, bhitm, bhito, obj); 164 } else { 165 switch (obj->otyp) { 166 case WAN_LIGHT: 167 litroom(TRUE); 168 break; 169 case WAN_SECRET_DOOR_DETECTION: 170 if (!findit()) 171 return (1); 172 break; 173 case WAN_CREATE_MONSTER: 174 { 175 int cnt = 1; 176 if (!rn2(23)) 177 cnt += rn2(7) + 1; 178 while (cnt--) 179 makemon(NULL, u.ux, u.uy); 180 } 181 break; 182 case WAN_WISHING: 183 { 184 char buf[BUFSZ]; 185 struct obj *otmp; 186 if (u.uluck + rn2(5) < 0) { 187 pline("Unfortunately, nothing happens."); 188 break; 189 } 190 pline("You may wish for an object. What do you want? "); 191 getlin(buf); 192 if (buf[0] == '\033') 193 buf[0] = 0; 194 otmp = readobjnam(buf); 195 otmp = addinv(otmp); 196 prinv(otmp); 197 break; 198 } 199 case WAN_DIGGING: 200 /* 201 * Original effect (approximately): 202 * from CORR: dig until we pierce a wall 203 * from ROOM: piece wall and dig until we reach 204 * an ACCESSIBLE place. 205 * Currently: dig for digdepth positions; 206 * also down on request of Lennart Augustsson. 207 */ 208 { 209 struct rm *room; 210 int digdepth; 211 if (u.uswallow) { 212 struct monst *mtmp = u.ustuck; 213 214 pline("You pierce %s's stomach wall!", 215 monnam(mtmp)); 216 mtmp->mhp = 1; /* almost dead */ 217 unstuck(mtmp); 218 mnexto(mtmp); 219 break; 220 } 221 if (u.dz) { 222 if (u.dz < 0) { 223 pline("You loosen a rock from the ceiling."); 224 pline("It falls on your head!"); 225 losehp(1, "falling rock"); 226 mksobj_at(ROCK, u.ux, u.uy); 227 fobj->quan = 1; 228 stackobj(fobj); 229 if (Invisible) 230 newsym(u.ux, u.uy); 231 } else 232 dighole(); 233 break; 234 } 235 zx = u.ux + u.dx; 236 zy = u.uy + u.dy; 237 digdepth = 8 + rn2(18); 238 Tmp_at(-1, '*'); /* open call */ 239 while (--digdepth >= 0) { 240 if (!isok(zx, zy)) 241 break; 242 room = &levl[zx][zy]; 243 Tmp_at(zx, zy); 244 if (!xdnstair) { 245 if (zx < 3 || zx > COLNO - 3 || 246 zy < 3 || zy > ROWNO - 3) 247 break; 248 if (room->typ == HWALL || 249 room->typ == VWALL) { 250 room->typ = ROOM; 251 break; 252 } 253 } else if (room->typ == HWALL || room->typ == VWALL || 254 room->typ == SDOOR || room->typ == LDOOR) { 255 room->typ = DOOR; 256 digdepth -= 2; 257 } else if (room->typ == SCORR || !room->typ) { 258 room->typ = CORR; 259 digdepth--; 260 } 261 mnewsym(zx, zy); 262 zx += u.dx; 263 zy += u.dy; 264 } 265 mnewsym(zx, zy); /* not always necessary */ 266 Tmp_at(-1, -1); /* closing call */ 267 break; 268 } 269 default: 270 buzz((int)obj->otyp - WAN_MAGIC_MISSILE, 271 u.ux, u.uy, u.dx, u.dy); 272 break; 273 } 274 if (!objects[obj->otyp].oc_name_known) { 275 objects[obj->otyp].oc_name_known = 1; 276 more_experienced(0, 10); 277 } 278 } 279 return (1); 280 } 281 282 const char * 283 exclam(int force) 284 { 285 /* force == 0 occurs e.g. with sleep ray */ 286 /* note that large force is usual with wands so that !! would 287 require information about hand/weapon/wand */ 288 return ((force < 0) ? "?" : (force <= 4) ? "." : "!"); 289 } 290 291 void 292 hit(const char *str, struct monst *mtmp, const char *force) 293 { 294 /* force: usually either "." or "!" */ 295 if (!cansee(mtmp->mx, mtmp->my)) 296 pline("The %s hits it.", str); 297 else 298 pline("The %s hits %s%s", str, monnam(mtmp), force); 299 } 300 301 void 302 miss(const char *str, struct monst *mtmp) 303 { 304 if (!cansee(mtmp->mx, mtmp->my)) 305 pline("The %s misses it.", str); 306 else 307 pline("The %s misses %s.", str, monnam(mtmp)); 308 } 309 310 /* 311 * bhit: called when a weapon is thrown (sym = obj->olet) or when an 312 * IMMEDIATE wand is zapped (sym = 0); the weapon falls down at end of range 313 * or when a monster is hit; the monster is returned, and bhitpos is set to 314 * the final position of the weapon thrown; the ray of a wand may affect 315 * several objects and monsters on its path - for each of these an argument 316 * function is called. 317 */ 318 /* check !u.uswallow before calling bhit() */ 319 320 /* ddx, ddy, range: direction and range 321 * sym: symbol displayed on path 322 * fhitm, fhito: fns called when mon/obj hit 323 * obj: 2nd arg to fhitm/fhito 324 */ 325 struct monst * 326 bhit(int ddx, int ddy, int range, char sym, 327 void (*fhitm)(struct monst *, struct obj *), 328 bool (*fhito)(struct obj *, struct obj *), struct obj *obj) 329 { 330 struct monst *mtmp; 331 struct obj *otmp; 332 int typ; 333 334 bhitpos.x = u.ux; 335 bhitpos.y = u.uy; 336 337 if (sym) /* open call */ 338 tmp_at(-1, sym); 339 while (range-- > 0) { 340 bhitpos.x += ddx; 341 bhitpos.y += ddy; 342 typ = levl[bhitpos.x][bhitpos.y].typ; 343 if ((mtmp = m_at(bhitpos.x, bhitpos.y))) { 344 if (sym) { 345 tmp_at(-1, -1); /* close call */ 346 return (mtmp); 347 } 348 (*fhitm)(mtmp, obj); 349 range -= 3; 350 } 351 if (fhito && (otmp = o_at(bhitpos.x, bhitpos.y))) { 352 if ((*fhito)(otmp, obj)) 353 range--; 354 } 355 if (!ZAP_POS(typ)) { 356 bhitpos.x -= ddx; 357 bhitpos.y -= ddy; 358 break; 359 } 360 if (sym) 361 tmp_at(bhitpos.x, bhitpos.y); 362 } 363 364 /* leave last symbol unless in a pool */ 365 if (sym) 366 tmp_at(-1, (levl[bhitpos.x][bhitpos.y].typ == POOL) ? -1 : 0); 367 return (0); 368 } 369 370 struct monst * 371 boomhit(int dx, int dy) 372 { 373 int i, ct; 374 struct monst *mtmp; 375 char sym = ')'; 376 377 bhitpos.x = u.ux; 378 bhitpos.y = u.uy; 379 380 for (i = 0; i < 8; i++) 381 if (xdir[i] == dx && ydir[i] == dy) 382 break; 383 tmp_at(-1, sym); /* open call */ 384 for (ct = 0; ct < 10; ct++) { 385 if (i == 8) 386 i = 0; 387 sym = ')' + '(' - sym; 388 tmp_at(-2, sym); /* change let call */ 389 dx = xdir[i]; 390 dy = ydir[i]; 391 bhitpos.x += dx; 392 bhitpos.y += dy; 393 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != NULL) { 394 tmp_at(-1, -1); 395 return (mtmp); 396 } 397 if (!ZAP_POS(levl[bhitpos.x][bhitpos.y].typ)) { 398 bhitpos.x -= dx; 399 bhitpos.y -= dy; 400 break; 401 } 402 if (bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */ 403 if (rn2(20) >= 10 + u.ulevel) { /* we hit ourselves */ 404 thitu(10, rnd(10), "boomerang"); 405 break; 406 } else { /* we catch it */ 407 tmp_at(-1, -1); 408 pline("Skillfully, you catch the boomerang."); 409 return (&youmonst); 410 } 411 } 412 tmp_at(bhitpos.x, bhitpos.y); 413 if (ct % 5 != 0) 414 i++; 415 } 416 tmp_at(-1, -1); /* do not leave last symbol */ 417 return (0); 418 } 419 420 static char 421 dirlet(int dx, int dy) 422 { 423 return 424 (dx == dy) ? '\\' : (dx && dy) ? '/' : dx ? '-' : '|'; 425 } 426 427 /* type == -1: monster spitting fire at you */ 428 /* type == -1,-2,-3: bolts sent out by wizard */ 429 /* called with dx = dy = 0 with vertical bolts */ 430 void 431 buzz(int type, xchar sx, xchar sy, int dx, int dy) 432 { 433 int abstype = abs(type); 434 const char *fltxt = (type == -1) ? "blaze of fire" : fl[abstype]; 435 struct rm *lev; 436 xchar range; 437 struct monst *mon; 438 439 if (u.uswallow) { 440 int tmp; 441 442 if (type < 0) 443 return; 444 tmp = zhit(u.ustuck, type); 445 pline("The %s rips into %s%s", 446 fltxt, monnam(u.ustuck), exclam(tmp)); 447 return; 448 } 449 if (type < 0) 450 pru(); 451 range = rn1(7, 7); 452 Tmp_at(-1, dirlet(dx, dy)); /* open call */ 453 while (range-- > 0) { 454 sx += dx; 455 sy += dy; 456 if ((lev = &levl[sx][sy])->typ) 457 Tmp_at(sx, sy); 458 else { 459 int bounce = 0; 460 if (cansee(sx - dx, sy - dy)) 461 pline("The %s bounces!", fltxt); 462 if (ZAP_POS(levl[sx][sy - dy].typ)) 463 bounce = 1; 464 if (ZAP_POS(levl[sx - dx][sy].typ)) { 465 if (!bounce || rn2(2)) 466 bounce = 2; 467 } 468 switch (bounce) { 469 case 0: 470 dx = -dx; 471 dy = -dy; 472 continue; 473 case 1: 474 dy = -dy; 475 sx -= dx; 476 break; 477 case 2: 478 dx = -dx; 479 sy -= dy; 480 break; 481 } 482 Tmp_at(-2, dirlet(dx, dy)); 483 continue; 484 } 485 if (lev->typ == POOL && abstype == 1 /* fire */) { 486 range -= 3; 487 lev->typ = ROOM; 488 if (cansee(sx, sy)) { 489 mnewsym(sx, sy); 490 pline("The water evaporates."); 491 } else 492 pline("You hear a hissing sound."); 493 } 494 if ((mon = m_at(sx, sy)) && 495 (type != -1 || mon->data->mlet != 'D')) { 496 wakeup(mon); 497 if (rnd(20) < 18 + mon->data->ac) { 498 int tmp = zhit(mon, abstype); 499 if (mon->mhp < 1) { 500 if (type < 0) { 501 if (cansee(mon->mx, mon->my)) 502 pline("%s is killed by the %s!", 503 Monnam(mon), fltxt); 504 mondied(mon); 505 } else 506 killed(mon); 507 } else 508 hit(fltxt, mon, exclam(tmp)); 509 range -= 2; 510 } else 511 miss(fltxt, mon); 512 } else if (sx == u.ux && sy == u.uy) { 513 nomul(0); 514 if (rnd(20) < 18 + u.uac) { 515 int dam = 0; 516 range -= 2; 517 pline("The %s hits you!", fltxt); 518 switch (abstype) { 519 case 0: 520 dam = d(2, 6); 521 break; 522 case 1: 523 if (Fire_resistance) 524 pline("You don't feel hot!"); 525 else 526 dam = d(6, 6); 527 if (!rn2(3)) 528 burn_scrolls(); 529 break; 530 case 2: 531 nomul(-rnd(25)); /* sleep ray */ 532 break; 533 case 3: 534 if (Cold_resistance) 535 pline("You don't feel cold!"); 536 else 537 dam = d(6, 6); 538 break; 539 case 4: 540 u.uhp = -1; 541 } 542 losehp(dam, fltxt); 543 } else 544 pline("The %s whizzes by you!", fltxt); 545 stop_occupation(); 546 } 547 if (!ZAP_POS(lev->typ)) { 548 int bounce = 0, rmn; 549 if (cansee(sx, sy)) 550 pline("The %s bounces!", fltxt); 551 range--; 552 if (!dx || !dy || !rn2(20)) { 553 dx = -dx; 554 dy = -dy; 555 } else { 556 if (ZAP_POS(rmn = levl[sx][sy - dy].typ) && 557 (IS_ROOM(rmn) || ZAP_POS(levl[sx + dx][sy - dy].typ))) 558 bounce = 1; 559 if (ZAP_POS(rmn = levl[sx - dx][sy].typ) && 560 (IS_ROOM(rmn) || ZAP_POS(levl[sx - dx][sy + dy].typ))) 561 if (!bounce || rn2(2)) 562 bounce = 2; 563 564 switch (bounce) { 565 case 0: 566 dy = -dy; 567 dx = -dx; 568 break; 569 case 1: 570 dy = -dy; 571 break; 572 case 2: 573 dx = -dx; 574 break; 575 } 576 Tmp_at(-2, dirlet(dx, dy)); 577 } 578 } 579 } 580 Tmp_at(-1, -1); 581 } 582 583 static int 584 zhit(struct monst *mon, int type) /* returns damage to mon */ 585 { 586 int tmp = 0; 587 588 switch (type) { 589 case 0: /* magic missile */ 590 tmp = d(2, 6); 591 break; 592 case -1: /* Dragon blazing fire */ 593 case 1: /* fire */ 594 if (strchr("Dg", mon->data->mlet)) 595 break; 596 tmp = d(6, 6); 597 if (strchr("YF", mon->data->mlet)) 598 tmp += 7; 599 break; 600 case 2: /* sleep*/ 601 mon->mfroz = 1; 602 break; 603 case 3: /* cold */ 604 if (strchr("YFgf", mon->data->mlet)) 605 break; 606 tmp = d(6, 6); 607 if (mon->data->mlet == 'D') 608 tmp += 7; 609 break; 610 case 4: /* death*/ 611 if (strchr(UNDEAD, mon->data->mlet)) 612 break; 613 tmp = mon->mhp + 1; 614 break; 615 } 616 mon->mhp -= tmp; 617 return (tmp); 618 } 619 620 #define CORPSE_I_TO_C(otyp) (char) ((otyp >= DEAD_ACID_BLOB)\ 621 ? 'a' + (otyp - DEAD_ACID_BLOB)\ 622 : '@' + (otyp - DEAD_HUMAN)) 623 static bool 624 revive(struct obj *obj) 625 { 626 struct monst *mtmp = NULL; 627 628 if (obj->olet == FOOD_SYM && obj->otyp > CORPSE) { 629 /* do not (yet) revive shopkeepers */ 630 /* Note: this might conceivably produce two monsters 631 * at the same position - strange, but harmless */ 632 mtmp = mkmon_at(CORPSE_I_TO_C(obj->otyp), obj->ox, obj->oy); 633 delobj(obj); 634 } 635 return (!!mtmp); /* TRUE if some monster created */ 636 } 637 638 static void 639 rloco(struct obj *obj) 640 { 641 int tx, ty, otx, oty; 642 643 otx = obj->ox; 644 oty = obj->oy; 645 do { 646 tx = rn1(COLNO - 3, 2); 647 ty = rn2(ROWNO); 648 } while (!goodpos(tx, ty)); 649 obj->ox = tx; 650 obj->oy = ty; 651 if (cansee(otx, oty)) 652 newsym(otx, oty); 653 } 654 655 /* fractured by pick-axe or wand of striking */ 656 /* no texts here! */ 657 void 658 fracture_rock(struct obj *obj) 659 { 660 obj->otyp = ROCK; 661 obj->quan = 7 + rn2(60); 662 obj->owt = weight(obj); 663 obj->olet = WEAPON_SYM; 664 if (cansee(obj->ox, obj->oy)) 665 prl(obj->ox, obj->oy); 666 } 667 668 static void 669 burn_scrolls(void) 670 { 671 struct obj *obj, *obj2; 672 int cnt = 0; 673 674 for (obj = invent; obj; obj = obj2) { 675 obj2 = obj->nobj; 676 if (obj->olet == SCROLL_SYM) { 677 cnt++; 678 useup(obj); 679 } 680 } 681 if (cnt > 1) { 682 pline("Your scrolls catch fire!"); 683 losehp(cnt, "burning scrolls"); 684 } else if (cnt) { 685 pline("Your scroll catches fire!"); 686 losehp(1, "burning scroll"); 687 } 688 } 689