1 /* NetHack 3.7 explode.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.122 $ */
2 /* Copyright (C) 1990 by Ken Arromdee */
3 /* NetHack may be freely redistributed. See license for details. */
7 staticfn
int explosionmask(struct monst
*, uchar
, char) NONNULLARG1
;
8 staticfn
void engulfer_explosion_msg(uchar
, char);
10 /* Note: Arrays are column first, while the screen is row first */
11 static const int explosion
[3][3] = {
12 { S_expl_tl
, S_expl_ml
, S_expl_bl
},
13 { S_expl_tc
, S_expl_mc
, S_expl_bc
},
14 { S_expl_tr
, S_expl_mr
, S_expl_br
} };
16 /* what to do at [x+i][y+j] for i=-1,0,1 and j=-1,0,1 */
18 EXPL_NONE
= 0, /* not specified yet or no shield effect needed */
19 EXPL_MON
= 1, /* monster is affected */
20 EXPL_HERO
= 2, /* hero is affected */
21 EXPL_SKIP
= 4 /* don't apply shield effect (out of bounds) */
24 /* check if shield effects are needed for location affected by explosion */
27 struct monst
*m
, /* target monster (might be youmonst) */
28 uchar adtyp
, /* damage type */
29 char olet
) /* object class (only matters for AD_DISN) */
33 if (m
== &gy
.youmonst
) {
36 /* leave 'res' with EXPL_NONE */
51 if ((olet
== WAND_CLASS
)
52 ? (nonliving(m
->data
) || is_demon(m
->data
))
61 if (Poison_resistance
)
69 impossible("explosion type %d?", adtyp
);
74 /* 'm' is a monster */
91 if ((olet
== WAND_CLASS
)
92 ? (nonliving(m
->data
) || is_demon(m
->data
)
94 : !!resists_disint(m
))
102 if (resists_poison(m
))
110 impossible("explosion type %d?", adtyp
);
118 engulfer_explosion_msg(uchar adtyp
, char olet
)
120 const char *adj
= (char *) 0;
122 if (digests(u
.ustuck
->data
)) {
131 if (olet
== WAND_CLASS
)
132 adj
= "irradiated by pure energy";
143 adj
= "an upset stomach";
149 pline("%s gets %s!", Monnam(u
.ustuck
), adj
);
159 if (olet
== WAND_CLASS
)
160 adj
= "overwhelmed by pure energy";
177 pline("%s gets slightly %s!", Monnam(u
.ustuck
), adj
);
181 /* Note: I had to choose one of three possible kinds of "type" when writing
182 * this function: a wand type (like in zap.c), an adtyp, or an object type.
183 * Wand types get complex because they must be converted to adtyps for
184 * determining such things as fire resistance. Adtyps get complex in that
185 * they don't supply enough information--was it a player or a monster that
186 * did it, and with a wand, spell, or breath weapon? Object types share both
187 * these disadvantages....
189 * Note: anything with a AT_BOOM AD_PHYS attack uses PHYS_EXPL_TYPE for type.
191 * Important note about Half_physical_damage:
192 * Unlike losehp(), explode() makes the Half_physical_damage adjustments
193 * itself, so the caller should never have done that ahead of time.
194 * It has to be done this way because the damage value is applied to
195 * things beside the player. Care is taken within explode() to ensure
196 * that Half_physical_damage only affects the damage applied to the hero.
200 coordxy x
, coordxy y
, /* explosion's location;
201 * adjacent spots are also affected */
202 int type
, /* same as in zap.c; -(wand typ) for some WAND_CLASS */
203 int dam
, /* damage amount */
204 char olet
, /* object class or BURNING_OIL or MON_EXPLODE */
205 int expltype
) /* explosion type: controls color of explosion glyphs */
207 int i
, j
, k
, damu
= dam
;
208 boolean starting
= 1;
209 boolean visible
, any_shield
;
210 int uhurt
= 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */
211 const char *str
= (const char *) 0;
212 struct monst
*mtmp
, *mdef
= 0;
214 int explmask
[3][3]; /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
216 boolean shopdamage
= FALSE
, generic
= FALSE
,
217 do_hallu
= FALSE
, inside_engulfer
, grabbed
, grabbing
;
219 char hallu_buf
[BUFSZ
], killr_buf
[BUFSZ
];
220 short exploding_wand_typ
= 0;
221 boolean you_exploding
= (olet
== MON_EXPLODE
&& type
>= 0);
222 boolean didmsg
= FALSE
;
224 if (olet
== WAND_CLASS
) { /* retributive strike */
225 /* 'type' is passed as (wand's object type * -1); save
226 object type and convert 'type' itself to zap-type */
229 exploding_wand_typ
= (short) type
;
230 /* most attack wands produce specific explosions;
231 other types produce a generic magical explosion */
232 if (objects
[type
].oc_dir
== RAY
233 && type
!= WAN_DIGGING
&& type
!= WAN_SLEEP
) {
234 type
-= WAN_MAGIC_MISSILE
;
235 if (type
< 0 || type
> 9) {
236 impossible("explode: wand has bad zap type (%d).", type
);
242 switch (Role_switch
) {
255 } else if (olet
== BURNING_OIL
) {
256 /* used to provide extra information to zap_over_floor() */
257 exploding_wand_typ
= POT_OIL
;
258 } else if (olet
== SCROLL_CLASS
) {
260 exploding_wand_typ
= SCR_FIRE
;
261 } else if (olet
== TRAP_EXPLODE
) {
262 type
= 0; /* hardcoded to generic magic explosion */
264 /* muse_unslime: SCR_FIRE */
266 /* hero gets credit/blame for killing this monster, not others */
268 expltype
= -expltype
;
270 /* if hero is engulfed and caused the explosion, only hero and
271 engulfer will be affected */
272 inside_engulfer
= (u
.uswallow
&& type
>= 0);
273 /* held but not engulfed implies holder is reaching into second spot
274 so might get hit by double damage */
275 grabbed
= grabbing
= FALSE
;
276 if (u
.ustuck
&& !u
.uswallow
) {
277 if (Upolyd
&& sticks(gy
.youmonst
.data
))
281 grabxy
.x
= u
.ustuck
->mx
;
282 grabxy
.y
= u
.ustuck
->my
;
284 grabxy
.x
= grabxy
.y
= 0; /* lint suppression */
286 * It is possible for a grabber to be outside the explosion
287 * radius and reaching inside to hold the hero. If so, it ought
288 * to take damage (the extra half of double damage). It is also
289 * possible for poly'd hero to be outside the radius and reaching
290 * in to hold a monster. Hero should take damage in that situation.
292 * Probably the simplest way to handle this would be to expand
293 * the radius used when collecting targets but exclude everything
294 * beyond the regular radius which isn't reaching inside. Then
295 * skip harm to gear of any extended targets when inflicting damage.
298 if (olet
== MON_EXPLODE
&& !you_exploding
) {
299 /* when explode() is called recursively, svk.killer.name might change
300 so retain a copy of the current value for this explosion */
301 str
= strcpy(killr_buf
, svk
.killer
.name
);
302 do_hallu
= (Hallucination
303 && (strstri(str
, "'s explosion")
304 || strstri(str
, "s' explosion")));
306 if (type
== PHYS_EXPL_TYPE
) {
307 /* currently only gas spores */
310 /* If str is e.g. "flaming sphere's explosion" from above, we want to
311 * still assign adtyp appropriately, but not replace str. */
312 const char *adstr
= NULL
;
314 switch (abs(type
) % 10) {
316 adstr
= "magical blast";
320 adstr
= (olet
== BURNING_OIL
) ? "burning oil"
321 : (olet
== SCROLL_CLASS
) ? "tower of flame" : "fireball";
322 /* fire damage, not physical damage */
326 adstr
= "ball of cold";
330 adstr
= (olet
== WAND_CLASS
) ? "death field"
331 : "disintegration field";
335 adstr
= "ball of lightning";
339 adstr
= "poison gas cloud";
343 adstr
= "splash of acid";
347 impossible("explosion base type %d?", type
);
354 any_shield
= visible
= FALSE
;
355 for (i
= 0; i
< 3; i
++)
356 for (j
= 0; j
< 3; j
++) {
360 explmask
[i
][j
] = EXPL_SKIP
;
363 explmask
[i
][j
] = EXPL_NONE
;
366 explmask
[i
][j
] = explosionmask(&gy
.youmonst
, adtyp
, olet
);
368 /* can be both you and mtmp if you're swallowed or riding */
370 if (!mtmp
&& u_at(xx
, yy
))
372 if (mtmp
&& DEADMONSTER(mtmp
))
375 explmask
[i
][j
] |= explosionmask(mtmp
, adtyp
, olet
);
378 if (mtmp
&& cansee(xx
, yy
) && !canspotmon(mtmp
))
379 map_invisible(xx
, yy
);
381 (void) unmap_invisible(xx
, yy
);
384 if ((explmask
[i
][j
] & (EXPL_MON
| EXPL_HERO
)) != 0)
389 /* Start the explosion */
390 for (i
= 0; i
< 3; i
++)
391 for (j
= 0; j
< 3; j
++) {
392 if (explmask
[i
][j
] == EXPL_SKIP
)
396 tmp_at(starting
? DISP_BEAM
: DISP_CHANGE
,
397 explosion_to_glyph(expltype
, explosion
[i
][j
]));
401 curs_on_u(); /* will flush screen and output */
403 if (any_shield
&& flags
.sparkle
) { /* simulate shield effect */
404 for (k
= 0; k
< SHIELD_COUNT
; k
++) {
405 for (i
= 0; i
< 3; i
++)
406 for (j
= 0; j
< 3; j
++) {
409 if ((explmask
[i
][j
] & (EXPL_MON
| EXPL_HERO
)) != 0)
411 * Bypass tmp_at() and send the shield glyphs
412 * directly to the buffered screen. tmp_at()
413 * will clean up the location for us later.
416 cmap_to_glyph(shield_static
[k
]));
418 curs_on_u(); /* will flush screen and output */
422 /* Cover last shield glyph with blast symbol. */
423 for (i
= 0; i
< 3; i
++)
424 for (j
= 0; j
< 3; j
++) {
427 if ((explmask
[i
][j
] & (EXPL_MON
| EXPL_HERO
)) != 0)
429 explosion_to_glyph(expltype
,
433 } else { /* delay a little bit. */
438 tmp_at(DISP_END
, 0); /* clear the explosion */
440 if (olet
== MON_EXPLODE
|| olet
== TRAP_EXPLODE
) {
444 if (!Deaf
&& olet
!= SCROLL_CLASS
) {
445 Soundeffect(se_blast
, 75);
446 You_hear("a blast.");
451 if (!Deaf
&& !didmsg
)
454 /* apply effects to monsters and floor objects first, in case the
455 damage to the hero is fatal and leaves bones */
457 for (i
= 0; i
< 3; i
++) {
458 for (j
= 0; j
< 3; j
++) {
461 if (explmask
[i
][j
] == EXPL_SKIP
)
466 uhurt
= ((explmask
[i
][j
] & EXPL_HERO
) != 0) ? 1 : 2;
467 /* If the player is attacking via polyself into something
468 * with an explosion attack, leave them (and their gear)
469 * unharmed, to avoid punishing them from using such
470 * polyforms creatively */
471 if (!svc
.context
.mon_moving
&& you_exploding
)
473 } else if (inside_engulfer
) {
474 /* for inside_engulfer, only <u.ux,u.uy> is affected */
478 /* Affect the floor unless the player caused the explosion
479 * from inside their engulfer. */
480 if (!(u
.uswallow
&& !svc
.context
.mon_moving
))
481 (void) zap_over_floor(xx
, yy
, type
,
486 if (!mtmp
&& u_at(xx
, yy
))
493 /* replace "gas spore" with a different description
494 for each target (we can't distinguish personal names
495 like "Barney" here in order to suppress "the" below,
496 so avoid any which begins with a capital letter) */
498 Sprintf(hallu_buf
, "%s explosion",
499 s_suffix(rndmonnam((char *) 0)));
500 } while (*hallu_buf
!= lowc(*hallu_buf
) && ++tryct
< 20);
503 if (engulfing_u(mtmp
)) {
504 engulfer_explosion_msg(adtyp
, olet
);
505 } else if (cansee(xx
, yy
)) {
508 pline("%s is caught in the %s!", Monnam(mtmp
), str
);
511 itemdmg
= destroy_items(mtmp
, (int) adtyp
, dam
);
512 if (adtyp
== AD_FIRE
) {
513 (void) burnarmor(mtmp
);
514 ignite_items(mtmp
->minvent
);
517 if ((explmask
[i
][j
] & EXPL_MON
) != 0) {
518 /* Damage from ring/wand explosion isn't itself
519 * electrical in nature, nor is damage from freezing
520 * potion really cold in nature, nor is damage from
521 * boiling potion or exploding oil; only burning items
522 * damage is the "same type" as the explosion. Because
523 * this is imperfect and marginal (burning items only
524 * deal 1 damage), ignore it for golemeffects(). */
525 golemeffects(mtmp
, (int) adtyp
, dam
);
526 mtmp
->mhp
-= itemdmg
; /* item destruction dmg */
528 /* Call resist with 0 and do damage manually so 1) we can
529 * get out the message before doing the damage, and 2) we
530 * can call mondied, not killed, if it's not your blast.
534 if (resist(mtmp
, olet
, 0, FALSE
)) {
535 /* inside_engulfer: <xx,yy> == <u.ux,u.uy> */
536 if (cansee(xx
, yy
) || inside_engulfer
)
537 pline("%s resists the %s!", Monnam(mtmp
), str
);
538 mdam
= (dam
+ 1) / 2;
540 /* if grabber is reaching into hero's spot and
541 hero's spot is within explosion radius, grabber
542 gets hit by double damage */
543 if (grabbed
&& mtmp
== u
.ustuck
&& next2u(x
, y
))
545 /* being resistant to opposite type of damage makes
546 target more vulnerable to current type of damage
547 (when target is also resistant to current type,
548 we won't get here) */
549 if (resists_cold(mtmp
) && adtyp
== AD_FIRE
)
551 else if (resists_fire(mtmp
) && adtyp
== AD_COLD
)
553 mtmp
->mhp
-= mdam
+ itemdmg
;
555 if (DEADMONSTER(mtmp
)) {
556 int xkflg
= ((adtyp
== AD_FIRE
557 && completelyburns(mtmp
->data
))
558 ? XKILL_NOCORPSE
: 0);
560 if (!svc
.context
.mon_moving
) {
561 xkilled(mtmp
, XKILL_GIVEMSG
| xkflg
);
562 } else if (mdef
&& mtmp
== mdef
) {
563 /* 'mdef' killed self trying to cure being turned
564 * into slime due to some action by the player.
565 * Hero gets the credit (experience) and most of
566 * the blame (possible loss of alignment and/or
567 * luck and/or telepathy depending on mtmp) but
568 * doesn't break pacifism. xkilled()'s message
569 * would be "you killed <mdef>" so give our own.
571 if (cansee(mtmp
->mx
, mtmp
->my
) || canspotmon(mtmp
))
572 pline("%s is %s!", Monnam(mtmp
),
573 xkflg
? "burned completely"
574 : nonliving(mtmp
->data
) ? "destroyed"
576 xkilled(mtmp
, XKILL_NOMSG
| XKILL_NOCONDUCT
| xkflg
);
579 adtyp
= AD_RBRE
; /* no corpse */
580 monkilled(mtmp
, "", (int) adtyp
);
582 } else if (!svc
.context
.mon_moving
) {
583 /* all affected monsters, even if mdef is set */
584 setmangry(mtmp
, TRUE
);
590 /* Do your injury last */
592 /* give message for any monster-induced explosion
593 or player-induced one other than scroll of fire */
594 if (flags
.verbose
&& (type
< 0 || olet
!= SCROLL_CLASS
)) {
595 if (do_hallu
) { /* (see explanation above) */
597 Sprintf(hallu_buf
, "%s explosion",
598 s_suffix(rndmonnam((char *) 0)));
599 } while (*hallu_buf
!= lowc(*hallu_buf
));
602 You("are caught in the %s!", str
);
603 iflags
.last_msg
= PLNMSG_CAUGHT_IN_EXPLOSION
;
605 /* do property damage first, in case we end up leaving bones */
606 if (adtyp
== AD_FIRE
)
610 You("are unharmed!");
611 } else if (adtyp
== AD_PHYS
|| adtyp
== AD_ACID
)
612 damu
= Maybe_Half_Phys(damu
);
613 if (adtyp
== AD_FIRE
) {
614 (void) burnarmor(&gy
.youmonst
);
615 ignite_items(gi
.invent
);
617 (void) destroy_items(&gy
.youmonst
, (int) adtyp
, dam
);
619 ugolemeffects((int) adtyp
, damu
);
621 /* if poly'd hero is grabbing another victim, hero takes
622 double damage (note: don't rely on u.ustuck here because
623 that victim might have been killed when hit by the blast) */
624 if (grabbing
&& dist2((int) grabxy
.x
, (int) grabxy
.y
, x
, y
) <= 2)
626 /* hero does not get same fire-resistant vs cold and
627 cold-resistant vs fire double damage as monsters [why not?] */
635 /* You resisted the damage, lets not keep that to ourselves */
637 monstseesu_ad(adtyp
);
639 monstunseesu_ad(adtyp
);
641 if (u
.uhp
<= 0 || (Upolyd
&& u
.mh
<= 0)) {
645 if (olet
== MON_EXPLODE
) {
646 if (generic
) /* explosion was unseen; str=="explosion", */
647 ; /* svk.killer.name=="gas spore's explosion". */
648 else if (str
!= svk
.killer
.name
&& str
!= hallu_buf
)
649 Strcpy(svk
.killer
.name
, str
);
650 svk
.killer
.format
= KILLED_BY_AN
;
651 } else if (olet
== TRAP_EXPLODE
) {
652 svk
.killer
.format
= NO_KILLER_PREFIX
;
653 Snprintf(svk
.killer
.name
, sizeof svk
.killer
.name
,
654 "caught %sself in a %s", uhim(),
656 } else if (type
>= 0 && olet
!= SCROLL_CLASS
) {
657 svk
.killer
.format
= NO_KILLER_PREFIX
;
658 Snprintf(svk
.killer
.name
, sizeof svk
.killer
.name
,
659 "caught %sself in %s own %s", uhim(),
662 svk
.killer
.format
= (!strcmpi(str
, "tower of flame")
663 || !strcmpi(str
, "fireball"))
666 Strcpy(svk
.killer
.name
, str
);
668 if (iflags
.last_msg
== PLNMSG_CAUGHT_IN_EXPLOSION
669 || iflags
.last_msg
== PLNMSG_TOWER_OF_FLAME
) /*seffects()*/
670 pline("It is fatal.");
672 pline_The("%s is fatal.", str
);
673 /* Known BUG: BURNING suppresses corpse in bones data,
674 but done does not handle killer reason correctly */
675 done((adtyp
== AD_FIRE
) ? BURNING
: DIED
);
678 exercise(A_STR
, FALSE
);
682 pay_for_damage((adtyp
== AD_FIRE
) ? "burn away"
683 : (adtyp
== AD_COLD
) ? "shatter"
684 : (adtyp
== AD_DISN
) ? "disintegrate"
689 /* explosions are noisy */
692 i
= 50; /* in case random damage is very small */
695 wake_nearto(x
, y
, i
);
698 struct scatter_chain
{
699 struct scatter_chain
*next
; /* pointer to next scatter item */
700 struct obj
*obj
; /* pointer to the object */
701 coordxy ox
; /* location of */
702 coordxy oy
; /* item */
703 schar dx
; /* direction of */
704 schar dy
; /* travel */
705 int range
; /* range of object */
706 boolean stopped
; /* flag for in-motion/stopped */
711 * VIS_EFFECTS Add visual effects to display
712 * MAY_HITMON Objects may hit monsters
713 * MAY_HITYOU Objects may hit hero
714 * MAY_HIT Objects may hit you or monsters
715 * MAY_DESTROY Objects may be destroyed at random
716 * MAY_FRACTURE Stone objects can be fractured (statues, boulders)
719 /* returns number of scattered objects */
721 scatter(coordxy sx
, coordxy sy
, /* location of objects to scatter */
722 int blastforce
, /* force behind the scattering */
723 unsigned int scflags
,
724 struct obj
*obj
) /* only scatter this obj */
732 boolean individual_object
= obj
? TRUE
: FALSE
;
733 boolean shop_origin
, lostgoods
= FALSE
;
734 struct monst
*mtmp
, *shkp
= 0;
735 struct scatter_chain
*stmp
, *stmp2
= 0;
736 struct scatter_chain
*schain
= (struct scatter_chain
*) 0;
739 if (individual_object
&& (obj
->ox
!= sx
|| obj
->oy
!= sy
))
740 impossible("scattered object <%d,%d> not at scatter site <%d,%d>",
741 obj
->ox
, obj
->oy
, sx
, sy
);
743 shop_origin
= ((shkp
= shop_keeper(*in_rooms(sx
, sy
, SHOPBASE
))) != 0
744 && costly_spot(sx
, sy
));
746 credit_report(shkp
, 0, TRUE
); /* establish baseline, without msgs */
748 while ((otmp
= (individual_object
? obj
749 : svl
.level
.objects
[sx
][sy
])) != 0) {
750 if (otmp
== uball
|| otmp
== uchain
) {
751 boolean waschain
= (otmp
== uchain
);
753 Soundeffect(se_chain_shatters
, 25);
754 pline_The("chain shatters!");
759 if (otmp
->quan
> 1L) {
760 qtmp
= otmp
->quan
- 1L;
761 if (qtmp
> LARGEST_INT
)
763 qtmp
= (long) rnd((int) qtmp
);
764 otmp
= splitobj(otmp
, qtmp
);
766 obj
= (struct obj
*) 0; /* all used */
768 obj_extract_self(otmp
);
771 /* 9 in 10 chance of fracturing boulders or statues */
772 if ((scflags
& MAY_FRACTURE
) != 0
773 && (otmp
->otyp
== BOULDER
|| otmp
->otyp
== STATUE
)
775 if (otmp
->otyp
== BOULDER
) {
776 if (cansee(sx
, sy
)) {
777 pline("%s apart.", Tobjnam(otmp
, "break"));
779 Soundeffect(se_stone_breaking
, 100);
780 You_hear("stone breaking.");
783 place_object(otmp
, sx
, sy
);
784 if ((otmp
= sobj_at(BOULDER
, sx
, sy
)) != 0) {
785 /* another boulder here, restack it to the top */
786 obj_extract_self(otmp
);
787 place_object(otmp
, sx
, sy
);
792 if ((trap
= t_at(sx
, sy
)) && trap
->ttyp
== STATUE_TRAP
)
794 if (cansee(sx
, sy
)) {
795 pline("%s.", Tobjnam(otmp
, "crumble"));
797 Soundeffect(se_stone_crumbling
, 100);
798 You_hear("stone crumbling.");
800 (void) break_statue(otmp
);
801 place_object(otmp
, sx
, sy
); /* put fragments on floor */
803 newsym(sx
, sy
); /* in case it's beyond radius of 'farthest' */
806 /* 1 in 10 chance of destruction of obj; glass, egg destruction */
807 } else if ((scflags
& MAY_DESTROY
) != 0
808 && (!rn2(10) || (objects
[otmp
->otyp
].oc_material
== GLASS
809 || otmp
->otyp
== EGG
))) {
810 if (breaks(otmp
, sx
, sy
))
815 stmp
= (struct scatter_chain
*) alloc(sizeof *stmp
);
816 stmp
->next
= (struct scatter_chain
*) 0;
820 tmp
= rn2(N_DIRS
); /* get the direction */
821 stmp
->dx
= xdir
[tmp
];
822 stmp
->dy
= ydir
[tmp
];
823 tmp
= blastforce
- (otmp
->owt
/ 40);
826 stmp
->range
= rnd(tmp
); /* anywhere up to that determ. by wt */
827 if (farthest
< stmp
->range
)
828 farthest
= stmp
->range
;
829 stmp
->stopped
= FALSE
;
838 while (farthest
-- > 0) {
839 for (stmp
= schain
; stmp
; stmp
= stmp
->next
) {
840 if ((stmp
->range
-- > 0) && (!stmp
->stopped
)) {
841 gt
.thrownobj
= stmp
->obj
; /* mainly in case it kills hero */
842 gb
.bhitpos
.x
= stmp
->ox
+ stmp
->dx
;
843 gb
.bhitpos
.y
= stmp
->oy
+ stmp
->dy
;
844 if (isok(gb
.bhitpos
.x
, gb
.bhitpos
.y
))
845 typ
= levl
[gb
.bhitpos
.x
][gb
.bhitpos
.y
].typ
;
848 if (!isok(gb
.bhitpos
.x
, gb
.bhitpos
.y
)) {
849 gb
.bhitpos
.x
-= stmp
->dx
;
850 gb
.bhitpos
.y
-= stmp
->dy
;
851 stmp
->stopped
= TRUE
;
852 } else if (!ZAP_POS(typ
)
853 || closed_door(gb
.bhitpos
.x
, gb
.bhitpos
.y
)) {
854 gb
.bhitpos
.x
-= stmp
->dx
;
855 gb
.bhitpos
.y
-= stmp
->dy
;
856 stmp
->stopped
= TRUE
;
857 } else if ((mtmp
= m_at(gb
.bhitpos
.x
, gb
.bhitpos
.y
)) != 0) {
858 if (scflags
& MAY_HITMON
) {
860 if (ohitmon(mtmp
, stmp
->obj
, 1, FALSE
)) {
861 stmp
->obj
= (struct obj
*) 0;
862 stmp
->stopped
= TRUE
;
865 } else if (u_at(gb
.bhitpos
.x
, gb
.bhitpos
.y
)) {
866 if (scflags
& MAY_HITYOU
) {
871 hitvalu
= 8 + stmp
->obj
->spe
;
872 if (bigmonst(gy
.youmonst
.data
))
874 hitu
= thitu(hitvalu
, dmgval(stmp
->obj
, &gy
.youmonst
),
875 &stmp
->obj
, (char *) 0);
877 stmp
->stopped
= TRUE
;
884 if (scflags
& VIS_EFFECTS
) {
885 /* tmp_at(gb.bhitpos.x, gb.bhitpos.y); */
886 /* nh_delay_output(); */
889 stmp
->ox
= gb
.bhitpos
.x
;
890 stmp
->oy
= gb
.bhitpos
.y
;
891 if (IS_SINK(levl
[stmp
->ox
][stmp
->oy
].typ
))
892 stmp
->stopped
= TRUE
;
893 gt
.thrownobj
= (struct obj
*) 0;
897 for (stmp
= schain
; stmp
; stmp
= stmp2
) {
899 boolean obj_left_shop
= FALSE
;
905 if (x
!= sx
|| y
!= sy
) {
906 total
+= stmp
->obj
->quan
;
907 obj_left_shop
= (shop_origin
&& !costly_spot(x
, y
));
909 if (!flooreffects(stmp
->obj
, x
, y
, "land")) {
911 && strchr(u
.urooms
, *in_rooms(u
.ux
, u
.uy
, SHOPBASE
))) {
912 /* At the moment this only takes on gold. While it is
913 simple enough to call addtobill for other items that
914 leave the shop due to scatter(), by default the hero
915 will get billed for the full shopkeeper asking-price
916 on the object's way out of shop. That can leave the
917 hero in a pickle. Even if the hero then manages to
918 retrieve the item and drop it back inside the shop,
919 the owed charges will only be reduced at that point
920 by the lesser shopkeeper buying-price.
921 The non-gold situation will likely get adjusted
924 if (stmp
->obj
->otyp
== GOLD_PIECE
) {
925 addtobill(stmp
->obj
, FALSE
, FALSE
, TRUE
);
929 place_object(stmp
->obj
, x
, y
);
933 free((genericptr_t
) stmp
);
937 if (u_at(sx
, sy
) && u
.uundetected
&& hides_under(gy
.youmonst
.data
))
938 (void) hideunder(&gy
.youmonst
);
939 if (((mtmp
= m_at(sx
, sy
)) != 0) && mtmp
->mtrapped
)
941 maybe_unhide_at(sx
, sy
);
942 if (lostgoods
) /* implies shop_origin and therefore shkp valid */
943 credit_report(shkp
, 1, FALSE
);
948 * Splatter burning oil from x,y to the surrounding area.
950 * This routine should really take a how and direction parameters.
951 * The how is how it was caused, e.g. kicked verses thrown. The
952 * direction is which way to spread the flaming oil. Different
953 * "how"s would give different dispersal patterns. For example,
954 * kicking a burning flask will splatter differently from a thrown
955 * flask hitting the ground.
957 * For now, just perform a "regular" explosion.
960 splatter_burning_oil(coordxy x
, coordxy y
, boolean diluted_oil
)
962 int dmg
= d(diluted_oil
? 3 : 4, 4);
964 /* ZT_SPELL(ZT_FIRE) = ZT_SPELL(AD_FIRE-1) = 10+(2-1) = 11 */
965 #define ZT_SPELL_O_FIRE 11 /* value kludge, see zap.c */
966 explode(x
, y
, ZT_SPELL_O_FIRE
, dmg
, BURNING_OIL
, EXPL_FIERY
);
969 /* lit potion of oil is exploding; extinguish it as a light source before
970 possibly killing the hero and attempting to save bones */
972 explode_oil(struct obj
*obj
, coordxy x
, coordxy y
)
974 boolean diluted_oil
= obj
->odiluted
;
977 impossible("exploding unlit oil");
979 obj
->how_lost
= LOST_EXPLODING
;
980 splatter_burning_oil(x
, y
, diluted_oil
);
983 /* Convert a damage type into an explosion display type. */
985 adtyp_to_expltype(const int adtyp
)
989 /* Electricity isn't magical, but there currently isn't an electric
990 * explosion type. Magical is the next best thing. */
1004 case AD_PHYS
: /* gas spore */
1005 return EXPL_NOXIOUS
;
1007 impossible("adtyp_to_expltype: bad explosion type %d", adtyp
);
1012 /* A monster explodes in a way that produces a real explosion (e.g. a sphere
1013 * or gas spore, not a yellow light or similar).
1014 * This is some common code between explmu() and explmm().
1019 struct attack
*mattk
)
1024 dmg
= d((int) mattk
->damn
, (int) mattk
->damd
);
1026 else if (mattk
->damd
) {
1027 dmg
= d((int) mon
->data
->mlevel
+ 1, (int) mattk
->damd
);
1033 if (mattk
->adtyp
== AD_PHYS
) {
1034 type
= PHYS_EXPL_TYPE
;
1036 else if (mattk
->adtyp
>= AD_MAGM
&& mattk
->adtyp
<= AD_SPC2
) {
1037 /* The -1, +20, *-1 math is to set it up as a 'monster breath' type
1038 * for the explosions (it isn't, but this is the closest analogue). */
1039 /* FIXME: there are macros for kind of thing... */
1040 type
= -((mattk
->adtyp
- 1) + 20);
1043 impossible("unknown type for mon_explode %d", mattk
->adtyp
);
1047 /* Kill it now so it won't appear to be caught in its own explosion.
1048 * Must check to see if already dead - which happens if this is called
1049 * from an AT_BOOM attack upon death. */
1050 if (!DEADMONSTER(mon
)) {
1054 /* This might end up killing you, too; you never know...
1055 * also, it is used in explode() messages */
1056 Sprintf(svk
.killer
.name
, "%s explosion",
1057 s_suffix(pmname(mon
->data
, Mgender(mon
))));
1058 svk
.killer
.format
= KILLED_BY_AN
;
1060 explode(mon
->mx
, mon
->my
, type
, dmg
, MON_EXPLODE
,
1061 adtyp_to_expltype(mattk
->adtyp
));
1064 svk
.killer
.name
[0] = '\0';