1/*	SCCS Id: @(#)explode.c	3.4	2002/11/10	*/
2/*	Copyright (C) 1990 by Ken Arromdee */
3/* NetHack may be freely redistributed.  See license for details. */
4
5#include "hack.h"
6
7#ifdef OVL0
8
9/* Note: Arrays are column first, while the screen is row first */
10static int expl[3][3] = {
11	{ S_explode1, S_explode4, S_explode7 },
12	{ S_explode2, S_explode5, S_explode8 },
13	{ S_explode3, S_explode6, S_explode9 }
14};
15
16/* Note: I had to choose one of three possible kinds of "type" when writing
17 * this function: a wand type (like in zap.c), an adtyp, or an object type.
18 * Wand types get complex because they must be converted to adtyps for
19 * determining such things as fire resistance.  Adtyps get complex in that
20 * they don't supply enough information--was it a player or a monster that
21 * did it, and with a wand, spell, or breath weapon?  Object types share both
22 * these disadvantages....
23 */
24void
25explode(x, y, type, dam, olet, expltype)
26int x, y;
27int type; /* the same as in zap.c */
28int dam;
29char olet;
30int expltype;
31{
32	int i, j, k, damu = dam;
33	boolean starting = 1;
34	boolean visible, any_shield;
35	int uhurt = 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */
36	const char *str;
37	int idamres, idamnonres;
38	struct monst *mtmp;
39	uchar adtyp;
40	int explmask[3][3];
41		/* 0=normal explosion, 1=do shieldeff, 2=do nothing */
42	boolean shopdamage = FALSE;
43	boolean generic = FALSE;
44
45	if (olet == WAND_CLASS)		/* retributive strike */
46		switch (Role_switch) {
47			case PM_PRIEST:
48			case PM_MONK:
49			case PM_WIZARD: damu /= 5;
50				  break;
51			case PM_HEALER:
52			case PM_KNIGHT: damu /= 2;
53				  break;
54			default:  break;
55		}
56
57	if (olet == MON_EXPLODE) {
58	    str = killer;
59	    killer = 0;		/* set again later as needed */
60	    adtyp = AD_PHYS;
61	} else
62	switch (abs(type) % 10) {
63		case 0: str = "magical blast";
64			adtyp = AD_MAGM;
65			break;
66		case 1: str =   olet == BURNING_OIL ?	"burning oil" :
67				olet == SCROLL_CLASS ?	"tower of flame" :
68							"fireball";
69			adtyp = AD_FIRE;
70			break;
71		case 2: str = "ball of cold";
72			adtyp = AD_COLD;
73			break;
74		case 4: str =  (olet == WAND_CLASS) ? "death field" :
75							"disintegration field";
76			adtyp = AD_DISN;
77			break;
78		case 5: str = "ball of lightning";
79			adtyp = AD_ELEC;
80			break;
81		case 6: str = "poison gas cloud";
82			adtyp = AD_DRST;
83			break;
84		case 7: str = "splash of acid";
85			adtyp = AD_ACID;
86			break;
87		default: impossible("explosion base type %d?", type); return;
88	}
89
90	any_shield = visible = FALSE;
91	for (i=0; i<3; i++) for (j=0; j<3; j++) {
92		if (!isok(i+x-1, j+y-1)) {
93			explmask[i][j] = 2;
94			continue;
95		} else
96			explmask[i][j] = 0;
97
98		if (i+x-1 == u.ux && j+y-1 == u.uy) {
99		    switch(adtyp) {
100			case AD_PHYS:
101				explmask[i][j] = 0;
102				break;
103			case AD_MAGM:
104				explmask[i][j] = !!Antimagic;
105				break;
106			case AD_FIRE:
107				explmask[i][j] = !!Fire_resistance;
108				break;
109			case AD_COLD:
110				explmask[i][j] = !!Cold_resistance;
111				break;
112			case AD_DISN:
113				explmask[i][j] = (olet == WAND_CLASS) ?
114						!!(nonliving(youmonst.data) || is_demon(youmonst.data)) :
115						!!Disint_resistance;
116				break;
117			case AD_ELEC:
118				explmask[i][j] = !!Shock_resistance;
119				break;
120			case AD_DRST:
121				explmask[i][j] = !!Poison_resistance;
122				break;
123			case AD_ACID:
124				explmask[i][j] = !!Acid_resistance;
125				break;
126			default:
127				impossible("explosion type %d?", adtyp);
128				break;
129		    }
130		}
131		/* can be both you and mtmp if you're swallowed */
132		mtmp = m_at(i+x-1, j+y-1);
133#ifdef STEED
134		if (!mtmp && i+x-1 == u.ux && j+y-1 == u.uy)
135			mtmp = u.usteed;
136#endif
137		if (mtmp) {
138		    if (mtmp->mhp < 1) explmask[i][j] = 2;
139		    else switch(adtyp) {
140			case AD_PHYS:
141				break;
142			case AD_MAGM:
143				explmask[i][j] |= resists_magm(mtmp);
144				break;
145			case AD_FIRE:
146				explmask[i][j] |= resists_fire(mtmp);
147				break;
148			case AD_COLD:
149				explmask[i][j] |= resists_cold(mtmp);
150				break;
151			case AD_DISN:
152				explmask[i][j] |= (olet == WAND_CLASS) ?
153					(nonliving(mtmp->data) || is_demon(mtmp->data)) :
154					resists_disint(mtmp);
155				break;
156			case AD_ELEC:
157				explmask[i][j] |= resists_elec(mtmp);
158				break;
159			case AD_DRST:
160				explmask[i][j] |= resists_poison(mtmp);
161				break;
162			case AD_ACID:
163				explmask[i][j] |= resists_acid(mtmp);
164				break;
165			default:
166				impossible("explosion type %d?", adtyp);
167				break;
168		    }
169		}
170		if (mtmp && cansee(i+x-1,j+y-1) && !canspotmon(mtmp))
171		    map_invisible(i+x-1, j+y-1);
172		else if (!mtmp && glyph_is_invisible(levl[i+x-1][j+y-1].glyph)) {
173		    unmap_object(i+x-1, j+y-1);
174		    newsym(i+x-1, j+y-1);
175		}
176		if (cansee(i+x-1, j+y-1)) visible = TRUE;
177		if (explmask[i][j] == 1) any_shield = TRUE;
178	}
179
180	if (visible) {
181		/* Start the explosion */
182		for (i=0; i<3; i++) for (j=0; j<3; j++) {
183			if (explmask[i][j] == 2) continue;
184			tmp_at(starting ? DISP_BEAM : DISP_CHANGE,
185				explosion_to_glyph(expltype,expl[i][j]));
186			tmp_at(i+x-1, j+y-1);
187			starting = 0;
188		}
189		curs_on_u();	/* will flush screen and output */
190
191		if (any_shield && flags.sparkle) { /* simulate shield effect */
192		    for (k = 0; k < SHIELD_COUNT; k++) {
193			for (i=0; i<3; i++) for (j=0; j<3; j++) {
194			    if (explmask[i][j] == 1)
195				/*
196				 * Bypass tmp_at() and send the shield glyphs
197				 * directly to the buffered screen.  tmp_at()
198				 * will clean up the location for us later.
199				 */
200				show_glyph(i+x-1, j+y-1,
201					cmap_to_glyph(shield_static[k]));
202			}
203			curs_on_u();	/* will flush screen and output */
204			delay_output();
205		    }
206
207		    /* Cover last shield glyph with blast symbol. */
208		    for (i=0; i<3; i++) for (j=0; j<3; j++) {
209			if (explmask[i][j] == 1)
210			    show_glyph(i+x-1,j+y-1,
211					explosion_to_glyph(expltype, expl[i][j]));
212		    }
213
214		} else {		/* delay a little bit. */
215		    delay_output();
216		    delay_output();
217		}
218
219		tmp_at(DISP_END, 0); /* clear the explosion */
220	} else {
221	    if (olet == MON_EXPLODE) {
222		str = "explosion";
223		generic = TRUE;
224	    }
225	    if (flags.soundok) You_hear("a blast.");
226	}
227
228    if (dam)
229	for (i=0; i<3; i++) for (j=0; j<3; j++) {
230		if (explmask[i][j] == 2) continue;
231		if (i+x-1 == u.ux && j+y-1 == u.uy)
232			uhurt = (explmask[i][j] == 1) ? 1 : 2;
233		idamres = idamnonres = 0;
234		if (type >= 0)
235		    (void)zap_over_floor((xchar)(i+x-1), (xchar)(j+y-1),
236		    		type, &shopdamage);
237
238		mtmp = m_at(i+x-1, j+y-1);
239#ifdef STEED
240		if (!mtmp && i+x-1 == u.ux && j+y-1 == u.uy)
241			mtmp = u.usteed;
242#endif
243		if (!mtmp) continue;
244		if (u.uswallow && mtmp == u.ustuck) {
245			if (is_animal(u.ustuck->data))
246				pline("%s gets %s!",
247				      Monnam(u.ustuck),
248				      (adtyp == AD_FIRE) ? "heartburn" :
249				      (adtyp == AD_COLD) ? "chilly" :
250				      (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ?
251				       "irradiated by pure energy" : "perforated") :
252				      (adtyp == AD_ELEC) ? "shocked" :
253				      (adtyp == AD_DRST) ? "poisoned" :
254				      (adtyp == AD_ACID) ? "an upset stomach" :
255				       "fried");
256			else
257				pline("%s gets slightly %s!",
258				      Monnam(u.ustuck),
259				      (adtyp == AD_FIRE) ? "toasted" :
260				      (adtyp == AD_COLD) ? "chilly" :
261				      (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ?
262				       "overwhelmed by pure energy" : "perforated") :
263				      (adtyp == AD_ELEC) ? "shocked" :
264				      (adtyp == AD_DRST) ? "intoxicated" :
265				      (adtyp == AD_ACID) ? "burned" :
266				       "fried");
267		} else if (cansee(i+x-1, j+y-1)) {
268		    if(mtmp->m_ap_type) seemimic(mtmp);
269		    pline("%s is caught in the %s!", Monnam(mtmp), str);
270		}
271
272		idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int) adtyp);
273		idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int) adtyp);
274		idamnonres += destroy_mitem(mtmp, POTION_CLASS, (int) adtyp);
275		idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int) adtyp);
276		idamnonres += destroy_mitem(mtmp, RING_CLASS, (int) adtyp);
277
278		if (explmask[i][j] == 1) {
279			golemeffects(mtmp, (int) adtyp, dam + idamres);
280			mtmp->mhp -= idamnonres;
281		} else {
282		/* call resist with 0 and do damage manually so 1) we can
283		 * get out the message before doing the damage, and 2) we can
284		 * call mondied, not killed, if it's not your blast
285		 */
286			int mdam = dam;
287
288			if (resist(mtmp, olet, 0, FALSE)) {
289			    if (cansee(i+x-1,j+y-1))
290				pline("%s resists the %s!", Monnam(mtmp), str);
291			    mdam = dam/2;
292			}
293			if (mtmp == u.ustuck)
294				mdam *= 2;
295			if (resists_cold(mtmp) && adtyp == AD_FIRE)
296				mdam *= 2;
297			else if (resists_fire(mtmp) && adtyp == AD_COLD)
298				mdam *= 2;
299			mtmp->mhp -= mdam;
300			mtmp->mhp -= (idamres + idamnonres);
301		}
302		if (mtmp->mhp <= 0) {
303			/* KMH -- Don't blame the player for pets killing gas spores */
304			if (!flags.mon_moving) killed(mtmp);
305			else monkilled(mtmp, "", (int)adtyp);
306		} else if (!flags.mon_moving) setmangry(mtmp);
307	}
308
309	/* Do your injury last */
310	if (uhurt) {
311		if ((type >= 0 || adtyp == AD_PHYS) &&	/* gas spores */
312				flags.verbose && olet != SCROLL_CLASS)
313			You("are caught in the %s!", str);
314		/* do property damage first, in case we end up leaving bones */
315		if (adtyp == AD_FIRE) burn_away_slime();
316		if (Invulnerable) {
317		    damu = 0;
318		    You("are unharmed!");
319		} else if (Half_physical_damage && adtyp == AD_PHYS)
320		    damu = (damu+1) / 2;
321		if (adtyp == AD_FIRE) (void) burnarmor(&youmonst);
322		destroy_item(SCROLL_CLASS, (int) adtyp);
323		destroy_item(SPBOOK_CLASS, (int) adtyp);
324		destroy_item(POTION_CLASS, (int) adtyp);
325		destroy_item(RING_CLASS, (int) adtyp);
326		destroy_item(WAND_CLASS, (int) adtyp);
327
328		ugolemeffects((int) adtyp, damu);
329		if (uhurt == 2) {
330		    if (Upolyd)
331		    	u.mh  -= damu;
332		    else
333			u.uhp -= damu;
334		    flags.botl = 1;
335		}
336
337		if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
338		    if (Upolyd) {
339			rehumanize();
340		    } else {
341			if (olet == MON_EXPLODE) {
342			    /* killer handled by caller */
343			    if (str != killer_buf && !generic)
344				Strcpy(killer_buf, str);
345			    killer_format = KILLED_BY_AN;
346			} else if (type >= 0 && olet != SCROLL_CLASS) {
347			    killer_format = NO_KILLER_PREFIX;
348			    Sprintf(killer_buf, "caught %sself in %s own %s",
349				    uhim(), uhis(), str);
350			} else if (!strncmpi(str,"tower of flame", 8) ||
351				   !strncmpi(str,"fireball", 8)) {
352			    killer_format = KILLED_BY_AN;
353			    Strcpy(killer_buf, str);
354			} else {
355			    killer_format = KILLED_BY;
356			    Strcpy(killer_buf, str);
357			}
358			killer = killer_buf;
359			/* Known BUG: BURNING suppresses corpse in bones data,
360			   but done does not handle killer reason correctly */
361			done((adtyp == AD_FIRE) ? BURNING : DIED);
362		    }
363		}
364		exercise(A_STR, FALSE);
365	}
366
367	if (shopdamage) {
368		pay_for_damage(adtyp == AD_FIRE ? "burn away" :
369			       adtyp == AD_COLD ? "shatter" :
370			       adtyp == AD_DISN ? "disintegrate" : "destroy",
371			       FALSE);
372	}
373
374	/* explosions are noisy */
375	i = dam * dam;
376	if (i < 50) i = 50;	/* in case random damage is very small */
377	wake_nearto(x, y, i);
378}
379#endif /* OVL0 */
380#ifdef OVL1
381
382struct scatter_chain {
383	struct scatter_chain *next;	/* pointer to next scatter item	*/
384	struct obj *obj;		/* pointer to the object	*/
385	xchar ox;			/* location of			*/
386	xchar oy;			/*	item			*/
387	schar dx;			/* direction of			*/
388	schar dy;			/*	travel			*/
389	int range;			/* range of object		*/
390	boolean stopped;		/* flag for in-motion/stopped	*/
391};
392
393/*
394 * scflags:
395 *	VIS_EFFECTS	Add visual effects to display
396 *	MAY_HITMON	Objects may hit monsters
397 *	MAY_HITYOU	Objects may hit hero
398 *	MAY_HIT		Objects may hit you or monsters
399 *	MAY_DESTROY	Objects may be destroyed at random
400 *	MAY_FRACTURE	Stone objects can be fractured (statues, boulders)
401 */
402
403/* returns number of scattered objects */
404long
405scatter(sx,sy,blastforce,scflags, obj)
406int sx,sy;				/* location of objects to scatter */
407int blastforce;				/* force behind the scattering	*/
408unsigned int scflags;
409struct obj *obj;			/* only scatter this obj        */
410{
411	register struct obj *otmp;
412	register int tmp;
413	int farthest = 0;
414	uchar typ;
415	long qtmp;
416	boolean used_up;
417	boolean individual_object = obj ? TRUE : FALSE;
418	struct monst *mtmp;
419	struct scatter_chain *stmp, *stmp2 = 0;
420	struct scatter_chain *schain = (struct scatter_chain *)0;
421	long total = 0L;
422
423	while ((otmp = individual_object ? obj : level.objects[sx][sy]) != 0) {
424	    if (otmp->quan > 1L) {
425		qtmp = otmp->quan - 1;
426		if (qtmp > LARGEST_INT) qtmp = LARGEST_INT;
427		qtmp = (long)rnd((int)qtmp);
428		otmp = splitobj(otmp, qtmp);
429	    } else {
430		obj = (struct obj *)0; /* all used */
431	    }
432	    obj_extract_self(otmp);
433	    used_up = FALSE;
434
435	    /* 9 in 10 chance of fracturing boulders or statues */
436	    if ((scflags & MAY_FRACTURE)
437			&& ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE))
438			&& rn2(10)) {
439		if (otmp->otyp == BOULDER) {
440		    pline("%s apart.", Tobjnam(otmp, "break"));
441		    fracture_rock(otmp);
442		    place_object(otmp, sx, sy);
443		    if ((otmp = sobj_at(BOULDER, sx, sy)) != 0) {
444			/* another boulder here, restack it to the top */
445			obj_extract_self(otmp);
446			place_object(otmp, sx, sy);
447		    }
448		} else {
449		    struct trap *trap;
450
451		    if ((trap = t_at(sx,sy)) && trap->ttyp == STATUE_TRAP)
452			    deltrap(trap);
453		    pline("%s.", Tobjnam(otmp, "crumble"));
454		    (void) break_statue(otmp);
455		    place_object(otmp, sx, sy);	/* put fragments on floor */
456		}
457		used_up = TRUE;
458
459	    /* 1 in 10 chance of destruction of obj; glass, egg destruction */
460	    } else if ((scflags & MAY_DESTROY) && (!rn2(10)
461			|| (objects[otmp->otyp].oc_material == GLASS
462			|| otmp->otyp == EGG))) {
463		if (breaks(otmp, (xchar)sx, (xchar)sy)) used_up = TRUE;
464	    }
465
466	    if (!used_up) {
467		stmp = (struct scatter_chain *)
468					alloc(sizeof(struct scatter_chain));
469		stmp->next = (struct scatter_chain *)0;
470		stmp->obj = otmp;
471		stmp->ox = sx;
472		stmp->oy = sy;
473		tmp = rn2(8);		/* get the direction */
474		stmp->dx = xdir[tmp];
475		stmp->dy = ydir[tmp];
476		tmp = blastforce - (otmp->owt/40);
477		if (tmp < 1) tmp = 1;
478		stmp->range = rnd(tmp); /* anywhere up to that determ. by wt */
479		if (farthest < stmp->range) farthest = stmp->range;
480		stmp->stopped = FALSE;
481		if (!schain)
482		    schain = stmp;
483		else
484		    stmp2->next = stmp;
485		stmp2 = stmp;
486	    }
487	}
488
489	while (farthest-- > 0) {
490		for (stmp = schain; stmp; stmp = stmp->next) {
491		   if ((stmp->range-- > 0) && (!stmp->stopped)) {
492			bhitpos.x = stmp->ox + stmp->dx;
493			bhitpos.y = stmp->oy + stmp->dy;
494			typ = levl[bhitpos.x][bhitpos.y].typ;
495			if(!isok(bhitpos.x, bhitpos.y)) {
496				bhitpos.x -= stmp->dx;
497				bhitpos.y -= stmp->dy;
498				stmp->stopped = TRUE;
499			} else if(!ZAP_POS(typ) ||
500					closed_door(bhitpos.x, bhitpos.y)) {
501				bhitpos.x -= stmp->dx;
502				bhitpos.y -= stmp->dy;
503				stmp->stopped = TRUE;
504			} else if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
505				if (scflags & MAY_HITMON) {
506				    stmp->range--;
507				    if (ohitmon(mtmp, stmp->obj, 1, FALSE)) {
508					stmp->obj = (struct obj *)0;
509					stmp->stopped = TRUE;
510				    }
511				}
512			} else if (bhitpos.x==u.ux && bhitpos.y==u.uy) {
513				if (scflags & MAY_HITYOU) {
514				    int hitvalu, hitu;
515
516				    if (multi) nomul(0);
517				    hitvalu = 8 + stmp->obj->spe;
518				    if (bigmonst(youmonst.data)) hitvalu++;
519				    hitu = thitu(hitvalu,
520						 dmgval(stmp->obj, &youmonst),
521						 stmp->obj, (char *)0);
522				    if (hitu) {
523					stmp->range -= 3;
524					stop_occupation();
525				    }
526				}
527			} else {
528				if (scflags & VIS_EFFECTS) {
529				    /* tmp_at(bhitpos.x, bhitpos.y); */
530				    /* delay_output(); */
531				}
532			}
533			stmp->ox = bhitpos.x;
534			stmp->oy = bhitpos.y;
535		   }
536		}
537	}
538	for (stmp = schain; stmp; stmp = stmp2) {
539		int x,y;
540
541		stmp2 = stmp->next;
542		x = stmp->ox; y = stmp->oy;
543		if (stmp->obj) {
544			if ( x!=sx || y!=sy )
545			    total += stmp->obj->quan;
546			place_object(stmp->obj, x, y);
547			stackobj(stmp->obj);
548		}
549		free((genericptr_t)stmp);
550		newsym(x,y);
551	}
552
553	return total;
554}
555
556
557/*
558 * Splatter burning oil from x,y to the surrounding area.
559 *
560 * This routine should really take a how and direction parameters.
561 * The how is how it was caused, e.g. kicked verses thrown.  The
562 * direction is which way to spread the flaming oil.  Different
563 * "how"s would give different dispersal patterns.  For example,
564 * kicking a burning flask will splatter differently from a thrown
565 * flask hitting the ground.
566 *
567 * For now, just perform a "regular" explosion.
568 */
569void
570splatter_burning_oil(x, y)
571    int x, y;
572{
573/* ZT_SPELL(ZT_FIRE) = ZT_SPELL(AD_FIRE-1) = 10+(2-1) = 11 */
574#define ZT_SPELL_O_FIRE 11 /* value kludge, see zap.c */
575    explode(x, y, ZT_SPELL_O_FIRE, d(4,4), BURNING_OIL, EXPL_FIERY);
576}
577
578#endif /* OVL1 */
579
580/*explode.c*/
581