1 /* NetHack 3.6 explode.c $NHDT-Date: 1450915435 2015/12/24 00:03:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.45 $ */
2 /* Copyright (C) 1990 by Ken Arromdee */
3 /* NetHack may be freely redistributed. See license for details. */
7 /* Note: Arrays are column first, while the screen is row first */
8 static int explosion
[3][3] = { { S_explode1
, S_explode4
, S_explode7
},
9 { S_explode2
, S_explode5
, S_explode8
},
10 { S_explode3
, S_explode6
, S_explode9
} };
12 /* Note: I had to choose one of three possible kinds of "type" when writing
13 * this function: a wand type (like in zap.c), an adtyp, or an object type.
14 * Wand types get complex because they must be converted to adtyps for
15 * determining such things as fire resistance. Adtyps get complex in that
16 * they don't supply enough information--was it a player or a monster that
17 * did it, and with a wand, spell, or breath weapon? Object types share both
18 * these disadvantages....
20 * Important note about Half_physical_damage:
21 * Unlike losehp(), explode() makes the Half_physical_damage adjustments
22 * itself, so the caller should never have done that ahead of time.
23 * It has to be done this way because the damage value is applied to
24 * things beside the player. Care is taken within explode() to ensure
25 * that Half_physical_damage only affects the damage applied to the hero.
28 explode(x
, y
, type
, dam
, olet
, expltype
)
30 int type
; /* the same as in zap.c; passes -(wand typ) for some WAND_CLASS */
35 int i
, j
, k
, damu
= dam
;
37 boolean visible
, any_shield
;
38 int uhurt
= 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */
39 const char *str
= (const char *) 0;
40 int idamres
, idamnonres
;
41 struct monst
*mtmp
, *mdef
= 0;
44 /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
45 boolean shopdamage
= FALSE
, generic
= FALSE
, physical_dmg
= FALSE
,
46 do_hallu
= FALSE
, inside_engulfer
;
47 char hallu_buf
[BUFSZ
];
48 short exploding_wand_typ
= 0;
50 if (olet
== WAND_CLASS
) { /* retributive strike */
51 /* 'type' is passed as (wand's object type * -1); save
52 object type and convert 'type' itself to zap-type */
55 exploding_wand_typ
= (short) type
;
56 /* most attack wands produce specific explosions;
57 other types produce a generic magical explosion */
58 if (objects
[type
].oc_dir
== RAY
59 && type
!= WAN_DIGGING
&& type
!= WAN_SLEEP
) {
60 type
-= WAN_MAGIC_MISSILE
;
61 if (type
< 0 || type
> 9) {
62 impossible("explode: wand has bad zap type (%d).", type
);
68 switch (Role_switch
) {
82 /* muse_unslime: SCR_FIRE */
84 /* hero gets credit/blame for killing this monster, not others */
88 /* if hero is engulfed and caused the explosion, only hero and
89 engulfer will be affected */
90 inside_engulfer
= (u
.uswallow
&& type
>= 0);
92 if (olet
== MON_EXPLODE
) {
94 do_hallu
= Hallucination
&& strstri(str
, "'s explosion");
97 switch (abs(type
) % 10) {
99 str
= "magical blast";
103 str
= (olet
== BURNING_OIL
) ? "burning oil"
104 : (olet
== SCROLL_CLASS
) ? "tower of flame" : "fireball";
105 /* fire damage, not physical damage */
109 str
= "ball of cold";
113 str
= (olet
== WAND_CLASS
) ? "death field"
114 : "disintegration field";
118 str
= "ball of lightning";
122 str
= "poison gas cloud";
126 str
= "splash of acid";
130 impossible("explosion base type %d?", type
);
134 any_shield
= visible
= FALSE
;
135 for (i
= 0; i
< 3; i
++)
136 for (j
= 0; j
< 3; j
++) {
137 if (!isok(i
+ x
- 1, j
+ y
- 1)) {
143 if (i
+ x
- 1 == u
.ux
&& j
+ y
- 1 == u
.uy
) {
149 explmask
[i
][j
] = !!Antimagic
;
152 explmask
[i
][j
] = !!Fire_resistance
;
155 explmask
[i
][j
] = !!Cold_resistance
;
158 explmask
[i
][j
] = (olet
== WAND_CLASS
)
159 ? !!(nonliving(youmonst
.data
)
160 || is_demon(youmonst
.data
))
161 : !!Disint_resistance
;
164 explmask
[i
][j
] = !!Shock_resistance
;
167 explmask
[i
][j
] = !!Poison_resistance
;
170 explmask
[i
][j
] = !!Acid_resistance
;
174 impossible("explosion type %d?", adtyp
);
178 /* can be both you and mtmp if you're swallowed */
179 mtmp
= m_at(i
+ x
- 1, j
+ y
- 1);
180 if (!mtmp
&& i
+ x
- 1 == u
.ux
&& j
+ y
- 1 == u
.uy
)
190 explmask
[i
][j
] |= resists_magm(mtmp
);
193 explmask
[i
][j
] |= resists_fire(mtmp
);
196 explmask
[i
][j
] |= resists_cold(mtmp
);
199 explmask
[i
][j
] |= (olet
== WAND_CLASS
)
200 ? (nonliving(mtmp
->data
)
201 || is_demon(mtmp
->data
)
202 || is_vampshifter(mtmp
))
203 : resists_disint(mtmp
);
206 explmask
[i
][j
] |= resists_elec(mtmp
);
209 explmask
[i
][j
] |= resists_poison(mtmp
);
212 explmask
[i
][j
] |= resists_acid(mtmp
);
215 impossible("explosion type %d?", adtyp
);
219 if (mtmp
&& cansee(i
+ x
- 1, j
+ y
- 1) && !canspotmon(mtmp
))
220 map_invisible(i
+ x
- 1, j
+ y
- 1);
221 else if (!mtmp
&& glyph_is_invisible(
222 levl
[i
+ x
- 1][j
+ y
- 1].glyph
)) {
223 unmap_object(i
+ x
- 1, j
+ y
- 1);
224 newsym(i
+ x
- 1, j
+ y
- 1);
226 if (cansee(i
+ x
- 1, j
+ y
- 1))
228 if (explmask
[i
][j
] == 1)
233 /* Start the explosion */
234 for (i
= 0; i
< 3; i
++)
235 for (j
= 0; j
< 3; j
++) {
236 if (explmask
[i
][j
] == 2)
238 tmp_at(starting
? DISP_BEAM
: DISP_CHANGE
,
239 explosion_to_glyph(expltype
, explosion
[i
][j
]));
240 tmp_at(i
+ x
- 1, j
+ y
- 1);
243 curs_on_u(); /* will flush screen and output */
245 if (any_shield
&& flags
.sparkle
) { /* simulate shield effect */
246 for (k
= 0; k
< SHIELD_COUNT
; k
++) {
247 for (i
= 0; i
< 3; i
++)
248 for (j
= 0; j
< 3; j
++) {
249 if (explmask
[i
][j
] == 1)
251 * Bypass tmp_at() and send the shield glyphs
252 * directly to the buffered screen. tmp_at()
253 * will clean up the location for us later.
255 show_glyph(i
+ x
- 1, j
+ y
- 1,
256 cmap_to_glyph(shield_static
[k
]));
258 curs_on_u(); /* will flush screen and output */
262 /* Cover last shield glyph with blast symbol. */
263 for (i
= 0; i
< 3; i
++)
264 for (j
= 0; j
< 3; j
++) {
265 if (explmask
[i
][j
] == 1)
267 i
+ x
- 1, j
+ y
- 1,
268 explosion_to_glyph(expltype
, explosion
[i
][j
]));
271 } else { /* delay a little bit. */
276 tmp_at(DISP_END
, 0); /* clear the explosion */
278 if (olet
== MON_EXPLODE
) {
282 if (!Deaf
&& olet
!= SCROLL_CLASS
)
283 You_hear("a blast.");
287 for (i
= 0; i
< 3; i
++)
288 for (j
= 0; j
< 3; j
++) {
289 if (explmask
[i
][j
] == 2)
291 if (i
+ x
- 1 == u
.ux
&& j
+ y
- 1 == u
.uy
)
292 uhurt
= (explmask
[i
][j
] == 1) ? 1 : 2;
293 /* for inside_engulfer, only <u.ux,u.uy> is affected */
294 else if (inside_engulfer
)
296 idamres
= idamnonres
= 0;
297 if (type
>= 0 && !u
.uswallow
)
298 (void) zap_over_floor((xchar
) (i
+ x
- 1),
299 (xchar
) (j
+ y
- 1), type
,
300 &shopdamage
, exploding_wand_typ
);
302 mtmp
= m_at(i
+ x
- 1, j
+ y
- 1);
303 if (!mtmp
&& i
+ x
- 1 == u
.ux
&& j
+ y
- 1 == u
.uy
)
308 /* replace "gas spore" with a different description
309 for each target (we can't distinguish personal names
310 like "Barney" here in order to suppress "the" below,
311 so avoid any which begins with a capital letter) */
313 Sprintf(hallu_buf
, "%s explosion",
314 s_suffix(rndmonnam((char *) 0)));
315 } while (*hallu_buf
!= lowc(*hallu_buf
));
318 if (u
.uswallow
&& mtmp
== u
.ustuck
) {
319 const char *adj
= (char *) 0;
321 if (is_animal(u
.ustuck
->data
)) {
330 if (olet
== WAND_CLASS
)
331 adj
= "irradiated by pure energy";
342 adj
= "an upset stomach";
348 pline("%s gets %s!", Monnam(u
.ustuck
), adj
);
358 if (olet
== WAND_CLASS
)
359 adj
= "overwhelmed by pure energy";
376 pline("%s gets slightly %s!", Monnam(u
.ustuck
), adj
);
378 } else if (cansee(i
+ x
- 1, j
+ y
- 1)) {
381 pline("%s is caught in the %s!", Monnam(mtmp
), str
);
384 idamres
+= destroy_mitem(mtmp
, SCROLL_CLASS
, (int) adtyp
);
385 idamres
+= destroy_mitem(mtmp
, SPBOOK_CLASS
, (int) adtyp
);
386 idamnonres
+= destroy_mitem(mtmp
, POTION_CLASS
, (int) adtyp
);
387 idamnonres
+= destroy_mitem(mtmp
, WAND_CLASS
, (int) adtyp
);
388 idamnonres
+= destroy_mitem(mtmp
, RING_CLASS
, (int) adtyp
);
390 if (explmask
[i
][j
] == 1) {
391 golemeffects(mtmp
, (int) adtyp
, dam
+ idamres
);
392 mtmp
->mhp
-= idamnonres
;
394 /* call resist with 0 and do damage manually so 1) we can
395 * get out the message before doing the damage, and 2) we
397 * call mondied, not killed, if it's not your blast
401 if (resist(mtmp
, olet
, 0, FALSE
)) {
402 /* inside_engulfer: <i+x-1,j+y-1> == <u.ux,u.uy> */
403 if (cansee(i
+ x
- 1, j
+ y
- 1) || inside_engulfer
)
404 pline("%s resists the %s!", Monnam(mtmp
), str
);
405 mdam
= (dam
+ 1) / 2;
407 if (mtmp
== u
.ustuck
)
409 if (resists_cold(mtmp
) && adtyp
== AD_FIRE
)
411 else if (resists_fire(mtmp
) && adtyp
== AD_COLD
)
414 mtmp
->mhp
-= (idamres
+ idamnonres
);
416 if (mtmp
->mhp
<= 0) {
417 if (mdef
? (mtmp
== mdef
) : !context
.mon_moving
)
420 monkilled(mtmp
, "", (int) adtyp
);
421 } else if (!context
.mon_moving
) {
422 /* all affected monsters, even if mdef is set */
427 /* Do your injury last */
429 /* give message for any monster-induced explosion
430 or player-induced one other than scroll of fire */
431 if (flags
.verbose
&& (type
< 0 || olet
!= SCROLL_CLASS
)) {
432 if (do_hallu
) { /* (see explanation above) */
434 Sprintf(hallu_buf
, "%s explosion",
435 s_suffix(rndmonnam((char *) 0)));
436 } while (*hallu_buf
!= lowc(*hallu_buf
));
439 You("are caught in the %s!", str
);
440 iflags
.last_msg
= PLNMSG_CAUGHT_IN_EXPLOSION
;
442 /* do property damage first, in case we end up leaving bones */
443 if (adtyp
== AD_FIRE
)
447 You("are unharmed!");
448 } else if (adtyp
== AD_PHYS
|| physical_dmg
)
449 damu
= Maybe_Half_Phys(damu
);
450 if (adtyp
== AD_FIRE
)
451 (void) burnarmor(&youmonst
);
452 destroy_item(SCROLL_CLASS
, (int) adtyp
);
453 destroy_item(SPBOOK_CLASS
, (int) adtyp
);
454 destroy_item(POTION_CLASS
, (int) adtyp
);
455 destroy_item(RING_CLASS
, (int) adtyp
);
456 destroy_item(WAND_CLASS
, (int) adtyp
);
458 ugolemeffects((int) adtyp
, damu
);
467 if (u
.uhp
<= 0 || (Upolyd
&& u
.mh
<= 0)) {
471 if (olet
== MON_EXPLODE
) {
472 if (generic
) /* explosion was unseen; str=="explosion", */
473 ; /* killer.name=="gas spore's explosion" */
474 else if (str
!= killer
.name
&& str
!= hallu_buf
)
475 Strcpy(killer
.name
, str
);
476 killer
.format
= KILLED_BY_AN
;
477 } else if (type
>= 0 && olet
!= SCROLL_CLASS
) {
478 killer
.format
= NO_KILLER_PREFIX
;
479 Sprintf(killer
.name
, "caught %sself in %s own %s", uhim(),
482 killer
.format
= (!strcmpi(str
, "tower of flame")
483 || !strcmpi(str
, "fireball"))
486 Strcpy(killer
.name
, str
);
488 if (iflags
.last_msg
== PLNMSG_CAUGHT_IN_EXPLOSION
489 || iflags
.last_msg
== PLNMSG_TOWER_OF_FLAME
) /*seffects()*/
490 pline("It is fatal.");
492 pline_The("%s is fatal.", str
);
493 /* Known BUG: BURNING suppresses corpse in bones data,
494 but done does not handle killer reason correctly */
495 done((adtyp
== AD_FIRE
) ? BURNING
: DIED
);
498 exercise(A_STR
, FALSE
);
502 pay_for_damage(adtyp
== AD_FIRE
506 : adtyp
== AD_DISN
? "disintegrate"
511 /* explosions are noisy */
514 i
= 50; /* in case random damage is very small */
517 wake_nearto(x
, y
, i
);
520 struct scatter_chain
{
521 struct scatter_chain
*next
; /* pointer to next scatter item */
522 struct obj
*obj
; /* pointer to the object */
523 xchar ox
; /* location of */
525 schar dx
; /* direction of */
526 schar dy
; /* travel */
527 int range
; /* range of object */
528 boolean stopped
; /* flag for in-motion/stopped */
533 * VIS_EFFECTS Add visual effects to display
534 * MAY_HITMON Objects may hit monsters
535 * MAY_HITYOU Objects may hit hero
536 * MAY_HIT Objects may hit you or monsters
537 * MAY_DESTROY Objects may be destroyed at random
538 * MAY_FRACTURE Stone objects can be fractured (statues, boulders)
541 /* returns number of scattered objects */
543 scatter(sx
, sy
, blastforce
, scflags
, obj
)
544 int sx
, sy
; /* location of objects to scatter */
545 int blastforce
; /* force behind the scattering */
546 unsigned int scflags
;
547 struct obj
*obj
; /* only scatter this obj */
549 register struct obj
*otmp
;
555 boolean individual_object
= obj
? TRUE
: FALSE
;
557 struct scatter_chain
*stmp
, *stmp2
= 0;
558 struct scatter_chain
*schain
= (struct scatter_chain
*) 0;
561 while ((otmp
= individual_object
? obj
: level
.objects
[sx
][sy
]) != 0) {
562 if (otmp
->quan
> 1L) {
563 qtmp
= otmp
->quan
- 1L;
564 if (qtmp
> LARGEST_INT
)
566 qtmp
= (long) rnd((int) qtmp
);
567 otmp
= splitobj(otmp
, qtmp
);
569 obj
= (struct obj
*) 0; /* all used */
571 obj_extract_self(otmp
);
574 /* 9 in 10 chance of fracturing boulders or statues */
575 if ((scflags
& MAY_FRACTURE
)
576 && ((otmp
->otyp
== BOULDER
) || (otmp
->otyp
== STATUE
))
578 if (otmp
->otyp
== BOULDER
) {
580 pline("%s apart.", Tobjnam(otmp
, "break"));
582 You_hear("stone breaking.");
584 place_object(otmp
, sx
, sy
);
585 if ((otmp
= sobj_at(BOULDER
, sx
, sy
)) != 0) {
586 /* another boulder here, restack it to the top */
587 obj_extract_self(otmp
);
588 place_object(otmp
, sx
, sy
);
593 if ((trap
= t_at(sx
, sy
)) && trap
->ttyp
== STATUE_TRAP
)
596 pline("%s.", Tobjnam(otmp
, "crumble"));
598 You_hear("stone crumbling.");
599 (void) break_statue(otmp
);
600 place_object(otmp
, sx
, sy
); /* put fragments on floor */
604 /* 1 in 10 chance of destruction of obj; glass, egg destruction */
605 } else if ((scflags
& MAY_DESTROY
)
606 && (!rn2(10) || (objects
[otmp
->otyp
].oc_material
== GLASS
607 || otmp
->otyp
== EGG
))) {
608 if (breaks(otmp
, (xchar
) sx
, (xchar
) sy
))
613 stmp
= (struct scatter_chain
*)
614 alloc(sizeof (struct scatter_chain
));
615 stmp
->next
= (struct scatter_chain
*) 0;
619 tmp
= rn2(8); /* get the direction */
620 stmp
->dx
= xdir
[tmp
];
621 stmp
->dy
= ydir
[tmp
];
622 tmp
= blastforce
- (otmp
->owt
/ 40);
625 stmp
->range
= rnd(tmp
); /* anywhere up to that determ. by wt */
626 if (farthest
< stmp
->range
)
627 farthest
= stmp
->range
;
628 stmp
->stopped
= FALSE
;
637 while (farthest
-- > 0) {
638 for (stmp
= schain
; stmp
; stmp
= stmp
->next
) {
639 if ((stmp
->range
-- > 0) && (!stmp
->stopped
)) {
640 bhitpos
.x
= stmp
->ox
+ stmp
->dx
;
641 bhitpos
.y
= stmp
->oy
+ stmp
->dy
;
642 typ
= levl
[bhitpos
.x
][bhitpos
.y
].typ
;
643 if (!isok(bhitpos
.x
, bhitpos
.y
)) {
644 bhitpos
.x
-= stmp
->dx
;
645 bhitpos
.y
-= stmp
->dy
;
646 stmp
->stopped
= TRUE
;
647 } else if (!ZAP_POS(typ
)
648 || closed_door(bhitpos
.x
, bhitpos
.y
)) {
649 bhitpos
.x
-= stmp
->dx
;
650 bhitpos
.y
-= stmp
->dy
;
651 stmp
->stopped
= TRUE
;
652 } else if ((mtmp
= m_at(bhitpos
.x
, bhitpos
.y
)) != 0) {
653 if (scflags
& MAY_HITMON
) {
655 if (ohitmon(mtmp
, stmp
->obj
, 1, FALSE
)) {
656 stmp
->obj
= (struct obj
*) 0;
657 stmp
->stopped
= TRUE
;
660 } else if (bhitpos
.x
== u
.ux
&& bhitpos
.y
== u
.uy
) {
661 if (scflags
& MAY_HITYOU
) {
666 hitvalu
= 8 + stmp
->obj
->spe
;
667 if (bigmonst(youmonst
.data
))
669 hitu
= thitu(hitvalu
, dmgval(stmp
->obj
, &youmonst
),
670 stmp
->obj
, (char *) 0);
677 if (scflags
& VIS_EFFECTS
) {
678 /* tmp_at(bhitpos.x, bhitpos.y); */
679 /* delay_output(); */
682 stmp
->ox
= bhitpos
.x
;
683 stmp
->oy
= bhitpos
.y
;
687 for (stmp
= schain
; stmp
; stmp
= stmp2
) {
694 if (x
!= sx
|| y
!= sy
)
695 total
+= stmp
->obj
->quan
;
696 place_object(stmp
->obj
, x
, y
);
699 free((genericptr_t
) stmp
);
707 * Splatter burning oil from x,y to the surrounding area.
709 * This routine should really take a how and direction parameters.
710 * The how is how it was caused, e.g. kicked verses thrown. The
711 * direction is which way to spread the flaming oil. Different
712 * "how"s would give different dispersal patterns. For example,
713 * kicking a burning flask will splatter differently from a thrown
714 * flask hitting the ground.
716 * For now, just perform a "regular" explosion.
719 splatter_burning_oil(x
, y
)
722 /* ZT_SPELL(ZT_FIRE) = ZT_SPELL(AD_FIRE-1) = 10+(2-1) = 11 */
723 #define ZT_SPELL_O_FIRE 11 /* value kludge, see zap.c */
724 explode(x
, y
, ZT_SPELL_O_FIRE
, d(4, 4), BURNING_OIL
, EXPL_FIERY
);
727 /* lit potion of oil is exploding; extinguish it as a light source before
728 possibly killing the hero and attempting to save bones */
730 explode_oil(obj
, x
, y
)
735 impossible("exploding unlit oil");
737 splatter_burning_oil(x
, y
);