1 /* aNetHack 0.0.1 explode.c $ANH-Date: 1450915435 2015/12/24 00:03:55 $ $ANH-Branch: master $:$ANH-Revision: 1.45 $ */
2 /* Copyright (C) 1990 by Ken Arromdee */
3 /* aNetHack 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;
43 int explmask
[3][3]; /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
44 boolean shopdamage
= FALSE
, generic
= FALSE
, physical_dmg
= FALSE
,
45 do_hallu
= FALSE
, inside_engulfer
;
46 char hallu_buf
[BUFSZ
];
47 short exploding_wand_typ
= 0;
49 if (olet
== WAND_CLASS
) { /* retributive strike */
50 /* 'type' is passed as (wand's object type * -1); save
51 object type and convert 'type' itself to zap-type */
54 exploding_wand_typ
= (short) type
;
55 /* most attack wands produce specific explosions;
56 other types produce a generic magical explosion */
57 if (objects
[type
].oc_dir
== RAY
58 && type
!= WAN_DIGGING
&& type
!= WAN_SLEEP
) {
59 type
-= WAN_MAGIC_MISSILE
;
60 if (type
< 0 || type
> 9) {
61 impossible("explode: wand has bad zap type (%d).", type
);
67 switch (Role_switch
) {
81 /* muse_unslime: SCR_FIRE */
83 /* hero gets credit/blame for killing this monster, not others */
87 /* if hero is engulfed and caused the explosion, only hero and
88 engulfer will be affected */
89 inside_engulfer
= (u
.uswallow
&& type
>= 0);
91 if (olet
== MON_EXPLODE
) {
93 do_hallu
= Hallucination
&& strstri(str
, "'s explosion");
96 switch (abs(type
) % 10) {
98 str
= "magical blast";
102 str
= (olet
== BURNING_OIL
) ? "burning oil"
103 : (olet
== SCROLL_CLASS
) ? "tower of flame" : "fireball";
104 /* fire damage, not physical damage */
108 str
= "ball of cold";
112 str
= (olet
== WAND_CLASS
) ? "death field"
113 : "disintegration field";
117 str
= "ball of lightning";
121 str
= "poison gas cloud";
125 str
= "splash of acid";
129 impossible("explosion base type %d?", type
);
133 any_shield
= visible
= FALSE
;
134 for (i
= 0; i
< 3; i
++)
135 for (j
= 0; j
< 3; j
++) {
136 if (!isok(i
+ x
- 1, j
+ y
- 1)) {
142 if (i
+ x
- 1 == u
.ux
&& j
+ y
- 1 == u
.uy
) {
148 explmask
[i
][j
] = !!Antimagic
;
151 explmask
[i
][j
] = !!Fire_resistance
;
154 explmask
[i
][j
] = !!Cold_resistance
;
157 explmask
[i
][j
] = (olet
== WAND_CLASS
)
158 ? !!(nonliving(youmonst
.data
)
159 || is_demon(youmonst
.data
))
160 : !!Disint_resistance
;
163 explmask
[i
][j
] = !!Shock_resistance
;
166 explmask
[i
][j
] = !!Poison_resistance
;
169 explmask
[i
][j
] = !!Acid_resistance
;
173 impossible("explosion type %d?", adtyp
);
177 /* can be both you and mtmp if you're swallowed */
178 mtmp
= m_at(i
+ x
- 1, j
+ y
- 1);
179 if (!mtmp
&& i
+ x
- 1 == u
.ux
&& j
+ y
- 1 == u
.uy
)
189 explmask
[i
][j
] |= resists_magm(mtmp
);
192 explmask
[i
][j
] |= resists_fire(mtmp
);
195 explmask
[i
][j
] |= resists_cold(mtmp
);
198 explmask
[i
][j
] |= (olet
== WAND_CLASS
)
199 ? (nonliving(mtmp
->data
)
200 || is_demon(mtmp
->data
)
201 || is_vampshifter(mtmp
))
202 : resists_disint(mtmp
);
205 explmask
[i
][j
] |= resists_elec(mtmp
);
208 explmask
[i
][j
] |= resists_poison(mtmp
);
211 explmask
[i
][j
] |= resists_acid(mtmp
);
214 impossible("explosion type %d?", adtyp
);
218 if (mtmp
&& cansee(i
+ x
- 1, j
+ y
- 1) && !canspotmon(mtmp
))
219 map_invisible(i
+ x
- 1, j
+ y
- 1);
220 else if (!mtmp
&& glyph_is_invisible(
221 levl
[i
+ x
- 1][j
+ y
- 1].glyph
)) {
222 unmap_object(i
+ x
- 1, j
+ y
- 1);
223 newsym(i
+ x
- 1, j
+ y
- 1);
225 if (cansee(i
+ x
- 1, j
+ y
- 1))
227 if (explmask
[i
][j
] == 1)
232 /* Start the explosion */
233 for (i
= 0; i
< 3; i
++)
234 for (j
= 0; j
< 3; j
++) {
235 if (explmask
[i
][j
] == 2)
237 tmp_at(starting
? DISP_BEAM
: DISP_CHANGE
,
238 explosion_to_glyph(expltype
, explosion
[i
][j
]));
239 tmp_at(i
+ x
- 1, j
+ y
- 1);
242 curs_on_u(); /* will flush screen and output */
244 if (any_shield
&& flags
.sparkle
) { /* simulate shield effect */
245 for (k
= 0; k
< SHIELD_COUNT
; k
++) {
246 for (i
= 0; i
< 3; i
++)
247 for (j
= 0; j
< 3; j
++) {
248 if (explmask
[i
][j
] == 1)
250 * Bypass tmp_at() and send the shield glyphs
251 * directly to the buffered screen. tmp_at()
252 * will clean up the location for us later.
254 show_glyph(i
+ x
- 1, j
+ y
- 1,
255 cmap_to_glyph(shield_static
[k
]));
257 curs_on_u(); /* will flush screen and output */
261 /* Cover last shield glyph with blast symbol. */
262 for (i
= 0; i
< 3; i
++)
263 for (j
= 0; j
< 3; j
++) {
264 if (explmask
[i
][j
] == 1)
266 i
+ x
- 1, j
+ y
- 1,
267 explosion_to_glyph(expltype
, explosion
[i
][j
]));
270 } else { /* delay a little bit. */
275 tmp_at(DISP_END
, 0); /* clear the explosion */
277 if (olet
== MON_EXPLODE
) {
281 if (!Deaf
&& olet
!= SCROLL_CLASS
)
282 You_hear("a blast.");
286 for (i
= 0; i
< 3; i
++)
287 for (j
= 0; j
< 3; j
++) {
288 if (explmask
[i
][j
] == 2)
290 if (i
+ x
- 1 == u
.ux
&& j
+ y
- 1 == u
.uy
)
291 uhurt
= (explmask
[i
][j
] == 1) ? 1 : 2;
292 /* for inside_engulfer, only <u.ux,u.uy> is affected */
293 else if (inside_engulfer
)
295 idamres
= idamnonres
= 0;
296 if (type
>= 0 && !u
.uswallow
)
297 (void) zap_over_floor((xchar
) (i
+ x
- 1),
298 (xchar
) (j
+ y
- 1), type
,
299 &shopdamage
, exploding_wand_typ
);
301 mtmp
= m_at(i
+ x
- 1, j
+ y
- 1);
302 if (!mtmp
&& i
+ x
- 1 == u
.ux
&& j
+ y
- 1 == u
.uy
)
307 /* replace "gas spore" with a different description
308 for each target (we can't distinguish personal names
309 like "Barney" here in order to suppress "the" below,
310 so avoid any which begins with a capital letter) */
312 Sprintf(hallu_buf
, "%s explosion",
313 s_suffix(rndmonnam((char *) 0)));
314 } while (*hallu_buf
!= lowc(*hallu_buf
));
317 if (u
.uswallow
&& mtmp
== u
.ustuck
) {
318 const char *adj
= (char *) 0;
320 if (is_animal(u
.ustuck
->data
)) {
329 if (olet
== WAND_CLASS
)
330 adj
= "irradiated by pure energy";
341 adj
= "an upset stomach";
347 pline("%s gets %s!", Monnam(u
.ustuck
), adj
);
357 if (olet
== WAND_CLASS
)
358 adj
= "overwhelmed by pure energy";
375 pline("%s gets slightly %s!", Monnam(u
.ustuck
), adj
);
377 } else if (cansee(i
+ x
- 1, j
+ y
- 1)) {
380 pline("%s is caught in the %s!", Monnam(mtmp
), str
);
383 idamres
+= destroy_mitem(mtmp
, SCROLL_CLASS
, (int) adtyp
);
384 idamres
+= destroy_mitem(mtmp
, SPBOOK_CLASS
, (int) adtyp
);
385 idamnonres
+= destroy_mitem(mtmp
, POTION_CLASS
, (int) adtyp
);
386 idamnonres
+= destroy_mitem(mtmp
, WAND_CLASS
, (int) adtyp
);
387 idamnonres
+= destroy_mitem(mtmp
, RING_CLASS
, (int) adtyp
);
389 if (explmask
[i
][j
] == 1) {
390 golemeffects(mtmp
, (int) adtyp
, dam
+ idamres
);
391 mtmp
->mhp
-= idamnonres
;
393 /* call resist with 0 and do damage manually so 1) we can
394 * get out the message before doing the damage, and 2) we
396 * call mondied, not killed, if it's not your blast
400 if (resist(mtmp
, olet
, 0, FALSE
)) {
401 /* inside_engulfer: <i+x-1,j+y-1> == <u.ux,u.uy> */
402 if (cansee(i
+ x
- 1, j
+ y
- 1) || inside_engulfer
)
403 pline("%s resists the %s!", Monnam(mtmp
), str
);
404 mdam
= (dam
+ 1) / 2;
406 if (mtmp
== u
.ustuck
)
408 if (resists_cold(mtmp
) && adtyp
== AD_FIRE
)
410 else if (resists_fire(mtmp
) && adtyp
== AD_COLD
)
413 mtmp
->mhp
-= (idamres
+ idamnonres
);
415 if (mtmp
->mhp
<= 0) {
416 if (!context
.mon_moving
) {
418 } else if (mdef
&& mtmp
== mdef
) {
419 /* 'mdef' killed self trying to cure being turned
420 * into slime due to some action by the player.
421 * Hero gets the credit (experience) and most of
422 * the blame (possible loss of alignment and/or
423 * luck and/or telepathy depending on mtmp) but
424 * doesn't break pacifism. xkilled()'s message
425 * would be "you killed <mdef>" so give our own.
427 if (cansee(mtmp
->mx
, mtmp
->my
) || canspotmon(mtmp
))
428 pline("%s is %s!", Monnam(mtmp
),
429 nonliving(mtmp
->data
) ? "destroyed"
431 xkilled(mtmp
, XKILL_NOMSG
| XKILL_NOCONDUCT
);
433 monkilled(mtmp
, "", (int) adtyp
);
434 } else if (!context
.mon_moving
) {
435 /* all affected monsters, even if mdef is set */
436 setmangry(mtmp
, TRUE
);
440 /* Do your injury last */
442 /* give message for any monster-induced explosion
443 or player-induced one other than scroll of fire */
444 if (flags
.verbose
&& (type
< 0 || olet
!= SCROLL_CLASS
)) {
445 if (do_hallu
) { /* (see explanation above) */
447 Sprintf(hallu_buf
, "%s explosion",
448 s_suffix(rndmonnam((char *) 0)));
449 } while (*hallu_buf
!= lowc(*hallu_buf
));
452 You("are caught in the %s!", str
);
453 iflags
.last_msg
= PLNMSG_CAUGHT_IN_EXPLOSION
;
455 /* do property damage first, in case we end up leaving bones */
456 if (adtyp
== AD_FIRE
)
460 You("are unharmed!");
461 } else if (adtyp
== AD_PHYS
|| physical_dmg
)
462 damu
= Maybe_Half_Phys(damu
);
463 if (adtyp
== AD_FIRE
)
464 (void) burnarmor(&youmonst
);
465 destroy_item(SCROLL_CLASS
, (int) adtyp
);
466 destroy_item(SPBOOK_CLASS
, (int) adtyp
);
467 destroy_item(POTION_CLASS
, (int) adtyp
);
468 destroy_item(RING_CLASS
, (int) adtyp
);
469 destroy_item(WAND_CLASS
, (int) adtyp
);
471 ugolemeffects((int) adtyp
, damu
);
480 if (u
.uhp
<= 0 || (Upolyd
&& u
.mh
<= 0)) {
484 if (olet
== MON_EXPLODE
) {
485 if (generic
) /* explosion was unseen; str=="explosion", */
486 ; /* killer.name=="gas spore's explosion" */
487 else if (str
!= killer
.name
&& str
!= hallu_buf
)
488 Strcpy(killer
.name
, str
);
489 killer
.format
= KILLED_BY_AN
;
490 } else if (type
>= 0 && olet
!= SCROLL_CLASS
) {
491 killer
.format
= NO_KILLER_PREFIX
;
492 Sprintf(killer
.name
, "caught %sself in %s own %s", uhim(),
495 killer
.format
= (!strcmpi(str
, "tower of flame")
496 || !strcmpi(str
, "fireball"))
499 Strcpy(killer
.name
, str
);
501 if (iflags
.last_msg
== PLNMSG_CAUGHT_IN_EXPLOSION
502 || iflags
.last_msg
== PLNMSG_TOWER_OF_FLAME
) /*seffects()*/
503 pline("It is fatal.");
505 pline_The("%s is fatal.", str
);
506 /* Known BUG: BURNING suppresses corpse in bones data,
507 but done does not handle killer reason correctly */
508 done((adtyp
== AD_FIRE
) ? BURNING
: DIED
);
511 exercise(A_STR
, FALSE
);
515 pay_for_damage(adtyp
== AD_FIRE
519 : adtyp
== AD_DISN
? "disintegrate"
524 /* explosions are noisy */
527 i
= 50; /* in case random damage is very small */
530 wake_nearto(x
, y
, i
);
533 struct scatter_chain
{
534 struct scatter_chain
*next
; /* pointer to next scatter item */
535 struct obj
*obj
; /* pointer to the object */
536 xchar ox
; /* location of */
538 schar dx
; /* direction of */
539 schar dy
; /* travel */
540 int range
; /* range of object */
541 boolean stopped
; /* flag for in-motion/stopped */
546 * VIS_EFFECTS Add visual effects to display
547 * MAY_HITMON Objects may hit monsters
548 * MAY_HITYOU Objects may hit hero
549 * MAY_HIT Objects may hit you or monsters
550 * MAY_DESTROY Objects may be destroyed at random
551 * MAY_FRACTURE Stone objects can be fractured (statues, boulders)
554 /* returns number of scattered objects */
556 scatter(sx
, sy
, blastforce
, scflags
, obj
)
557 int sx
, sy
; /* location of objects to scatter */
558 int blastforce
; /* force behind the scattering */
559 unsigned int scflags
;
560 struct obj
*obj
; /* only scatter this obj */
562 register struct obj
*otmp
;
568 boolean individual_object
= obj
? TRUE
: FALSE
;
570 struct scatter_chain
*stmp
, *stmp2
= 0;
571 struct scatter_chain
*schain
= (struct scatter_chain
*) 0;
574 while ((otmp
= individual_object
? obj
: level
.objects
[sx
][sy
]) != 0) {
575 if (otmp
->quan
> 1L) {
576 qtmp
= otmp
->quan
- 1L;
577 if (qtmp
> LARGEST_INT
)
579 qtmp
= (long) rnd((int) qtmp
);
580 otmp
= splitobj(otmp
, qtmp
);
582 obj
= (struct obj
*) 0; /* all used */
584 obj_extract_self(otmp
);
587 /* 9 in 10 chance of fracturing boulders or statues */
588 if ((scflags
& MAY_FRACTURE
)
589 && ((otmp
->otyp
== BOULDER
) || (otmp
->otyp
== STATUE
))
591 if (otmp
->otyp
== BOULDER
) {
593 pline("%s apart.", Tobjnam(otmp
, "break"));
595 You_hear("stone breaking.");
597 place_object(otmp
, sx
, sy
);
598 if ((otmp
= sobj_at(BOULDER
, sx
, sy
)) != 0) {
599 /* another boulder here, restack it to the top */
600 obj_extract_self(otmp
);
601 place_object(otmp
, sx
, sy
);
606 if ((trap
= t_at(sx
, sy
)) && trap
->ttyp
== STATUE_TRAP
)
609 pline("%s.", Tobjnam(otmp
, "crumble"));
611 You_hear("stone crumbling.");
612 (void) break_statue(otmp
);
613 place_object(otmp
, sx
, sy
); /* put fragments on floor */
617 /* 1 in 10 chance of destruction of obj; glass, egg destruction */
618 } else if ((scflags
& MAY_DESTROY
)
619 && (!rn2(10) || (objects
[otmp
->otyp
].oc_material
== GLASS
620 || otmp
->otyp
== EGG
))) {
621 if (breaks(otmp
, (xchar
) sx
, (xchar
) sy
))
626 stmp
= (struct scatter_chain
*)
627 alloc(sizeof (struct scatter_chain
));
628 stmp
->next
= (struct scatter_chain
*) 0;
632 tmp
= rn2(8); /* get the direction */
633 stmp
->dx
= xdir
[tmp
];
634 stmp
->dy
= ydir
[tmp
];
635 tmp
= blastforce
- (otmp
->owt
/ 40);
638 stmp
->range
= rnd(tmp
); /* anywhere up to that determ. by wt */
639 if (farthest
< stmp
->range
)
640 farthest
= stmp
->range
;
641 stmp
->stopped
= FALSE
;
650 while (farthest
-- > 0) {
651 for (stmp
= schain
; stmp
; stmp
= stmp
->next
) {
652 if ((stmp
->range
-- > 0) && (!stmp
->stopped
)) {
653 bhitpos
.x
= stmp
->ox
+ stmp
->dx
;
654 bhitpos
.y
= stmp
->oy
+ stmp
->dy
;
655 typ
= levl
[bhitpos
.x
][bhitpos
.y
].typ
;
656 if (!isok(bhitpos
.x
, bhitpos
.y
)) {
657 bhitpos
.x
-= stmp
->dx
;
658 bhitpos
.y
-= stmp
->dy
;
659 stmp
->stopped
= TRUE
;
660 } else if (!ZAP_POS(typ
)
661 || closed_door(bhitpos
.x
, bhitpos
.y
)) {
662 bhitpos
.x
-= stmp
->dx
;
663 bhitpos
.y
-= stmp
->dy
;
664 stmp
->stopped
= TRUE
;
665 } else if ((mtmp
= m_at(bhitpos
.x
, bhitpos
.y
)) != 0) {
666 if (scflags
& MAY_HITMON
) {
668 if (ohitmon(mtmp
, stmp
->obj
, 1, FALSE
)) {
669 stmp
->obj
= (struct obj
*) 0;
670 stmp
->stopped
= TRUE
;
673 } else if (bhitpos
.x
== u
.ux
&& bhitpos
.y
== u
.uy
) {
674 if (scflags
& MAY_HITYOU
) {
679 hitvalu
= 8 + stmp
->obj
->spe
;
680 if (bigmonst(youmonst
.data
))
682 hitu
= thitu(hitvalu
, dmgval(stmp
->obj
, &youmonst
),
683 stmp
->obj
, (char *) 0);
690 if (scflags
& VIS_EFFECTS
) {
691 /* tmp_at(bhitpos.x, bhitpos.y); */
692 /* delay_output(); */
695 stmp
->ox
= bhitpos
.x
;
696 stmp
->oy
= bhitpos
.y
;
700 for (stmp
= schain
; stmp
; stmp
= stmp2
) {
707 if (x
!= sx
|| y
!= sy
)
708 total
+= stmp
->obj
->quan
;
709 place_object(stmp
->obj
, x
, y
);
712 free((genericptr_t
) stmp
);
720 * Splatter burning oil from x,y to the surrounding area.
722 * This routine should really take a how and direction parameters.
723 * The how is how it was caused, e.g. kicked verses thrown. The
724 * direction is which way to spread the flaming oil. Different
725 * "how"s would give different dispersal patterns. For example,
726 * kicking a burning flask will splatter differently from a thrown
727 * flask hitting the ground.
729 * For now, just perform a "regular" explosion.
732 splatter_burning_oil(x
, y
)
735 /* ZT_SPELL(ZT_FIRE) = ZT_SPELL(AD_FIRE-1) = 10+(2-1) = 11 */
736 #define ZT_SPELL_O_FIRE 11 /* value kludge, see zap.c */
737 explode(x
, y
, ZT_SPELL_O_FIRE
, d(4, 4), BURNING_OIL
, EXPL_FIERY
);
740 /* lit potion of oil is exploding; extinguish it as a light source before
741 possibly killing the hero and attempting to save bones */
743 explode_oil(obj
, x
, y
)
748 impossible("exploding unlit oil");
750 splatter_burning_oil(x
, y
);