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