1/* SCCS Id: @(#)explode.c 3.4 2002/11/10 */ 2/* Copyright (C) 1990 by Ken Arromdee */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5#include "hack.h" 6 7#ifdef OVL0 8 9/* Note: Arrays are column first, while the screen is row first */ 10static int expl[3][3] = { 11 { S_explode1, S_explode4, S_explode7 }, 12 { S_explode2, S_explode5, S_explode8 }, 13 { S_explode3, S_explode6, S_explode9 } 14}; 15 16/* Note: I had to choose one of three possible kinds of "type" when writing 17 * this function: a wand type (like in zap.c), an adtyp, or an object type. 18 * Wand types get complex because they must be converted to adtyps for 19 * determining such things as fire resistance. Adtyps get complex in that 20 * they don't supply enough information--was it a player or a monster that 21 * did it, and with a wand, spell, or breath weapon? Object types share both 22 * these disadvantages.... 23 */ 24void 25explode(x, y, type, dam, olet, expltype) 26int x, y; 27int type; /* the same as in zap.c */ 28int dam; 29char olet; 30int expltype; 31{ 32 int i, j, k, damu = dam; 33 boolean starting = 1; 34 boolean visible, any_shield; 35 int uhurt = 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */ 36 const char *str; 37 int idamres, idamnonres; 38 struct monst *mtmp; 39 uchar adtyp; 40 int explmask[3][3]; 41 /* 0=normal explosion, 1=do shieldeff, 2=do nothing */ 42 boolean shopdamage = FALSE; 43 boolean generic = FALSE; 44 45 if (olet == WAND_CLASS) /* retributive strike */ 46 switch (Role_switch) { 47 case PM_PRIEST: 48 case PM_MONK: 49 case PM_WIZARD: damu /= 5; 50 break; 51 case PM_HEALER: 52 case PM_KNIGHT: damu /= 2; 53 break; 54 default: break; 55 } 56 57 if (olet == MON_EXPLODE) { 58 str = killer; 59 killer = 0; /* set again later as needed */ 60 adtyp = AD_PHYS; 61 } else 62 switch (abs(type) % 10) { 63 case 0: str = "magical blast"; 64 adtyp = AD_MAGM; 65 break; 66 case 1: str = olet == BURNING_OIL ? "burning oil" : 67 olet == SCROLL_CLASS ? "tower of flame" : 68 "fireball"; 69 adtyp = AD_FIRE; 70 break; 71 case 2: str = "ball of cold"; 72 adtyp = AD_COLD; 73 break; 74 case 4: str = (olet == WAND_CLASS) ? "death field" : 75 "disintegration field"; 76 adtyp = AD_DISN; 77 break; 78 case 5: str = "ball of lightning"; 79 adtyp = AD_ELEC; 80 break; 81 case 6: str = "poison gas cloud"; 82 adtyp = AD_DRST; 83 break; 84 case 7: str = "splash of acid"; 85 adtyp = AD_ACID; 86 break; 87 default: impossible("explosion base type %d?", type); return; 88 } 89 90 any_shield = visible = FALSE; 91 for (i=0; i<3; i++) for (j=0; j<3; j++) { 92 if (!isok(i+x-1, j+y-1)) { 93 explmask[i][j] = 2; 94 continue; 95 } else 96 explmask[i][j] = 0; 97 98 if (i+x-1 == u.ux && j+y-1 == u.uy) { 99 switch(adtyp) { 100 case AD_PHYS: 101 explmask[i][j] = 0; 102 break; 103 case AD_MAGM: 104 explmask[i][j] = !!Antimagic; 105 break; 106 case AD_FIRE: 107 explmask[i][j] = !!Fire_resistance; 108 break; 109 case AD_COLD: 110 explmask[i][j] = !!Cold_resistance; 111 break; 112 case AD_DISN: 113 explmask[i][j] = (olet == WAND_CLASS) ? 114 !!(nonliving(youmonst.data) || is_demon(youmonst.data)) : 115 !!Disint_resistance; 116 break; 117 case AD_ELEC: 118 explmask[i][j] = !!Shock_resistance; 119 break; 120 case AD_DRST: 121 explmask[i][j] = !!Poison_resistance; 122 break; 123 case AD_ACID: 124 explmask[i][j] = !!Acid_resistance; 125 break; 126 default: 127 impossible("explosion type %d?", adtyp); 128 break; 129 } 130 } 131 /* can be both you and mtmp if you're swallowed */ 132 mtmp = m_at(i+x-1, j+y-1); 133#ifdef STEED 134 if (!mtmp && i+x-1 == u.ux && j+y-1 == u.uy) 135 mtmp = u.usteed; 136#endif 137 if (mtmp) { 138 if (mtmp->mhp < 1) explmask[i][j] = 2; 139 else switch(adtyp) { 140 case AD_PHYS: 141 break; 142 case AD_MAGM: 143 explmask[i][j] |= resists_magm(mtmp); 144 break; 145 case AD_FIRE: 146 explmask[i][j] |= resists_fire(mtmp); 147 break; 148 case AD_COLD: 149 explmask[i][j] |= resists_cold(mtmp); 150 break; 151 case AD_DISN: 152 explmask[i][j] |= (olet == WAND_CLASS) ? 153 (nonliving(mtmp->data) || is_demon(mtmp->data)) : 154 resists_disint(mtmp); 155 break; 156 case AD_ELEC: 157 explmask[i][j] |= resists_elec(mtmp); 158 break; 159 case AD_DRST: 160 explmask[i][j] |= resists_poison(mtmp); 161 break; 162 case AD_ACID: 163 explmask[i][j] |= resists_acid(mtmp); 164 break; 165 default: 166 impossible("explosion type %d?", adtyp); 167 break; 168 } 169 } 170 if (mtmp && cansee(i+x-1,j+y-1) && !canspotmon(mtmp)) 171 map_invisible(i+x-1, j+y-1); 172 else if (!mtmp && glyph_is_invisible(levl[i+x-1][j+y-1].glyph)) { 173 unmap_object(i+x-1, j+y-1); 174 newsym(i+x-1, j+y-1); 175 } 176 if (cansee(i+x-1, j+y-1)) visible = TRUE; 177 if (explmask[i][j] == 1) any_shield = TRUE; 178 } 179 180 if (visible) { 181 /* Start the explosion */ 182 for (i=0; i<3; i++) for (j=0; j<3; j++) { 183 if (explmask[i][j] == 2) continue; 184 tmp_at(starting ? DISP_BEAM : DISP_CHANGE, 185 explosion_to_glyph(expltype,expl[i][j])); 186 tmp_at(i+x-1, j+y-1); 187 starting = 0; 188 } 189 curs_on_u(); /* will flush screen and output */ 190 191 if (any_shield && flags.sparkle) { /* simulate shield effect */ 192 for (k = 0; k < SHIELD_COUNT; k++) { 193 for (i=0; i<3; i++) for (j=0; j<3; j++) { 194 if (explmask[i][j] == 1) 195 /* 196 * Bypass tmp_at() and send the shield glyphs 197 * directly to the buffered screen. tmp_at() 198 * will clean up the location for us later. 199 */ 200 show_glyph(i+x-1, j+y-1, 201 cmap_to_glyph(shield_static[k])); 202 } 203 curs_on_u(); /* will flush screen and output */ 204 delay_output(); 205 } 206 207 /* Cover last shield glyph with blast symbol. */ 208 for (i=0; i<3; i++) for (j=0; j<3; j++) { 209 if (explmask[i][j] == 1) 210 show_glyph(i+x-1,j+y-1, 211 explosion_to_glyph(expltype, expl[i][j])); 212 } 213 214 } else { /* delay a little bit. */ 215 delay_output(); 216 delay_output(); 217 } 218 219 tmp_at(DISP_END, 0); /* clear the explosion */ 220 } else { 221 if (olet == MON_EXPLODE) { 222 str = "explosion"; 223 generic = TRUE; 224 } 225 if (flags.soundok) You_hear("a blast."); 226 } 227 228 if (dam) 229 for (i=0; i<3; i++) for (j=0; j<3; j++) { 230 if (explmask[i][j] == 2) continue; 231 if (i+x-1 == u.ux && j+y-1 == u.uy) 232 uhurt = (explmask[i][j] == 1) ? 1 : 2; 233 idamres = idamnonres = 0; 234 if (type >= 0) 235 (void)zap_over_floor((xchar)(i+x-1), (xchar)(j+y-1), 236 type, &shopdamage); 237 238 mtmp = m_at(i+x-1, j+y-1); 239#ifdef STEED 240 if (!mtmp && i+x-1 == u.ux && j+y-1 == u.uy) 241 mtmp = u.usteed; 242#endif 243 if (!mtmp) continue; 244 if (u.uswallow && mtmp == u.ustuck) { 245 if (is_animal(u.ustuck->data)) 246 pline("%s gets %s!", 247 Monnam(u.ustuck), 248 (adtyp == AD_FIRE) ? "heartburn" : 249 (adtyp == AD_COLD) ? "chilly" : 250 (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ? 251 "irradiated by pure energy" : "perforated") : 252 (adtyp == AD_ELEC) ? "shocked" : 253 (adtyp == AD_DRST) ? "poisoned" : 254 (adtyp == AD_ACID) ? "an upset stomach" : 255 "fried"); 256 else 257 pline("%s gets slightly %s!", 258 Monnam(u.ustuck), 259 (adtyp == AD_FIRE) ? "toasted" : 260 (adtyp == AD_COLD) ? "chilly" : 261 (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ? 262 "overwhelmed by pure energy" : "perforated") : 263 (adtyp == AD_ELEC) ? "shocked" : 264 (adtyp == AD_DRST) ? "intoxicated" : 265 (adtyp == AD_ACID) ? "burned" : 266 "fried"); 267 } else if (cansee(i+x-1, j+y-1)) { 268 if(mtmp->m_ap_type) seemimic(mtmp); 269 pline("%s is caught in the %s!", Monnam(mtmp), str); 270 } 271 272 idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int) adtyp); 273 idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int) adtyp); 274 idamnonres += destroy_mitem(mtmp, POTION_CLASS, (int) adtyp); 275 idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int) adtyp); 276 idamnonres += destroy_mitem(mtmp, RING_CLASS, (int) adtyp); 277 278 if (explmask[i][j] == 1) { 279 golemeffects(mtmp, (int) adtyp, dam + idamres); 280 mtmp->mhp -= idamnonres; 281 } else { 282 /* call resist with 0 and do damage manually so 1) we can 283 * get out the message before doing the damage, and 2) we can 284 * call mondied, not killed, if it's not your blast 285 */ 286 int mdam = dam; 287 288 if (resist(mtmp, olet, 0, FALSE)) { 289 if (cansee(i+x-1,j+y-1)) 290 pline("%s resists the %s!", Monnam(mtmp), str); 291 mdam = dam/2; 292 } 293 if (mtmp == u.ustuck) 294 mdam *= 2; 295 if (resists_cold(mtmp) && adtyp == AD_FIRE) 296 mdam *= 2; 297 else if (resists_fire(mtmp) && adtyp == AD_COLD) 298 mdam *= 2; 299 mtmp->mhp -= mdam; 300 mtmp->mhp -= (idamres + idamnonres); 301 } 302 if (mtmp->mhp <= 0) { 303 /* KMH -- Don't blame the player for pets killing gas spores */ 304 if (!flags.mon_moving) killed(mtmp); 305 else monkilled(mtmp, "", (int)adtyp); 306 } else if (!flags.mon_moving) setmangry(mtmp); 307 } 308 309 /* Do your injury last */ 310 if (uhurt) { 311 if ((type >= 0 || adtyp == AD_PHYS) && /* gas spores */ 312 flags.verbose && olet != SCROLL_CLASS) 313 You("are caught in the %s!", str); 314 /* do property damage first, in case we end up leaving bones */ 315 if (adtyp == AD_FIRE) burn_away_slime(); 316 if (Invulnerable) { 317 damu = 0; 318 You("are unharmed!"); 319 } else if (Half_physical_damage && adtyp == AD_PHYS) 320 damu = (damu+1) / 2; 321 if (adtyp == AD_FIRE) (void) burnarmor(&youmonst); 322 destroy_item(SCROLL_CLASS, (int) adtyp); 323 destroy_item(SPBOOK_CLASS, (int) adtyp); 324 destroy_item(POTION_CLASS, (int) adtyp); 325 destroy_item(RING_CLASS, (int) adtyp); 326 destroy_item(WAND_CLASS, (int) adtyp); 327 328 ugolemeffects((int) adtyp, damu); 329 if (uhurt == 2) { 330 if (Upolyd) 331 u.mh -= damu; 332 else 333 u.uhp -= damu; 334 flags.botl = 1; 335 } 336 337 if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) { 338 if (Upolyd) { 339 rehumanize(); 340 } else { 341 if (olet == MON_EXPLODE) { 342 /* killer handled by caller */ 343 if (str != killer_buf && !generic) 344 Strcpy(killer_buf, str); 345 killer_format = KILLED_BY_AN; 346 } else if (type >= 0 && olet != SCROLL_CLASS) { 347 killer_format = NO_KILLER_PREFIX; 348 Sprintf(killer_buf, "caught %sself in %s own %s", 349 uhim(), uhis(), str); 350 } else if (!strncmpi(str,"tower of flame", 8) || 351 !strncmpi(str,"fireball", 8)) { 352 killer_format = KILLED_BY_AN; 353 Strcpy(killer_buf, str); 354 } else { 355 killer_format = KILLED_BY; 356 Strcpy(killer_buf, str); 357 } 358 killer = killer_buf; 359 /* Known BUG: BURNING suppresses corpse in bones data, 360 but done does not handle killer reason correctly */ 361 done((adtyp == AD_FIRE) ? BURNING : DIED); 362 } 363 } 364 exercise(A_STR, FALSE); 365 } 366 367 if (shopdamage) { 368 pay_for_damage(adtyp == AD_FIRE ? "burn away" : 369 adtyp == AD_COLD ? "shatter" : 370 adtyp == AD_DISN ? "disintegrate" : "destroy", 371 FALSE); 372 } 373 374 /* explosions are noisy */ 375 i = dam * dam; 376 if (i < 50) i = 50; /* in case random damage is very small */ 377 wake_nearto(x, y, i); 378} 379#endif /* OVL0 */ 380#ifdef OVL1 381 382struct scatter_chain { 383 struct scatter_chain *next; /* pointer to next scatter item */ 384 struct obj *obj; /* pointer to the object */ 385 xchar ox; /* location of */ 386 xchar oy; /* item */ 387 schar dx; /* direction of */ 388 schar dy; /* travel */ 389 int range; /* range of object */ 390 boolean stopped; /* flag for in-motion/stopped */ 391}; 392 393/* 394 * scflags: 395 * VIS_EFFECTS Add visual effects to display 396 * MAY_HITMON Objects may hit monsters 397 * MAY_HITYOU Objects may hit hero 398 * MAY_HIT Objects may hit you or monsters 399 * MAY_DESTROY Objects may be destroyed at random 400 * MAY_FRACTURE Stone objects can be fractured (statues, boulders) 401 */ 402 403/* returns number of scattered objects */ 404long 405scatter(sx,sy,blastforce,scflags, obj) 406int sx,sy; /* location of objects to scatter */ 407int blastforce; /* force behind the scattering */ 408unsigned int scflags; 409struct obj *obj; /* only scatter this obj */ 410{ 411 register struct obj *otmp; 412 register int tmp; 413 int farthest = 0; 414 uchar typ; 415 long qtmp; 416 boolean used_up; 417 boolean individual_object = obj ? TRUE : FALSE; 418 struct monst *mtmp; 419 struct scatter_chain *stmp, *stmp2 = 0; 420 struct scatter_chain *schain = (struct scatter_chain *)0; 421 long total = 0L; 422 423 while ((otmp = individual_object ? obj : level.objects[sx][sy]) != 0) { 424 if (otmp->quan > 1L) { 425 qtmp = otmp->quan - 1; 426 if (qtmp > LARGEST_INT) qtmp = LARGEST_INT; 427 qtmp = (long)rnd((int)qtmp); 428 otmp = splitobj(otmp, qtmp); 429 } else { 430 obj = (struct obj *)0; /* all used */ 431 } 432 obj_extract_self(otmp); 433 used_up = FALSE; 434 435 /* 9 in 10 chance of fracturing boulders or statues */ 436 if ((scflags & MAY_FRACTURE) 437 && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE)) 438 && rn2(10)) { 439 if (otmp->otyp == BOULDER) { 440 pline("%s apart.", Tobjnam(otmp, "break")); 441 fracture_rock(otmp); 442 place_object(otmp, sx, sy); 443 if ((otmp = sobj_at(BOULDER, sx, sy)) != 0) { 444 /* another boulder here, restack it to the top */ 445 obj_extract_self(otmp); 446 place_object(otmp, sx, sy); 447 } 448 } else { 449 struct trap *trap; 450 451 if ((trap = t_at(sx,sy)) && trap->ttyp == STATUE_TRAP) 452 deltrap(trap); 453 pline("%s.", Tobjnam(otmp, "crumble")); 454 (void) break_statue(otmp); 455 place_object(otmp, sx, sy); /* put fragments on floor */ 456 } 457 used_up = TRUE; 458 459 /* 1 in 10 chance of destruction of obj; glass, egg destruction */ 460 } else if ((scflags & MAY_DESTROY) && (!rn2(10) 461 || (objects[otmp->otyp].oc_material == GLASS 462 || otmp->otyp == EGG))) { 463 if (breaks(otmp, (xchar)sx, (xchar)sy)) used_up = TRUE; 464 } 465 466 if (!used_up) { 467 stmp = (struct scatter_chain *) 468 alloc(sizeof(struct scatter_chain)); 469 stmp->next = (struct scatter_chain *)0; 470 stmp->obj = otmp; 471 stmp->ox = sx; 472 stmp->oy = sy; 473 tmp = rn2(8); /* get the direction */ 474 stmp->dx = xdir[tmp]; 475 stmp->dy = ydir[tmp]; 476 tmp = blastforce - (otmp->owt/40); 477 if (tmp < 1) tmp = 1; 478 stmp->range = rnd(tmp); /* anywhere up to that determ. by wt */ 479 if (farthest < stmp->range) farthest = stmp->range; 480 stmp->stopped = FALSE; 481 if (!schain) 482 schain = stmp; 483 else 484 stmp2->next = stmp; 485 stmp2 = stmp; 486 } 487 } 488 489 while (farthest-- > 0) { 490 for (stmp = schain; stmp; stmp = stmp->next) { 491 if ((stmp->range-- > 0) && (!stmp->stopped)) { 492 bhitpos.x = stmp->ox + stmp->dx; 493 bhitpos.y = stmp->oy + stmp->dy; 494 typ = levl[bhitpos.x][bhitpos.y].typ; 495 if(!isok(bhitpos.x, bhitpos.y)) { 496 bhitpos.x -= stmp->dx; 497 bhitpos.y -= stmp->dy; 498 stmp->stopped = TRUE; 499 } else if(!ZAP_POS(typ) || 500 closed_door(bhitpos.x, bhitpos.y)) { 501 bhitpos.x -= stmp->dx; 502 bhitpos.y -= stmp->dy; 503 stmp->stopped = TRUE; 504 } else if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) { 505 if (scflags & MAY_HITMON) { 506 stmp->range--; 507 if (ohitmon(mtmp, stmp->obj, 1, FALSE)) { 508 stmp->obj = (struct obj *)0; 509 stmp->stopped = TRUE; 510 } 511 } 512 } else if (bhitpos.x==u.ux && bhitpos.y==u.uy) { 513 if (scflags & MAY_HITYOU) { 514 int hitvalu, hitu; 515 516 if (multi) nomul(0); 517 hitvalu = 8 + stmp->obj->spe; 518 if (bigmonst(youmonst.data)) hitvalu++; 519 hitu = thitu(hitvalu, 520 dmgval(stmp->obj, &youmonst), 521 stmp->obj, (char *)0); 522 if (hitu) { 523 stmp->range -= 3; 524 stop_occupation(); 525 } 526 } 527 } else { 528 if (scflags & VIS_EFFECTS) { 529 /* tmp_at(bhitpos.x, bhitpos.y); */ 530 /* delay_output(); */ 531 } 532 } 533 stmp->ox = bhitpos.x; 534 stmp->oy = bhitpos.y; 535 } 536 } 537 } 538 for (stmp = schain; stmp; stmp = stmp2) { 539 int x,y; 540 541 stmp2 = stmp->next; 542 x = stmp->ox; y = stmp->oy; 543 if (stmp->obj) { 544 if ( x!=sx || y!=sy ) 545 total += stmp->obj->quan; 546 place_object(stmp->obj, x, y); 547 stackobj(stmp->obj); 548 } 549 free((genericptr_t)stmp); 550 newsym(x,y); 551 } 552 553 return total; 554} 555 556 557/* 558 * Splatter burning oil from x,y to the surrounding area. 559 * 560 * This routine should really take a how and direction parameters. 561 * The how is how it was caused, e.g. kicked verses thrown. The 562 * direction is which way to spread the flaming oil. Different 563 * "how"s would give different dispersal patterns. For example, 564 * kicking a burning flask will splatter differently from a thrown 565 * flask hitting the ground. 566 * 567 * For now, just perform a "regular" explosion. 568 */ 569void 570splatter_burning_oil(x, y) 571 int x, y; 572{ 573/* ZT_SPELL(ZT_FIRE) = ZT_SPELL(AD_FIRE-1) = 10+(2-1) = 11 */ 574#define ZT_SPELL_O_FIRE 11 /* value kludge, see zap.c */ 575 explode(x, y, ZT_SPELL_O_FIRE, d(4,4), BURNING_OIL, EXPL_FIERY); 576} 577 578#endif /* OVL1 */ 579 580/*explode.c*/ 581