NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / src / explode.c
blob2d598b36e650568129a4f010177137dd81af8d6f
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. */
5 #include "hack.h"
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.
27 void
28 explode(x, y, type, dam, olet, expltype)
29 int x, y;
30 int type; /* the same as in zap.c; passes -(wand typ) for some WAND_CLASS */
31 int dam;
32 char olet;
33 int expltype;
35 int i, j, k, damu = dam;
36 boolean starting = 1;
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;
42 uchar adtyp;
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 */
52 if (type < 0) {
53 type = -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);
62 type = 0;
64 } else
65 type = 0;
67 switch (Role_switch) {
68 case PM_PRIEST:
69 case PM_MONK:
70 case PM_WIZARD:
71 damu /= 5;
72 break;
73 case PM_HEALER:
74 case PM_KNIGHT:
75 damu /= 2;
76 break;
77 default:
78 break;
81 /* muse_unslime: SCR_FIRE */
82 if (expltype < 0) {
83 /* hero gets credit/blame for killing this monster, not others */
84 mdef = m_at(x, y);
85 expltype = -expltype;
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) {
92 str = killer.name;
93 do_hallu = Hallucination && strstri(str, "'s explosion");
94 adtyp = AD_PHYS;
95 } else
96 switch (abs(type) % 10) {
97 case 0:
98 str = "magical blast";
99 adtyp = AD_MAGM;
100 break;
101 case 1:
102 str = (olet == BURNING_OIL) ? "burning oil"
103 : (olet == SCROLL_CLASS) ? "tower of flame" : "fireball";
104 /* fire damage, not physical damage */
105 adtyp = AD_FIRE;
106 break;
107 case 2:
108 str = "ball of cold";
109 adtyp = AD_COLD;
110 break;
111 case 4:
112 str = (olet == WAND_CLASS) ? "death field"
113 : "disintegration field";
114 adtyp = AD_DISN;
115 break;
116 case 5:
117 str = "ball of lightning";
118 adtyp = AD_ELEC;
119 break;
120 case 6:
121 str = "poison gas cloud";
122 adtyp = AD_DRST;
123 break;
124 case 7:
125 str = "splash of acid";
126 adtyp = AD_ACID;
127 break;
128 default:
129 impossible("explosion base type %d?", type);
130 return;
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)) {
137 explmask[i][j] = 2;
138 continue;
139 } else
140 explmask[i][j] = 0;
142 if (i + x - 1 == u.ux && j + y - 1 == u.uy) {
143 switch (adtyp) {
144 case AD_PHYS:
145 explmask[i][j] = 0;
146 break;
147 case AD_MAGM:
148 explmask[i][j] = !!Antimagic;
149 break;
150 case AD_FIRE:
151 explmask[i][j] = !!Fire_resistance;
152 break;
153 case AD_COLD:
154 explmask[i][j] = !!Cold_resistance;
155 break;
156 case AD_DISN:
157 explmask[i][j] = (olet == WAND_CLASS)
158 ? !!(nonliving(youmonst.data)
159 || is_demon(youmonst.data))
160 : !!Disint_resistance;
161 break;
162 case AD_ELEC:
163 explmask[i][j] = !!Shock_resistance;
164 break;
165 case AD_DRST:
166 explmask[i][j] = !!Poison_resistance;
167 break;
168 case AD_ACID:
169 explmask[i][j] = !!Acid_resistance;
170 physical_dmg = TRUE;
171 break;
172 default:
173 impossible("explosion type %d?", adtyp);
174 break;
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)
180 mtmp = u.usteed;
181 if (mtmp) {
182 if (mtmp->mhp < 1)
183 explmask[i][j] = 2;
184 else
185 switch (adtyp) {
186 case AD_PHYS:
187 break;
188 case AD_MAGM:
189 explmask[i][j] |= resists_magm(mtmp);
190 break;
191 case AD_FIRE:
192 explmask[i][j] |= resists_fire(mtmp);
193 break;
194 case AD_COLD:
195 explmask[i][j] |= resists_cold(mtmp);
196 break;
197 case AD_DISN:
198 explmask[i][j] |= (olet == WAND_CLASS)
199 ? (nonliving(mtmp->data)
200 || is_demon(mtmp->data)
201 || is_vampshifter(mtmp))
202 : resists_disint(mtmp);
203 break;
204 case AD_ELEC:
205 explmask[i][j] |= resists_elec(mtmp);
206 break;
207 case AD_DRST:
208 explmask[i][j] |= resists_poison(mtmp);
209 break;
210 case AD_ACID:
211 explmask[i][j] |= resists_acid(mtmp);
212 break;
213 default:
214 impossible("explosion type %d?", adtyp);
215 break;
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))
226 visible = TRUE;
227 if (explmask[i][j] == 1)
228 any_shield = TRUE;
231 if (visible) {
232 /* Start the explosion */
233 for (i = 0; i < 3; i++)
234 for (j = 0; j < 3; j++) {
235 if (explmask[i][j] == 2)
236 continue;
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);
240 starting = 0;
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 */
258 delay_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)
265 show_glyph(
266 i + x - 1, j + y - 1,
267 explosion_to_glyph(expltype, explosion[i][j]));
270 } else { /* delay a little bit. */
271 delay_output();
272 delay_output();
275 tmp_at(DISP_END, 0); /* clear the explosion */
276 } else {
277 if (olet == MON_EXPLODE) {
278 str = "explosion";
279 generic = TRUE;
281 if (!Deaf && olet != SCROLL_CLASS)
282 You_hear("a blast.");
285 if (dam)
286 for (i = 0; i < 3; i++)
287 for (j = 0; j < 3; j++) {
288 if (explmask[i][j] == 2)
289 continue;
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)
294 continue;
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)
303 mtmp = u.usteed;
304 if (!mtmp)
305 continue;
306 if (do_hallu) {
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) */
311 do {
312 Sprintf(hallu_buf, "%s explosion",
313 s_suffix(rndmonnam((char *) 0)));
314 } while (*hallu_buf != lowc(*hallu_buf));
315 str = hallu_buf;
317 if (u.uswallow && mtmp == u.ustuck) {
318 const char *adj = (char *) 0;
320 if (is_animal(u.ustuck->data)) {
321 switch (adtyp) {
322 case AD_FIRE:
323 adj = "heartburn";
324 break;
325 case AD_COLD:
326 adj = "chilly";
327 break;
328 case AD_DISN:
329 if (olet == WAND_CLASS)
330 adj = "irradiated by pure energy";
331 else
332 adj = "perforated";
333 break;
334 case AD_ELEC:
335 adj = "shocked";
336 break;
337 case AD_DRST:
338 adj = "poisoned";
339 break;
340 case AD_ACID:
341 adj = "an upset stomach";
342 break;
343 default:
344 adj = "fried";
345 break;
347 pline("%s gets %s!", Monnam(u.ustuck), adj);
348 } else {
349 switch (adtyp) {
350 case AD_FIRE:
351 adj = "toasted";
352 break;
353 case AD_COLD:
354 adj = "chilly";
355 break;
356 case AD_DISN:
357 if (olet == WAND_CLASS)
358 adj = "overwhelmed by pure energy";
359 else
360 adj = "perforated";
361 break;
362 case AD_ELEC:
363 adj = "shocked";
364 break;
365 case AD_DRST:
366 adj = "intoxicated";
367 break;
368 case AD_ACID:
369 adj = "burned";
370 break;
371 default:
372 adj = "fried";
373 break;
375 pline("%s gets slightly %s!", Monnam(u.ustuck), adj);
377 } else if (cansee(i + x - 1, j + y - 1)) {
378 if (mtmp->m_ap_type)
379 seemimic(mtmp);
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;
392 } else {
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
395 * can
396 * call mondied, not killed, if it's not your blast
398 int mdam = dam;
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)
407 mdam *= 2;
408 if (resists_cold(mtmp) && adtyp == AD_FIRE)
409 mdam *= 2;
410 else if (resists_fire(mtmp) && adtyp == AD_COLD)
411 mdam *= 2;
412 mtmp->mhp -= mdam;
413 mtmp->mhp -= (idamres + idamnonres);
415 if (mtmp->mhp <= 0) {
416 if (!context.mon_moving) {
417 killed(mtmp);
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"
430 : "killed");
431 xkilled(mtmp, XKILL_NOMSG | XKILL_NOCONDUCT);
432 } else
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 */
441 if (uhurt) {
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) */
446 do {
447 Sprintf(hallu_buf, "%s explosion",
448 s_suffix(rndmonnam((char *) 0)));
449 } while (*hallu_buf != lowc(*hallu_buf));
450 str = 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)
457 burn_away_slime();
458 if (Invulnerable) {
459 damu = 0;
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);
472 if (uhurt == 2) {
473 if (Upolyd)
474 u.mh -= damu;
475 else
476 u.uhp -= damu;
477 context.botl = 1;
480 if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
481 if (Upolyd) {
482 rehumanize();
483 } else {
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(),
493 uhis(), str);
494 } else {
495 killer.format = (!strcmpi(str, "tower of flame")
496 || !strcmpi(str, "fireball"))
497 ? KILLED_BY_AN
498 : KILLED_BY;
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.");
504 else
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);
514 if (shopdamage) {
515 pay_for_damage(adtyp == AD_FIRE
516 ? "burn away"
517 : adtyp == AD_COLD
518 ? "shatter"
519 : adtyp == AD_DISN ? "disintegrate"
520 : "destroy",
521 FALSE);
524 /* explosions are noisy */
525 i = dam * dam;
526 if (i < 50)
527 i = 50; /* in case random damage is very small */
528 if (inside_engulfer)
529 i = (i + 3) / 4;
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 */
537 xchar oy; /* item */
538 schar dx; /* direction of */
539 schar dy; /* travel */
540 int range; /* range of object */
541 boolean stopped; /* flag for in-motion/stopped */
545 * scflags:
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 */
555 long
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;
563 register int tmp;
564 int farthest = 0;
565 uchar typ;
566 long qtmp;
567 boolean used_up;
568 boolean individual_object = obj ? TRUE : FALSE;
569 struct monst *mtmp;
570 struct scatter_chain *stmp, *stmp2 = 0;
571 struct scatter_chain *schain = (struct scatter_chain *) 0;
572 long total = 0L;
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)
578 qtmp = LARGEST_INT;
579 qtmp = (long) rnd((int) qtmp);
580 otmp = splitobj(otmp, qtmp);
581 } else {
582 obj = (struct obj *) 0; /* all used */
584 obj_extract_self(otmp);
585 used_up = FALSE;
587 /* 9 in 10 chance of fracturing boulders or statues */
588 if ((scflags & MAY_FRACTURE)
589 && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE))
590 && rn2(10)) {
591 if (otmp->otyp == BOULDER) {
592 if (cansee(sx, sy))
593 pline("%s apart.", Tobjnam(otmp, "break"));
594 else
595 You_hear("stone breaking.");
596 fracture_rock(otmp);
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);
603 } else {
604 struct trap *trap;
606 if ((trap = t_at(sx, sy)) && trap->ttyp == STATUE_TRAP)
607 deltrap(trap);
608 if (cansee(sx, sy))
609 pline("%s.", Tobjnam(otmp, "crumble"));
610 else
611 You_hear("stone crumbling.");
612 (void) break_statue(otmp);
613 place_object(otmp, sx, sy); /* put fragments on floor */
615 used_up = TRUE;
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))
622 used_up = TRUE;
625 if (!used_up) {
626 stmp = (struct scatter_chain *)
627 alloc(sizeof (struct scatter_chain));
628 stmp->next = (struct scatter_chain *) 0;
629 stmp->obj = otmp;
630 stmp->ox = sx;
631 stmp->oy = sy;
632 tmp = rn2(8); /* get the direction */
633 stmp->dx = xdir[tmp];
634 stmp->dy = ydir[tmp];
635 tmp = blastforce - (otmp->owt / 40);
636 if (tmp < 1)
637 tmp = 1;
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;
642 if (!schain)
643 schain = stmp;
644 else
645 stmp2->next = stmp;
646 stmp2 = stmp;
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) {
667 stmp->range--;
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) {
675 int hitvalu, hitu;
677 if (multi)
678 nomul(0);
679 hitvalu = 8 + stmp->obj->spe;
680 if (bigmonst(youmonst.data))
681 hitvalu++;
682 hitu = thitu(hitvalu, dmgval(stmp->obj, &youmonst),
683 stmp->obj, (char *) 0);
684 if (hitu) {
685 stmp->range -= 3;
686 stop_occupation();
689 } else {
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) {
701 int x, y;
703 stmp2 = stmp->next;
704 x = stmp->ox;
705 y = stmp->oy;
706 if (stmp->obj) {
707 if (x != sx || y != sy)
708 total += stmp->obj->quan;
709 place_object(stmp->obj, x, y);
710 stackobj(stmp->obj);
712 free((genericptr_t) stmp);
713 newsym(x, y);
716 return total;
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.
731 void
732 splatter_burning_oil(x, y)
733 int 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 */
742 void
743 explode_oil(obj, x, y)
744 struct obj *obj;
745 int x, y;
747 if (!obj->lamplit)
748 impossible("exploding unlit oil");
749 end_burn(obj, TRUE);
750 splatter_burning_oil(x, y);
753 /*explode.c*/