1 /* NetHack 3.6 explode.c $NHDT-Date: 1446955298 2015/11/08 04:01:38 $ $NHDT-Branch: master $:$NHDT-Revision: 1.44 $ */
2 /* Copyright (C) 1990 by Ken Arromdee */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016 */
8 /* JNetHack may be freely redistributed. See license for details. */
12 /* Note: Arrays are column first, while the screen is row first */
13 static int explosion[3][3] = { { S_explode1, S_explode4, S_explode7 },
14 { S_explode2, S_explode5, S_explode8 },
15 { S_explode3, S_explode6, S_explode9 } };
17 /* Note: I had to choose one of three possible kinds of "type" when writing
18 * this function: a wand type (like in zap.c), an adtyp, or an object type.
19 * Wand types get complex because they must be converted to adtyps for
20 * determining such things as fire resistance. Adtyps get complex in that
21 * they don't supply enough information--was it a player or a monster that
22 * did it, and with a wand, spell, or breath weapon? Object types share both
23 * these disadvantages....
25 * Important note about Half_physical_damage:
26 * Unlike losehp(), explode() makes the Half_physical_damage adjustments
27 * itself, so the caller should never have done that ahead of time.
28 * It has to be done this way because the damage value is applied to
29 * things beside the player. Care is taken within explode() to ensure
30 * that Half_physical_damage only affects the damage applied to the hero.
33 explode(x, y, type, dam, olet, expltype)
35 int type; /* the same as in zap.c; passes -(wand typ) for some WAND_CLASS */
40 int i, j, k, damu = dam;
42 boolean visible, any_shield;
43 int uhurt = 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */
44 const char *str = (const char *) 0;
45 int idamres, idamnonres;
46 struct monst *mtmp, *mdef = 0;
49 /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
50 boolean shopdamage = FALSE, generic = FALSE, physical_dmg = FALSE,
51 do_hallu = FALSE, inside_engulfer;
52 char hallu_buf[BUFSZ];
53 short exploding_wand_typ = 0;
55 if (olet == WAND_CLASS) { /* retributive strike */
56 /* 'type' is passed as (wand's object type * -1); save
57 object type and convert 'type' itself to zap-type */
60 exploding_wand_typ = (short) type;
61 /* most attack wands produce specific explosions;
62 other types produce a generic magical explosion */
63 if (objects[type].oc_dir == RAY
64 && type != WAN_DIGGING && type != WAN_SLEEP) {
65 type -= WAN_MAGIC_MISSILE;
66 if (type < 0 || type > 9) {
67 impossible("explode: wand has bad zap type (%d).", type);
73 switch (Role_switch) {
87 /* muse_unslime: SCR_FIRE */
89 /* hero gets credit/blame for killing this monster, not others */
93 /* if hero is engulfed and caused the explosion, only hero and
94 engulfer will be affected */
95 inside_engulfer = (u.uswallow && type >= 0);
97 if (olet == MON_EXPLODE) {
99 do_hallu = Hallucination && strstri(str, "'s explosion");
102 switch (abs(type) % 10) {
105 str = "magical blast";
107 str = "
\96\82\96@
\82Ì
\95\97";
112 str = (olet == BURNING_OIL) ? "burning oil"
113 : (olet == SCROLL_CLASS) ? "tower of flame" : "fireball";
115 str = (olet == BURNING_OIL) ? "
\94R
\82¦
\82Ä
\82¢
\82é
\96û"
116 : (olet == SCROLL_CLASS) ? "
\89Î
\92\8c" : "
\89Î
\82Ì
\8bÊ";
118 /* fire damage, not physical damage */
123 str = "ball of cold";
125 str = "
\95X
\82Ì
\8bÊ";
130 str = (olet == WAND_CLASS) ? "death field"
131 : "disintegration field";
133 str = (olet == WAND_CLASS) ? "
\8e\80\82Ì
\95\97"
134 : "
\95ª
\89ð
\82Ì
\95\97";
140 str = "ball of lightning";
147 str = "poison gas cloud";
149 str = "
\93Å
\82Ì
\89_";
154 str = "splash of acid";
156 str = "
\8e_
\82Ì
\82µ
\82Ô
\82«";
160 impossible("explosion base type %d?", type);
164 any_shield = visible = FALSE;
165 for (i = 0; i < 3; i++)
166 for (j = 0; j < 3; j++) {
167 if (!isok(i + x - 1, j + y - 1)) {
173 if (i + x - 1 == u.ux && j + y - 1 == u.uy) {
179 explmask[i][j] = !!Antimagic;
182 explmask[i][j] = !!Fire_resistance;
185 explmask[i][j] = !!Cold_resistance;
188 explmask[i][j] = (olet == WAND_CLASS)
189 ? !!(nonliving(youmonst.data)
190 || is_demon(youmonst.data))
191 : !!Disint_resistance;
194 explmask[i][j] = !!Shock_resistance;
197 explmask[i][j] = !!Poison_resistance;
200 explmask[i][j] = !!Acid_resistance;
204 impossible("explosion type %d?", adtyp);
208 /* can be both you and mtmp if you're swallowed */
209 mtmp = m_at(i + x - 1, j + y - 1);
210 if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy)
220 explmask[i][j] |= resists_magm(mtmp);
223 explmask[i][j] |= resists_fire(mtmp);
226 explmask[i][j] |= resists_cold(mtmp);
229 explmask[i][j] |= (olet == WAND_CLASS)
230 ? (nonliving(mtmp->data)
231 || is_demon(mtmp->data)
232 || is_vampshifter(mtmp))
233 : resists_disint(mtmp);
236 explmask[i][j] |= resists_elec(mtmp);
239 explmask[i][j] |= resists_poison(mtmp);
242 explmask[i][j] |= resists_acid(mtmp);
245 impossible("explosion type %d?", adtyp);
249 if (mtmp && cansee(i + x - 1, j + y - 1) && !canspotmon(mtmp))
250 map_invisible(i + x - 1, j + y - 1);
251 else if (!mtmp && glyph_is_invisible(
252 levl[i + x - 1][j + y - 1].glyph)) {
253 unmap_object(i + x - 1, j + y - 1);
254 newsym(i + x - 1, j + y - 1);
256 if (cansee(i + x - 1, j + y - 1))
258 if (explmask[i][j] == 1)
263 /* Start the explosion */
264 for (i = 0; i < 3; i++)
265 for (j = 0; j < 3; j++) {
266 if (explmask[i][j] == 2)
268 tmp_at(starting ? DISP_BEAM : DISP_CHANGE,
269 explosion_to_glyph(expltype, explosion[i][j]));
270 tmp_at(i + x - 1, j + y - 1);
273 curs_on_u(); /* will flush screen and output */
275 if (any_shield && flags.sparkle) { /* simulate shield effect */
276 for (k = 0; k < SHIELD_COUNT; k++) {
277 for (i = 0; i < 3; i++)
278 for (j = 0; j < 3; j++) {
279 if (explmask[i][j] == 1)
281 * Bypass tmp_at() and send the shield glyphs
282 * directly to the buffered screen. tmp_at()
283 * will clean up the location for us later.
285 show_glyph(i + x - 1, j + y - 1,
286 cmap_to_glyph(shield_static[k]));
288 curs_on_u(); /* will flush screen and output */
292 /* Cover last shield glyph with blast symbol. */
293 for (i = 0; i < 3; i++)
294 for (j = 0; j < 3; j++) {
295 if (explmask[i][j] == 1)
297 i + x - 1, j + y - 1,
298 explosion_to_glyph(expltype, explosion[i][j]));
301 } else { /* delay a little bit. */
306 tmp_at(DISP_END, 0); /* clear the explosion */
308 if (olet == MON_EXPLODE) {
315 if (!Deaf && olet != SCROLL_CLASS)
317 You_hear("a blast.");
319 You_hear("
\94\9a\94
\89¹
\82ð
\95·
\82¢
\82½
\81D");
323 for (i = 0; i < 3; i++)
324 for (j = 0; j < 3; j++) {
325 if (explmask[i][j] == 2)
327 if (i + x - 1 == u.ux && j + y - 1 == u.uy)
328 uhurt = (explmask[i][j] == 1) ? 1 : 2;
329 /* for inside_engulfer, only <u.ux,u.uy> is affected */
330 else if (inside_engulfer)
332 idamres = idamnonres = 0;
333 if (type >= 0 && !u.uswallow)
334 (void) zap_over_floor((xchar) (i + x - 1),
335 (xchar) (j + y - 1), type,
336 &shopdamage, exploding_wand_typ);
338 mtmp = m_at(i + x - 1, j + y - 1);
339 if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy)
344 /* replace "gas spore" with a different description
345 for each target (we can't distinguish personal names
346 like "Barney" here in order to suppress "the" below,
347 so avoid any which begins with a capital letter) */
349 Sprintf(hallu_buf, "%s explosion",
350 s_suffix(rndmonnam(NULL)));
351 } while (*hallu_buf != lowc(*hallu_buf));
354 if (u.uswallow && mtmp == u.ustuck) {
355 const char *adj = NULL;
356 if (is_animal(u.ustuck->data)) {
362 adj = "
\94R
\82¦
\82½";
368 adj = "
\93\80\82ç
\82³
\82ê
\82½";
371 if (olet == WAND_CLASS)
373 adj = "irradiated by pure energy";
375 adj = "
\8fò
\89»
\82Ì
\97Í
\82ð
\97\81\82Ñ
\82½";
380 adj = "
\8c\8a\82ð
\82 \82¯
\82ç
\82ê
\82½";
386 adj = "
\93d
\8c\82\82ð
\82
\82ç
\82Á
\82½";
392 adj = "
\93Å
\82ð
\82
\82ç
\82Á
\82½";
396 adj = "an upset stomach";
398 adj = "
\8e_
\82ð
\82
\82ç
\82Á
\82½";
404 adj = "
\83p
\83\8a\83p
\83\8a\82É
\82È
\82Á
\82½";
408 pline("%s gets %s!", Monnam(u.ustuck), adj);
410 pline("%s
\82Í%s
\81I", Monnam(u.ustuck), adj);
417 adj = "
\8fÅ
\82°
\82½";
423 adj = "
\93\80\82Á
\82½";
426 if (olet == WAND_CLASS)
428 adj = "overwhelmed by pure energy";
430 adj = "
\8fò
\89»
\82Ì
\97Í
\82ð
\97\81\82Ñ
\82½";
435 adj = "
\8c\8a\82ð
\82 \82¯
\82ç
\82ê
\82½";
441 adj = "
\93d
\8c\82\82ð
\82
\82ç
\82Á
\82½";
447 adj = "
\93Å
\82ð
\82
\82ç
\82Á
\82½";
453 adj = "
\8e_
\82ð
\82
\82ç
\82Á
\82½";
459 adj = "
\83p
\83\8a\83p
\83\8a\82É
\82È
\82Á
\82½";
463 pline("%s gets slightly %s!", Monnam(u.ustuck), adj);
465 pline("%s
\82Í
\8f
\82µ
\82¾
\82¯%s
\81I", Monnam(u.ustuck), adj);
467 } else if (cansee(i + x - 1, j + y - 1)) {
471 pline("%s is caught in the %s!", Monnam(mtmp), str);
473 pline("%s
\82Í%s
\82É
\82Â
\82Â
\82Ü
\82ê
\82½
\81I", Monnam(mtmp), str);
476 idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int) adtyp);
477 idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int) adtyp);
478 idamnonres += destroy_mitem(mtmp, POTION_CLASS, (int) adtyp);
479 idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int) adtyp);
480 idamnonres += destroy_mitem(mtmp, RING_CLASS, (int) adtyp);
482 if (explmask[i][j] == 1) {
483 golemeffects(mtmp, (int) adtyp, dam + idamres);
484 mtmp->mhp -= idamnonres;
486 /* call resist with 0 and do damage manually so 1) we can
487 * get out the message before doing the damage, and 2) we
489 * call mondied, not killed, if it's not your blast
493 if (resist(mtmp, olet, 0, FALSE)) {
494 /* inside_engulfer: <i+x-1,j+y-1> == <u.ux,u.uy> */
495 if (cansee(i + x - 1, j + y - 1) || inside_engulfer)
497 pline("%s resists the %s!", Monnam(mtmp), str);
499 pline("%s
\82Í%s
\82É
\92ï
\8dR
\82µ
\82½
\81I", Monnam(mtmp), str);
500 mdam = (dam + 1) / 2;
502 if (mtmp == u.ustuck)
504 if (resists_cold(mtmp) && adtyp == AD_FIRE)
506 else if (resists_fire(mtmp) && adtyp == AD_COLD)
509 mtmp->mhp -= (idamres + idamnonres);
511 if (mtmp->mhp <= 0) {
512 if (mdef ? (mtmp == mdef) : !context.mon_moving)
515 monkilled(mtmp, "", (int) adtyp);
516 } else if (!context.mon_moving) {
517 /* all affected monsters, even if mdef is set */
522 /* Do your injury last */
524 /* give message for any monster-induced explosion
525 or player-induced one other than scroll of fire */
526 if (flags.verbose && (type < 0 || olet != SCROLL_CLASS)) {
527 if (do_hallu) { /* (see explanation above) */
529 Sprintf(hallu_buf, "%s explosion",
530 s_suffix(rndmonnam(NULL)));
531 } while (*hallu_buf != lowc(*hallu_buf));
535 You("are caught in the %s!", str);
537 You("%s
\82É
\82Â
\82Â
\82Ü
\82ê
\82½
\81I", str);
538 iflags.last_msg = PLNMSG_CAUGHT_IN_EXPLOSION;
540 /* do property damage first, in case we end up leaving bones */
541 if (adtyp == AD_FIRE)
546 You("are unharmed!");
548 You("
\8f\9d\82Â
\82©
\82È
\82¢
\81I");
549 } else if (adtyp == AD_PHYS || physical_dmg)
550 damu = Maybe_Half_Phys(damu);
551 if (adtyp == AD_FIRE)
552 (void) burnarmor(&youmonst);
553 destroy_item(SCROLL_CLASS, (int) adtyp);
554 destroy_item(SPBOOK_CLASS, (int) adtyp);
555 destroy_item(POTION_CLASS, (int) adtyp);
556 destroy_item(RING_CLASS, (int) adtyp);
557 destroy_item(WAND_CLASS, (int) adtyp);
559 ugolemeffects((int) adtyp, damu);
568 if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
572 if (olet == MON_EXPLODE) {
573 /* killer handled by caller */
576 else if (str != killer.name && str != hallu_buf)
577 Strcpy(killer.name, str);
578 killer.format = KILLED_BY_AN;
580 Strcat(killer.name, "
\82Å");
582 } else if (type >= 0 && olet != SCROLL_CLASS) {
584 killer.format = NO_KILLER_PREFIX;
585 Sprintf(killer.name, "caught %sself in %s own %s", uhim(),
588 killer.format = KILLED_BY;
589 Sprintf(killer.name, "
\8e©
\95ª
\8e©
\90g
\82Ì%s
\82É
\82Â
\82Â
\82Ü
\82ê
\82Ä",
593 #if 0 /*JP*//* an
\82ð
\82Â
\82¯
\82é
\82©
\82Ç
\82¤
\82©
\82Í
\8aÖ
\8cW
\82È
\82¢ */
594 killer.format = (!strcmpi(str, "tower of flame")
595 || !strcmpi(str, "fireball"))
598 Strcpy(killer.name, str);
600 killer.format = KILLED_BY;
601 Strcpy(killer.name, str);
602 Strcat(killer.name, "
\82Å");
605 if (iflags.last_msg == PLNMSG_CAUGHT_IN_EXPLOSION
607 == PLNMSG_TOWER_OF_FLAME) /*seffects()*/
608 pline("It is fatal.");
610 pline_The("%s is fatal.", str);
611 /* Known BUG: BURNING suppresses corpse in bones data,
612 but done does not handle killer reason correctly */
613 done((adtyp == AD_FIRE) ? BURNING : DIED);
616 exercise(A_STR, FALSE);
621 pay_for_damage(adtyp == AD_FIRE
625 : adtyp == AD_DISN ? "disintegrate"
629 pay_for_damage(adtyp == AD_FIRE
632 ? "
\95²
\81X
\82É
\82·
\82é"
633 : adtyp == AD_DISN ? "
\95²
\8dÓ
\82·
\82é"
634 : "
\94j
\89ó
\82·
\82é",
639 /* explosions are noisy */
642 i = 50; /* in case random damage is very small */
645 wake_nearto(x, y, i);
648 struct scatter_chain {
649 struct scatter_chain *next; /* pointer to next scatter item */
650 struct obj *obj; /* pointer to the object */
651 xchar ox; /* location of */
653 schar dx; /* direction of */
654 schar dy; /* travel */
655 int range; /* range of object */
656 boolean stopped; /* flag for in-motion/stopped */
661 * VIS_EFFECTS Add visual effects to display
662 * MAY_HITMON Objects may hit monsters
663 * MAY_HITYOU Objects may hit hero
664 * MAY_HIT Objects may hit you or monsters
665 * MAY_DESTROY Objects may be destroyed at random
666 * MAY_FRACTURE Stone objects can be fractured (statues, boulders)
669 /* returns number of scattered objects */
671 scatter(sx, sy, blastforce, scflags, obj)
672 int sx, sy; /* location of objects to scatter */
673 int blastforce; /* force behind the scattering */
674 unsigned int scflags;
675 struct obj *obj; /* only scatter this obj */
677 register struct obj *otmp;
683 boolean individual_object = obj ? TRUE : FALSE;
685 struct scatter_chain *stmp, *stmp2 = 0;
686 struct scatter_chain *schain = (struct scatter_chain *) 0;
689 while ((otmp = individual_object ? obj : level.objects[sx][sy]) != 0) {
690 if (otmp->quan > 1L) {
691 qtmp = otmp->quan - 1L;
692 if (qtmp > LARGEST_INT)
694 qtmp = (long) rnd((int) qtmp);
695 otmp = splitobj(otmp, qtmp);
697 obj = (struct obj *) 0; /* all used */
699 obj_extract_self(otmp);
702 /* 9 in 10 chance of fracturing boulders or statues */
703 if ((scflags & MAY_FRACTURE)
704 && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE))
706 if (otmp->otyp == BOULDER) {
708 pline("%s apart.", Tobjnam(otmp, "break"));
710 pline("%s
\82Í
\88ê
\95\94\95ª
\82ª
\8dÓ
\82¯
\82½
\81D",xname(otmp));
712 place_object(otmp, sx, sy);
713 if ((otmp = sobj_at(BOULDER, sx, sy)) != 0) {
714 /* another boulder here, restack it to the top */
715 obj_extract_self(otmp);
716 place_object(otmp, sx, sy);
721 if ((trap = t_at(sx, sy)) && trap->ttyp == STATUE_TRAP)
724 pline("%s.", Tobjnam(otmp, "crumble"));
726 pline("%s
\82Í
\82±
\82È
\82²
\82È
\82É
\82È
\82Á
\82½
\81D",xname(otmp));
727 (void) break_statue(otmp);
728 #ifndef FIX_BUG_C340_2
729 place_object(otmp, sx, sy); /* put fragments on floor */
734 /* 1 in 10 chance of destruction of obj; glass, egg destruction */
735 } else if ((scflags & MAY_DESTROY)
736 && (!rn2(10) || (objects[otmp->otyp].oc_material == GLASS
737 || otmp->otyp == EGG))) {
738 if (breaks(otmp, (xchar) sx, (xchar) sy))
744 (struct scatter_chain *) alloc(sizeof(struct scatter_chain));
745 stmp->next = (struct scatter_chain *) 0;
749 tmp = rn2(8); /* get the direction */
750 stmp->dx = xdir[tmp];
751 stmp->dy = ydir[tmp];
752 tmp = blastforce - (otmp->owt / 40);
755 stmp->range = rnd(tmp); /* anywhere up to that determ. by wt */
756 if (farthest < stmp->range)
757 farthest = stmp->range;
758 stmp->stopped = FALSE;
767 while (farthest-- > 0) {
768 for (stmp = schain; stmp; stmp = stmp->next) {
769 if ((stmp->range-- > 0) && (!stmp->stopped)) {
770 bhitpos.x = stmp->ox + stmp->dx;
771 bhitpos.y = stmp->oy + stmp->dy;
772 typ = levl[bhitpos.x][bhitpos.y].typ;
773 if (!isok(bhitpos.x, bhitpos.y)) {
774 bhitpos.x -= stmp->dx;
775 bhitpos.y -= stmp->dy;
776 stmp->stopped = TRUE;
777 } else if (!ZAP_POS(typ)
778 || closed_door(bhitpos.x, bhitpos.y)) {
779 bhitpos.x -= stmp->dx;
780 bhitpos.y -= stmp->dy;
781 stmp->stopped = TRUE;
782 } else if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
783 if (scflags & MAY_HITMON) {
785 if (ohitmon(mtmp, stmp->obj, 1, FALSE)) {
786 stmp->obj = (struct obj *) 0;
787 stmp->stopped = TRUE;
790 } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
791 if (scflags & MAY_HITYOU) {
796 hitvalu = 8 + stmp->obj->spe;
797 if (bigmonst(youmonst.data))
799 hitu = thitu(hitvalu, dmgval(stmp->obj, &youmonst),
800 stmp->obj, (char *) 0);
807 if (scflags & VIS_EFFECTS) {
808 /* tmp_at(bhitpos.x, bhitpos.y); */
809 /* delay_output(); */
812 stmp->ox = bhitpos.x;
813 stmp->oy = bhitpos.y;
817 for (stmp = schain; stmp; stmp = stmp2) {
824 if (x != sx || y != sy)
825 total += stmp->obj->quan;
826 place_object(stmp->obj, x, y);
829 free((genericptr_t) stmp);
837 * Splatter burning oil from x,y to the surrounding area.
839 * This routine should really take a how and direction parameters.
840 * The how is how it was caused, e.g. kicked verses thrown. The
841 * direction is which way to spread the flaming oil. Different
842 * "how"s would give different dispersal patterns. For example,
843 * kicking a burning flask will splatter differently from a thrown
844 * flask hitting the ground.
846 * For now, just perform a "regular" explosion.
849 splatter_burning_oil(x, y)
852 /* ZT_SPELL(ZT_FIRE) = ZT_SPELL(AD_FIRE-1) = 10+(2-1) = 11 */
853 #define ZT_SPELL_O_FIRE 11 /* value kludge, see zap.c */
854 explode(x, y, ZT_SPELL_O_FIRE, d(4, 4), BURNING_OIL, EXPL_FIERY);
857 /* lit potion of oil is exploding; extinguish it as a light source before
858 possibly killing the hero and attempting to save bones */
860 explode_oil(obj, x, y)
865 impossible("exploding unlit oil");
867 splatter_burning_oil(x, y);