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. */
9 /* Note: Arrays are column first, while the screen is row first */
10 static 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 }
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....
25 explode(x, y, type, dam, olet, expltype)
27 int type; /* the same as in zap.c */
32 int i, j, k, damu = dam;
34 boolean visible, any_shield;
35 int uhurt = 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */
37 int idamres, idamnonres;
41 /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
42 boolean shopdamage = FALSE;
43 boolean generic = FALSE;
45 if (olet == WAND_CLASS) /* retributive strike */
46 switch (Role_switch) {
49 case PM_WIZARD: damu /= 5;
52 case PM_KNIGHT: damu /= 2;
57 if (olet == MON_EXPLODE) {
59 killer = 0; /* set again later as needed */
62 switch (abs(type) % 10) {
63 case 0: str = "magical blast";
66 case 1: str = olet == BURNING_OIL ? "burning oil" :
67 olet == SCROLL_CLASS ? "tower of flame" :
71 case 2: str = "ball of cold";
74 case 4: str = (olet == WAND_CLASS) ? "death field" :
75 "disintegration field";
78 case 5: str = "ball of lightning";
81 case 6: str = "poison gas cloud";
84 case 7: str = "splash of acid";
87 default: impossible("explosion base type %d?", type); return;
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)) {
98 if (i+x-1 == u.ux && j+y-1 == u.uy) {
104 explmask[i][j] = !!Antimagic;
107 explmask[i][j] = !!Fire_resistance;
110 explmask[i][j] = !!Cold_resistance;
113 explmask[i][j] = (olet == WAND_CLASS) ?
114 !!(nonliving(youmonst.data) || is_demon(youmonst.data)) :
118 explmask[i][j] = !!Shock_resistance;
121 explmask[i][j] = !!Poison_resistance;
124 explmask[i][j] = !!Acid_resistance;
127 impossible("explosion type %d?", adtyp);
131 /* can be both you and mtmp if you're swallowed */
132 mtmp = m_at(i+x-1, j+y-1);
134 if (!mtmp && i+x-1 == u.ux && j+y-1 == u.uy)
138 if (mtmp->mhp < 1) explmask[i][j] = 2;
143 explmask[i][j] |= resists_magm(mtmp);
146 explmask[i][j] |= resists_fire(mtmp);
149 explmask[i][j] |= resists_cold(mtmp);
152 explmask[i][j] |= (olet == WAND_CLASS) ?
153 (nonliving(mtmp->data) || is_demon(mtmp->data)) :
154 resists_disint(mtmp);
157 explmask[i][j] |= resists_elec(mtmp);
160 explmask[i][j] |= resists_poison(mtmp);
163 explmask[i][j] |= resists_acid(mtmp);
166 impossible("explosion type %d?", adtyp);
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);
176 if (cansee(i+x-1, j+y-1)) visible = TRUE;
177 if (explmask[i][j] == 1) any_shield = TRUE;
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);
189 curs_on_u(); /* will flush screen and output */
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)
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.
200 show_glyph(i+x-1, j+y-1,
201 cmap_to_glyph(shield_static[k]));
203 curs_on_u(); /* will flush screen and output */
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]));
214 } else { /* delay a little bit. */
219 tmp_at(DISP_END, 0); /* clear the explosion */
221 if (olet == MON_EXPLODE) {
225 if (flags.soundok) You_hear("a blast.");
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;
235 (void)zap_over_floor((xchar)(i+x-1), (xchar)(j+y-1),
238 mtmp = m_at(i+x-1, j+y-1);
240 if (!mtmp && i+x-1 == u.ux && j+y-1 == u.uy)
244 if (u.uswallow && mtmp == u.ustuck) {
245 if (is_animal(u.ustuck->data))
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" :
257 pline("%s gets slightly %s!",
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" :
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);
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);
278 if (explmask[i][j] == 1) {
279 golemeffects(mtmp, (int) adtyp, dam + idamres);
280 mtmp->mhp -= idamnonres;
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
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);
293 if (mtmp == u.ustuck)
295 if (resists_cold(mtmp) && adtyp == AD_FIRE)
297 else if (resists_fire(mtmp) && adtyp == AD_COLD)
300 mtmp->mhp -= (idamres + idamnonres);
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);
309 /* Do your injury last */
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();
318 You("are unharmed!");
319 } else if (Half_physical_damage && adtyp == AD_PHYS)
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);
328 ugolemeffects((int) adtyp, damu);
337 if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
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);
355 killer_format = KILLED_BY;
356 Strcpy(killer_buf, str);
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);
364 exercise(A_STR, FALSE);
368 pay_for_damage(adtyp == AD_FIRE ? "burn away" :
369 adtyp == AD_COLD ? "shatter" :
370 adtyp == AD_DISN ? "disintegrate" : "destroy",
374 /* explosions are noisy */
376 if (i < 50) i = 50; /* in case random damage is very small */
377 wake_nearto(x, y, i);
382 struct 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 */
387 schar dx; /* direction of */
388 schar dy; /* travel */
389 int range; /* range of object */
390 boolean stopped; /* flag for in-motion/stopped */
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)
403 /* returns number of scattered objects */
405 scatter(sx,sy,blastforce,scflags, obj)
406 int sx,sy; /* location of objects to scatter */
407 int blastforce; /* force behind the scattering */
408 unsigned int scflags;
409 struct obj *obj; /* only scatter this obj */
411 register struct obj *otmp;
417 boolean individual_object = obj ? TRUE : FALSE;
419 struct scatter_chain *stmp, *stmp2 = 0;
420 struct scatter_chain *schain = (struct scatter_chain *)0;
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);
430 obj = (struct obj *)0; /* all used */
432 obj_extract_self(otmp);
435 /* 9 in 10 chance of fracturing boulders or statues */
436 if ((scflags & MAY_FRACTURE)
437 && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE))
439 if (otmp->otyp == BOULDER) {
440 pline("%s apart.", Tobjnam(otmp, "break"));
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);
451 if ((trap = t_at(sx,sy)) && trap->ttyp == STATUE_TRAP)
453 pline("%s.", Tobjnam(otmp, "crumble"));
454 (void) break_statue(otmp);
455 place_object(otmp, sx, sy); /* put fragments on floor */
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;
467 stmp = (struct scatter_chain *)
468 alloc(sizeof(struct scatter_chain));
469 stmp->next = (struct scatter_chain *)0;
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;
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) {
507 if (ohitmon(mtmp, stmp->obj, 1, FALSE)) {
508 stmp->obj = (struct obj *)0;
509 stmp->stopped = TRUE;
512 } else if (bhitpos.x==u.ux && bhitpos.y==u.uy) {
513 if (scflags & MAY_HITYOU) {
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);
528 if (scflags & VIS_EFFECTS) {
529 /* tmp_at(bhitpos.x, bhitpos.y); */
530 /* delay_output(); */
533 stmp->ox = bhitpos.x;
534 stmp->oy = bhitpos.y;
538 for (stmp = schain; stmp; stmp = stmp2) {
542 x = stmp->ox; y = stmp->oy;
544 if ( x!=sx || y!=sy )
545 total += stmp->obj->quan;
546 place_object(stmp->obj, x, y);
549 free((genericptr_t)stmp);
558 * Splatter burning oil from x,y to the surrounding area.
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.
567 * For now, just perform a "regular" explosion.
570 splatter_burning_oil(x, y)
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);