Blindfold removal fix
[slashemextended.git] / src / explode.c
blob50b0de06df868ade91804974adbdffe70a6ed551
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. */
5 #include "hack.h"
7 #ifdef OVL0
9 /* ExplodeRegions share some commonalities with NhRegions, but not enough to
10 * make it worth trying to create a common implementation.
12 typedef struct {
13 xchar x, y;
14 /*xchar*/int blast; /* blast symbol */
15 xchar shielded; /* True if this location is shielded */
16 } ExplodeLocation;
18 typedef struct {
19 ExplodeLocation *locations;
20 short nlocations, alocations;
21 } ExplodeRegion;
23 STATIC_DCL ExplodeRegion *
24 create_explode_region()
26 ExplodeRegion *reg;
28 reg = (ExplodeRegion *)alloc(sizeof(ExplodeRegion));
29 reg->locations = (ExplodeLocation *)0;
30 reg->nlocations = 0;
31 reg->alocations = 0;
32 return reg;
35 STATIC_DCL void
36 add_location_to_explode_region(reg, x, y)
37 ExplodeRegion *reg;
38 xchar x, y;
40 int i;
41 ExplodeLocation *new;
42 for(i = 0; i < reg->nlocations; i++)
43 if (reg->locations[i].x == x && reg->locations[i].y == y)
44 return;
45 if (reg->nlocations == reg->alocations) {
46 reg->alocations = reg->alocations ? 2 * reg->alocations : 32;
47 new = (ExplodeLocation *)
48 alloc(reg->alocations * sizeof(ExplodeLocation));
49 (void) memcpy((void *)new, (void *)reg->locations,
50 reg->nlocations * sizeof(ExplodeLocation));
51 free((void *)reg->locations);
52 reg->locations = new;
54 reg->locations[reg->nlocations].x = x;
55 reg->locations[reg->nlocations].y = y;
56 /* reg->locations[reg->nlocations].blast = 0; */
57 /* reg->locations[reg->nlocations].shielded = 0; */
58 reg->nlocations++;
61 STATIC_DCL int
62 compare_explode_location(loc1, loc2)
63 ExplodeLocation *loc1, *loc2;
65 return loc1->y == loc2->y ? loc1->x - loc2->x : loc1->y - loc2->y;
68 STATIC_DCL void
69 set_blast_symbols(reg)
70 ExplodeRegion *reg;
72 int i, j, bitmask;
73 /* The index into the blast symbol array is a bitmask containing 4 bits:
74 * bit 3: True if the location immediately to the north is present
75 * bit 2: True if the location immediately to the south is present
76 * bit 1: True if the location immediately to the east is present
77 * bit 0: True if the location immediately to the west is present
79 static int blast_symbols[16] = {
80 S_explode5, S_explode6, S_explode4, S_explode5,
81 S_explode2, S_explode3, S_explode1, S_explode2,
82 S_explode8, S_explode9, S_explode7, S_explode8,
83 S_explode5, S_explode6, S_explode4, S_explode5,
85 /* Sort in order of North -> South, West -> East */
86 qsort(reg->locations, reg->nlocations, sizeof(ExplodeLocation),
87 compare_explode_location);
88 /* Pass 1: Build the bitmasks in the blast field */
89 for(i = 0; i < reg->nlocations; i++)
90 reg->locations[i].blast = 0;
91 for(i = 0; i < reg->nlocations; i++) {
92 bitmask = 0;
93 if (i && reg->locations[i-1].y == reg->locations[i].y &&
94 reg->locations[i-1].x == reg->locations[i].x-1) {
95 reg->locations[i].blast |= 1; /* Location to the west */
96 reg->locations[i-1].blast |= 2; /* Location to the east */
98 for(j = i-1; j >= 0; j--) {
99 if (reg->locations[j].y < reg->locations[i].y-1)
100 break;
101 else if (reg->locations[j].y == reg->locations[i].y-1 &&
102 reg->locations[j].x == reg->locations[i].x) {
103 reg->locations[i].blast |= 8; /* Location to the north */
104 reg->locations[j].blast |= 4; /* Location to the south */
105 break;
109 /* Pass 2: Set the blast symbols */
110 for(i = 0; i < reg->nlocations; i++)
111 reg->locations[i].blast = blast_symbols[reg->locations[i].blast];
114 STATIC_DCL void
115 free_explode_region(reg)
116 ExplodeRegion *reg;
118 free((void *)reg->locations);
119 free((void *)reg);
122 /* This is the "do-it-all" explosion command */
123 STATIC_DCL void do_explode(int,int,ExplodeRegion *,int,int,CHAR_P,int,int,BOOLEAN_P);
125 /* Note: I had to choose one of three possible kinds of "type" when writing
126 * this function: a wand type (like in zap.c), an adtyp, or an object type.
127 * Wand types get complex because they must be converted to adtyps for
128 * determining such things as fire resistance. Adtyps get complex in that
129 * they don't supply enough information--was it a player or a monster that
130 * did it, and with a wand, spell, or breath weapon? Object types share both
131 * these disadvantages....
133 * Explosions derived from vanilla NetHack:
135 * src nature olet expl Comment
136 * Your wand MAGIC_MISSILE WAND FROSTY Exploding wands of cold
137 * Your wand MAGIC_MISSILE WAND FIERY Exploding wands of fire/
138 * fireball
139 * Your wand MAGIC_MISSILE WAND MAGICAL Other explosive wands
140 * Your spell FIRE BURNING_OIL FIERY Splattered buring oil
141 * Mon's ? - MON_EXPLODE NOXIOUS Exploding gas spore
142 * Your spell FIRE 0 FIERY Filling a lamp with oil
143 * when lit
144 * Your spell FIRE SCROLL FIERY Reading a scroll of fire
145 * Your spell FIRE WAND FIERY Zap yourself with wand/
146 * spell of fireball
147 * Your spell FIRE 0 FIERY Your fireball
149 * Slash'EM specific explosions:
151 * src nature olet expl Comment
152 * Your spell FIRE WEAPON FIERY Explosive projectile
153 * Your spell FIRE WEAPON FIERY Bolts shot by Hellfire
154 * Mon's spell FIRE FIERY WEAPON Explosive projectile (BUG)
155 * Mon's spell FIRE FIERY WEAPON Bolts shot by Hellfire (BUG)
156 * Your spell MAGIC_MISSILE WAND MAGICAL Spirit bomb technique
157 * Mon's spell FIRE 0 FIERY Monster's fireball
159 * Sigil of tempest:
161 * src nature olet expl Comment
162 * Your spell MAGIC_MISSILE 0 MAGICAL Hero casts magic missile
163 * Your spell DEATH 0 MAGICAL Hero casts finger of death
164 * Your spell FIRE 0 FIERY Hero casts fireball
165 * Your spell LIGHTNING 0 FIERY Hero casts lightning
166 * Your spell COLD 0 FROSTY Hero casts cone of cold
167 * Your spell SLEEP 0 NOXIOUS Hero casts sleep
168 * Your spell POISON_GAS 0 NOXIOUS Hero casts poison blast
169 * Your spell ACID 0 NOXIOUS Hero casts acid stream
171 * Mega spells:
173 * src nature olet expl Comment
174 * Your mega FIRE 0 FIERY
175 * Your mega COLD 0 FROSTY
176 * Your mega MAGIC_MISSLE 0 MAGICAL
178 * Notes:
179 * Nature is encoded as (abs(type) % 10) and src is determined using the
180 * following table:
181 * Types Src
182 * -30 - -39 Mon's wand
183 * -20 - -29 Mon's breath
184 * -10 - -19 Mon's spell
185 * -1 - -9 Special
186 * 0 - 9 Your wand
187 * 10 - 19 Your spell
188 * 20 - 29 Your breath
189 * 30 - 39 Your mega
190 * There is only one special type currently defined:
191 * -1 Exploding gas spore
193 void
194 explode(x, y, type, dam, olet, expltype)
195 xchar x, y; /* WAC was int...i think it's supposed to be xchar */
196 int type; /* the same as in zap.c */
197 int dam;
198 char olet;
199 int expltype;
201 int i, j;
202 ExplodeRegion *area;
203 area = create_explode_region();
204 for(i = 0; i < 3; i++)
205 for(j = 0; j < 3; j++)
206 if (isok(i+x-1,j+y-1) && ZAP_POS((&levl[i+x-1][j+y-1])->typ))
207 add_location_to_explode_region(area, i+x-1, j+y-1);
208 do_explode(x, y, area, type, dam, olet, expltype, 0, !flags.mon_moving);
209 free_explode_region(area);
212 void
213 do_explode(x, y, area, type, dam, olet, expltype, dest, yours)
214 xchar x, y; /* WAC was int...i think it's supposed to be xchar */
215 ExplodeRegion *area;
216 int type; /* the same as in zap.c */
217 int dam;
218 char olet;
219 int expltype;
220 int dest; /* 0 = normal, 1 = silent, 2 = silent/remote */
221 boolean yours; /* is it your fault (for killing monsters) */
223 int i, k, damu = dam;
224 boolean starting = 1;
225 boolean visible, any_shield;
226 int uhurt = 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */
227 const char *str;
228 int idamres, idamnonres;
229 struct monst *mtmp;
230 uchar adtyp;
231 boolean explmask;
232 boolean shopdamage = FALSE;
233 boolean generic = FALSE;
234 boolean silent = FALSE, remote = FALSE;
235 xchar xi, yi;
237 if (dest > 0) silent = TRUE;
238 if (dest == 2) remote = TRUE;
240 if (olet == WAND_CLASS) /* retributive strike */
241 switch (Role_switch) {
242 case PM_PRIEST:
243 /*WAC add Flame, Ice mages, Necromancer */
244 case PM_FLAME_MAGE:
245 case PM_ICE_MAGE:
246 case PM_NECROMANCER:
247 case PM_WIZARD: damu /= 5;
248 break;
249 case PM_HEALER:
250 case PM_KNIGHT: damu /= 2;
251 break;
252 default: break;
255 if (olet == MON_EXPLODE) {
256 str = killer;
257 killer = 0; /* set again later as needed */
258 adtyp = AD_PHYS;
259 } else
260 switch (abs(type) % 10) {
261 case 0: str = "magical blast";
262 adtyp = AD_MAGM;
263 break;
264 case 1: str = olet == BURNING_OIL ? "burning oil" :
265 olet == SCROLL_CLASS ? "tower of flame" :
266 "fireball";
267 adtyp = AD_FIRE;
268 break;
269 case 2: str = "ball of cold";
270 adtyp = AD_COLD;
271 break;
272 case 3: str = "ball of sleep";
273 adtyp = AD_SLEE;
274 break;
275 /* Assume that wands are death, others are disintegration */
276 case 4: str = (olet == WAND_CLASS) ? "death field" :
277 "disintegration field";
278 adtyp = AD_DISN;
279 break;
280 case 5: str = "ball of lightning";
281 adtyp = AD_ELEC;
282 break;
283 case 6: str = "poison gas cloud";
284 adtyp = AD_DRST;
285 break;
286 case 7: str = "splash of acid";
287 adtyp = AD_ACID;
288 break;
289 case 8: str = "pure energy irradiation";
290 adtyp = AD_LITE;
291 break;
292 case 9: str = "psionic energy";
293 adtyp = AD_SPC2;
294 break;
295 default: impossible("explosion base type %d?", type); return;
298 /*WAC add light source for fire*/
299 #ifdef LIGHT_SRC_SPELL
300 if ((!remote) && ((adtyp == AD_FIRE) || (adtyp == AD_ELEC))) {
301 new_light_source(x, y, 2, LS_TEMP, (void *) 1);
302 vision_recalc(0);
304 #endif
306 any_shield = visible = FALSE;
307 for(i = 0; i < area->nlocations; i++) {
308 explmask = FALSE;
309 xi = area->locations[i].x;
310 yi = area->locations[i].y;
311 if (xi == u.ux && yi == u.uy) {
312 switch(adtyp) {
313 case AD_PHYS:
314 case AD_LITE:
315 case AD_SPC2:
316 break;
317 case AD_MAGM:
318 explmask = !!Antimagic;
319 break;
320 case AD_FIRE:
321 explmask = !!Fire_resistance;
322 break;
323 case AD_COLD:
324 explmask = !!Cold_resistance;
325 break;
326 case AD_SLEE:
327 explmask = !!Sleep_resistance;
328 break;
329 case AD_DISN:
330 explmask = (olet == WAND_CLASS) ?
331 !!(nonliving(youmonst.data) || is_demon(youmonst.data) || Death_resistance) :
332 !!Disint_resistance;
333 break;
334 case AD_ELEC:
335 explmask = !!Shock_resistance;
336 break;
337 case AD_DRST:
338 explmask = !!Poison_resistance;
339 break;
340 case AD_ACID:
341 explmask = !!Acid_resistance;
342 break;
343 default:
344 impossible("explosion type %d?", adtyp);
345 break;
349 mtmp = m_at(xi, yi);
350 if (!mtmp && xi == u.ux && yi == u.uy)
351 mtmp = u.usteed;
352 if (mtmp) {
353 switch(adtyp) {
354 case AD_PHYS:
355 case AD_LITE:
356 case AD_SPC2:
357 break;
358 case AD_MAGM:
359 explmask |= resists_magm(mtmp);
360 break;
361 case AD_FIRE:
362 explmask |= (resists_fire(mtmp) && !player_will_pierce_resistance() );
363 break;
364 case AD_COLD:
365 explmask |= (resists_cold(mtmp) && !player_will_pierce_resistance() );
366 break;
367 case AD_SLEE:
368 explmask |= resists_sleep(mtmp);
369 break;
370 case AD_DISN:
371 explmask |= (olet == WAND_CLASS) ?
372 (nonliving(mtmp->data) || is_demon(mtmp->data)) :
373 resists_disint(mtmp);
374 break;
375 case AD_ELEC:
376 explmask |= (resists_elec(mtmp) && !player_will_pierce_resistance() );
377 break;
378 case AD_DRST:
379 explmask |= (resists_poison(mtmp) && !player_will_pierce_resistance()) ;
380 break;
381 case AD_ACID:
382 explmask |= (resists_acid(mtmp) && !player_will_pierce_resistance()) ;
383 break;
384 default:
385 impossible("explosion type %d?", adtyp);
386 break;
389 if (mtmp && cansee(xi,yi) && !canspotmon(mtmp) && !(mtmp->data->msound == MS_DEEPSTATE) && !(mtmp->egotype_deepstatemember))
390 map_invisible(xi, yi);
391 else if (!mtmp && memory_is_invisible(xi, yi)) {
392 unmap_object(xi, yi);
393 newsym(xi, yi);
395 if (cansee(xi, yi)) visible = TRUE;
396 if (explmask) any_shield = TRUE;
397 area->locations[i].shielded = explmask;
400 /* Not visible if remote */
401 if (remote) visible = FALSE;
403 if (visible) {
404 #ifdef ALLEG_FX
405 if (iflags.usealleg) {
406 alleg_explode(x, y, adtyp);
407 if (any_shield) /* simulate a shield effect */
408 for(i = 0; i < area->nlocations; i++) {
409 if (area->locations[i].shielded)
410 shieldeff(area->locations[i].x,
411 area->locations[i].y);
413 } else {
414 #endif
415 set_blast_symbols(area);
416 /* Start the explosion */
417 for(i = 0; i < area->nlocations; i++) {
418 tmp_at(starting ? DISP_BEAM : DISP_CHANGE,
419 explosion_to_glyph(expltype,
420 area->locations[i].blast));
421 tmp_at(area->locations[i].x, area->locations[i].y);
422 starting = 0;
424 curs_on_u(); /* will flush screen and output */
426 if (any_shield && flags.sparkle) { /* simulate shield effect */
427 for (k = 0; k < SHIELD_COUNT; k++) {
428 for(i = 0; i < area->nlocations; i++) {
429 if (area->locations[i].shielded)
431 * Bypass tmp_at() and send the shield glyphs
432 * directly to the buffered screen. tmp_at()
433 * will clean up the location for us later.
435 show_glyph(area->locations[i].x,
436 area->locations[i].y,
437 cmap_to_glyph(shield_static[k]));
439 curs_on_u(); /* will flush screen and output */
440 delay_output();
443 /* Cover last shield glyph with blast symbol. */
444 for(i = 0; i < area->nlocations; i++) {
445 if (area->locations[i].shielded)
446 show_glyph(area->locations[i].x,
447 area->locations[i].y,
448 explosion_to_glyph(expltype,
449 area->locations[i].blast));
452 } else { /* delay a little bit. */
453 delay_output();
454 delay_output();
456 tmp_at(DISP_END, 0); /* clear the explosion */
457 #ifdef ALLEG_FX
459 #endif
460 } else if (!remote) {
461 if (olet == MON_EXPLODE) {
462 str = "explosion";
463 generic = TRUE;
465 if (flags.soundok)
466 You_hear(is_waterypool(x, y) ? "a muffled explosion." : "a blast.");
469 if (dam) for(i = 0; i < area->nlocations; i++) {
470 xi = area->locations[i].x;
471 yi = area->locations[i].y;
472 if (xi == u.ux && yi == u.uy)
473 uhurt = area->locations[i].shielded ? 1 : 2;
474 idamres = idamnonres = 0;
476 /* DS: Allow monster induced explosions also */
477 if (type >= 0 || type <= -10)
478 (void)zap_over_floor(xi, yi, type, &shopdamage);
480 mtmp = m_at(xi, yi);
481 if (!mtmp && xi == u.ux && yi == u.uy)
482 mtmp = u.usteed;
483 if (!mtmp) continue;
484 if (DEADMONSTER(mtmp)) continue;
485 if (u.uswallow && mtmp == u.ustuck) {
486 if (is_animal(u.ustuck->data)) {
487 if (!silent) pline("%s gets %s!",
488 Monnam(u.ustuck),
489 (adtyp == AD_FIRE) ? "heartburn" :
490 (adtyp == AD_COLD) ? "chilly" :
491 (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ?
492 "irradiated by pure energy" : "perforated") :
493 (adtyp == AD_ELEC) ? "shocked" :
494 (adtyp == AD_DRST) ? "poisoned" :
495 (adtyp == AD_ACID) ? "an upset stomach" :
496 "fried");
497 } else {
498 if (!silent) pline("%s gets slightly %s!",
499 Monnam(u.ustuck),
500 (adtyp == AD_FIRE) ? "toasted" :
501 (adtyp == AD_COLD) ? "chilly" :
502 (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ?
503 "overwhelmed by pure energy" : "perforated") :
504 (adtyp == AD_ELEC) ? "shocked" :
505 (adtyp == AD_DRST) ? "intoxicated" :
506 (adtyp == AD_ACID) ? "burned" :
507 "fried");
509 } else if (!silent && cansee(xi, yi)) {
510 if(mtmp->m_ap_type) seemimic(mtmp);
511 pline("%s is caught in the %s!", Monnam(mtmp), str);
514 if (!rn2(33)) idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int) adtyp);
515 if (!rn2(33)) idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int) adtyp);
516 if (!rn2(33)) idamnonres += destroy_mitem(mtmp, POTION_CLASS, (int) adtyp);
517 if (!rn2(33)) idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int) adtyp);
518 if (!rn2(33)) idamnonres += destroy_mitem(mtmp, RING_CLASS, (int) adtyp);
520 if (area->locations[i].shielded) {
521 golemeffects(mtmp, (int) adtyp, dam + idamres);
522 mtmp->mhp -= idamnonres;
523 } else {
524 /* call resist with 0 and do damage manually so 1) we can
525 * get out the message before doing the damage, and 2) we can
526 * call mondied, not killed, if it's not your blast
528 int mdam = dam;
530 if (resist(mtmp, olet, 0, FALSE)) {
531 if (!silent && cansee(xi,yi))
532 pline("%s resists the %s!", Monnam(mtmp), str);
533 mdam = dam/2;
535 if (mtmp == u.ustuck)
536 mdam *= 2;
537 if (resists_cold(mtmp) && adtyp == AD_FIRE)
538 mdam *= 2;
539 else if (resists_fire(mtmp) && adtyp == AD_COLD)
540 mdam *= 2;
541 mtmp->mhp -= mdam;
542 mtmp->mhp -= (idamres + idamnonres);
543 #ifdef SHOW_DMG
544 if (mtmp->mhp > 0 && !remote)
545 showdmg(mdam + idamres + idamnonres);
546 #endif
548 if (mtmp->mhp <= 0) {
549 /* KMH -- Don't blame the player for pets killing gas spores */
550 if (yours) xkilled(mtmp, (silent ? 0 : 1));
551 else monkilled(mtmp, "", (int)adtyp);
552 } else if (!flags.mon_moving && yours) setmangry(mtmp);
555 #ifdef LIGHT_SRC_SPELL
556 /*WAC kill the light source*/
557 if ((!remote) && ((adtyp == AD_FIRE) || (adtyp == AD_ELEC))) {
558 del_light_source(LS_TEMP, (void *) 1);
560 #endif
562 /* Do your injury last */
564 /* You are not hurt if this is remote */
565 if (remote) uhurt = FALSE;
566 if (u.detonationhack) uhurt = FALSE;
568 if (uhurt) {
570 if (youmonst.m_ap_type == M_AP_OBJECT && (multi < 0)) {
571 You("took damage, and therefore stopped mimicking an object!");
572 unmul((char *)0); /* immediately stop mimicking */
573 youmonst.m_ap_type = M_AP_NOTHING;
574 youmonst.mappearance = 0;
577 /* [ALI] Give message if it's a weapon (grenade) exploding */
578 if ((type >= 0 || adtyp == AD_PHYS || olet == WEAPON_CLASS) &&
579 /* gas spores */
580 flags.verbose && olet != SCROLL_CLASS)
581 You("are caught in the %s!", str);
582 /* do property damage first, in case we end up leaving bones */
583 if (adtyp == AD_FIRE) burn_away_slime();
585 if (uarms && uarms->oartifact == ART_OF_VOIDING && damu > 0) {
586 damu -= rnd(2);
587 if (damu < 1) damu = 1;
589 if (uarms && uarms->oartifact == ART_OF_NULLING && damu > 0) {
590 damu -= rnd(4);
591 if (damu < 1) damu = 1;
594 /* player may get lucky and take less damage --Amy */
595 if (!rn2(3) && damu >= 1) {damu++; damu = damu / 2; if (damu < 1) damu = 1;}
596 if (!rn2(10) && damu >= 1 && GushLevel >= 10) {damu++; damu = damu / 3; if (damu < 1) damu = 1;}
597 if (!rn2(15) && damu >= 1 && GushLevel >= 14) {damu++; damu = damu / 4; if (damu < 1) damu = 1;}
598 if (!rn2(20) && damu >= 1 && GushLevel >= 20) {damu++; damu = damu / 5; if (damu < 1) damu = 1;}
599 if (!rn2(50) && damu >= 1 && GushLevel >= 30) {damu++; damu = damu / 10; if (damu < 1) damu= 1;}
601 /* exploding wands were too strong, a cursed wand of solar beam at XL1 shouldn't be instant death all the time */
602 if (u.explodewandhack && damu >= 1 && u.ulevel < 20) {
604 damu++;
605 damu *= u.ulevel;
606 damu /= 20;
607 if (damu < 1) damu = 1;
610 if (PlayerInConeHeels && damu > 0) {
611 register int dmgreductor = 95;
612 if (!(PlayerCannotUseSkills)) switch (P_SKILL(P_HIGH_HEELS)) {
613 case P_BASIC: dmgreductor = 92; break;
614 case P_SKILLED: dmgreductor = 89; break;
615 case P_EXPERT: dmgreductor = 86; break;
616 case P_MASTER: dmgreductor = 83; break;
617 case P_GRAND_MASTER: dmgreductor = 80; break;
618 case P_SUPREME_MASTER: dmgreductor = 77; break;
620 damu++;
621 damu *= dmgreductor;
622 damu /= 100;
623 if (damu < 1) damu = 1;
626 if (Race_if(PM_ITAQUE) && damu > 0) {
627 damu++;
628 damu *= (100 - u.ulevel);
629 damu /= 100;
630 if (damu < 1) damu = 1;
633 if (is_sand(u.ux,u.uy) && damu > 0) {
634 damu++;
635 damu *= 4;
636 damu /= 5;
637 if (damu < 1) damu = 1;
640 if (Race_if(PM_CARTHAGE) && u.usteed && (mcalcmove(u.usteed) < 12) && damu > 0) {
641 damu++;
642 damu *= 4;
643 damu /= 5;
644 if (damu < 1) damu = 1;
647 if (StrongDetect_monsters && damu > 0) {
648 damu++;
649 damu *= 9;
650 damu /= 10;
651 if (damu < 1) damu = 1;
654 if (Race_if(PM_VIKING) && damu > 0) {
655 damu *= 5;
656 damu /= 4;
659 if (Race_if(PM_SPARD) && damu > 0) {
660 damu *= 5;
661 damu /= 4;
664 if (Race_if(PM_MAYMES) && uwep && weapon_type(uwep) == P_FIREARM && damu > 0) {
665 damu++;
666 damu *= 4;
667 damu /= 5;
668 if (damu < 1) damu = 1;
671 if (damu > 0 && uarmf && itemhasappearance(uarmf, APP_MARJI_SHOES)) {
672 damu++;
673 damu *= 9;
674 damu /= 10;
675 if (damu < 1) damu = 1;
678 if (damu > 0 && uarm && uarm->oartifact == ART_KRAH_HOOOOO) {
679 damu++;
680 damu *= 9;
681 damu /= 10;
682 if (damu < 1) damu = 1;
685 if (damu > 0 && uarms && uarms->oartifact == ART_AL_UD) {
686 damu++;
687 damu *= 9;
688 damu /= 10;
689 if (damu < 1) damu = 1;
692 /* very early on, low-level characters should be more survivable
693 * this can certainly be exploited in some way; if players start exploiting it I'll have to fix it
694 * but it should fix the annoying problem where you often instadie to a trap while your max HP are bad --Amy */
695 if (depth(&u.uz) == 1 && u.ulevel == 1 && moves < 1000 && In_dod(&u.uz) && damu > 1) { damu /= 2; }
696 if (depth(&u.uz) == 1 && u.ulevel == 2 && moves < 1000 && In_dod(&u.uz) && damu > 1) { damu *= 2; damu /= 3; }
698 if (damu && Race_if(PM_YUKI_PLAYA)) damu += rnd(5);
699 if (Role_if(PM_BLEEDER)) damu = damu * 2; /* bleeders are harder than hard mode */
700 if (!rn2(10) && (uarmf && uarmf->oartifact == ART_XTRA_CUTENESS)) damu = damu * 2;
701 if (have_cursedmagicresstone()) damu = damu * 2;
702 if (WinceState) {
703 int damutemp = damu;
704 damu *= 11;
705 damu /= 10;
706 if (StrongWinceState) {
707 damu *= 11;
708 damu /= 10;
710 if (damu <= damutemp) damu++;
712 if (ACURR(A_CON) == 2) {
713 damu *= 11;
714 damu /= 10;
716 if (ACURR(A_CON) == 1) {
717 damu *= 12;
718 damu /= 10;
720 if (uarmh && uarmh->oartifact == ART_HEAD_W) {
721 damu *= 11;
722 damu /= 10;
724 if (Role_if(PM_DANCER) && !rn2(3)) damu = damu * 3;
725 if (Race_if(PM_METAL)) damu *= rnd(10);
726 if (HardModeEffect || u.uprops[HARD_MODE_EFFECT].extrinsic || have_hardmodestone() || autismringcheck(ART_RING_OF_FAST_LIVING) || autismweaponcheck(ART_PAINBOWSWANDIR) || autismweaponcheck(ART_RAISING_HEART) || (uimplant && uimplant->oartifact == ART_IME_SPEW) || (uarm && uarm->oartifact == ART_CHEST_TANK) ) damu = damu * 2;
727 if (uamul && uamul->otyp == AMULET_OF_VULNERABILITY) damu *= rnd(4);
728 if (RngeFrailness) damu = damu * 2;
730 if (Race_if(PM_SHELL) && !Upolyd && damu > 1) damu /= 2;
732 if (u.martialstyle == MARTIALSTYLE_SILAT && !rn2(5) && !uwep && (!u.twoweap || !uswapwep) && damu > 1) damu /= 2;
734 if (isfriday && !rn2(50)) damu += rnd(damu);
736 if (Invulnerable || (uarmf && uarmf->oartifact == ART_GODLY_POSTMAN && !rn2(10)) || ((PlayerInBlockHeels || PlayerInWedgeHeels) && tech_inuse(T_EXTREME_STURDINESS) && !rn2(2) ) || (Stoned_chiller && Stoned && !(u.stonedchilltimer) && !rn2(3)) ) {
737 damu = 0;
738 You("are unharmed!");
739 } else if (StrongWonderlegs && !rn2(10) && Wounded_legs) {
740 damu = 0;
741 You("are unharmed!");
742 } else if (u.metalguard) {
743 u.metalguard = 0;
744 damu = 0;
745 Your("metal guard prevents the damage!");
746 } else if (uarms && uarms->otyp == NULLIFICATION_SHIELD && !rn2(20) ) {
747 damu = 0;
748 Your("shield nullifies the damage!");
749 } else if (uimplant && uimplant->oartifact == ART_GLEN_HOSPITAL && !rn2(10)) {
750 damu = 0;
751 Your("implant nullifies the damage!");
752 } else if (PlayerInConeHeels && !PlayerCannotUseSkills && P_SKILL(P_CONE_HEELS) >= P_BASIC && (rnd(100) < P_SKILL(P_CONE_HEELS)) ) {
753 damu = 0;
754 Your("cone heels nullify the damage!");
755 } else if (u.twoweap && uswapwep && uswapwep->oartifact == ART_SHIELD_TONFA && !rn2(10)) {
756 damu = 0;
757 Your("tonfa nullifies the damage!");
758 } else if (uarm && uarm->oartifact == ART_SUSA_MAIL && !rn2(10)) {
759 damu = 0;
760 Your("armor nullifies the damage!");
761 } else if (uarmf && uarmf->oartifact == ART_ELENA_S_EPITOME && !rn2(10)) {
762 damu = 0;
763 Your("pair of heels nullifies the damage!");
764 } else if (uarmf && uarmf->oartifact == ART_IRMA_S_CHOICE && !rn2(10)) {
765 damu = 0;
766 Your("pair of heels nullifies the damage!");
767 } else if (uarms && uarms->oartifact == ART_PLANK_OF_CARNEADES && !rn2(10)) {
768 damu = 0;
769 Your("shield nullifies the damage!");
770 } else if (uarms && uarms->oartifact == ART_AL_UD && !rn2(10)) {
771 damu = 0;
772 Your("shield nullifies the damage!");
773 } else if (uarms && uarms->oartifact == ART_DOUBLEBLANK && !rn2(10)) {
774 damu = 0;
775 Your("shield nullifies the damage!");
776 } else if (uwep && uwep->oartifact == ART_ETERNAL_LONGING && !rn2(10)) {
777 damu = 0;
778 Your("soft lady shoe nullifies the damage!");
779 } else {
780 if (Half_physical_damage && adtyp == AD_PHYS && (rn2(2) || (uwep && uwep->oartifact == ART_SOOTHE_)) ) damu = (damu+1) / 2;
781 if (StrongHalf_physical_damage && adtyp == AD_PHYS && (rn2(2) || (uwep && uwep->oartifact == ART_SOOTHE_)) ) damu = (damu+1) / 2;
784 if (adtyp == AD_FIRE) {if (isevilvariant || !rn2(Race_if(PM_SEA_ELF) ? 1 : issoviet ? 2 : 33)) (void) burnarmor(&youmonst);}
785 if (isevilvariant || !rn2(Race_if(PM_SEA_ELF) ? 1 : issoviet ? 3 : 15)) /* new calculations --Amy */ destroy_item(SCROLL_CLASS, (int) adtyp);
786 if (isevilvariant || !rn2(Race_if(PM_SEA_ELF) ? 1 : issoviet ? 3 : 15)) /* new calculations --Amy */ destroy_item(SPBOOK_CLASS, (int) adtyp);
787 if (isevilvariant || !rn2(Race_if(PM_SEA_ELF) ? 1 : issoviet ? 3 : 15)) /* new calculations --Amy */ destroy_item(POTION_CLASS, (int) adtyp);
788 if (isevilvariant || !rn2(Race_if(PM_SEA_ELF) ? 1 : issoviet ? 3 : 15)) /* new calculations --Amy */ destroy_item(RING_CLASS, (int) adtyp);
789 if (isevilvariant || !rn2(Race_if(PM_SEA_ELF) ? 1 : issoviet ? 15 : 75)) /* new calculations --Amy */ destroy_item(AMULET_CLASS, (int) adtyp);
790 if (isevilvariant || !rn2(Race_if(PM_SEA_ELF) ? 1 : issoviet ? 3 : 15)) /* new calculations --Amy */ destroy_item(WAND_CLASS, (int) adtyp);
792 ugolemeffects((int) adtyp, damu);
794 if (uactivesymbiosis && !u.symbiotedmghack && (rn2(100) < u.symbioteaggressivity) && !(u.usymbiote.mhpmax >= 5 && u.usymbiote.mhp <= (u.usymbiote.mhpmax / 5) && rn2(5)) ) {
795 if (tech_inuse(T_POWERBIOSIS) && damu > 1) damu /= 2;
796 if (tech_inuse(T_IMPLANTED_SYMBIOSIS) && uimplant && objects[uimplant->otyp].oc_charged && uimplant->spe > 0) {
797 int imbiophases = uimplant->spe;
798 while ((imbiophases > 0) && damu > 1) {
799 imbiophases--;
800 damu *= 10;
801 damu /= 11;
804 u.usymbiote.mhp -= damu;
805 Your("%s symbiote takes the damage for you.", mons[u.usymbiote.mnum].mname);
806 if (u.usymbiote.mhp <= 0) {
808 if (uarmf && itemhasappearance(uarmf, APP_REMORA_HEELS) && u.usymbiote.mnum == PM_REMORA) {
809 if (uarmf->spe > -1) uarmf->spe = -1;
812 if (uamul && uamul->otyp == AMULET_OF_SYMBIOTE_SAVING) {
813 makeknown(AMULET_OF_SYMBIOTE_SAVING);
814 useup(uamul);
815 u.usymbiote.mhp = u.usymbiote.mhpmax;
816 Your("symbiote glows, and your amulet crumbles to dust!");
817 } else {
818 u.usymbiote.active = 0;
819 u.usymbiote.mnum = PM_PLAYERMON;
820 u.usymbiote.mhp = 0;
821 u.usymbiote.mhpmax = 0;
822 u.usymbiote.cursed = u.usymbiote.hvycurse = u.usymbiote.prmcurse = u.usymbiote.bbcurse = u.usymbiote.morgcurse = u.usymbiote.evilcurse = u.usymbiote.stckcurse = 0;
823 u.cnd_symbiotesdied++;
824 if (FunnyHallu) pline("Ack! You feel like you quaffed aqua pura by mistake, and feel like something inside you has been flushed away!");
825 else Your("symbiote dies from protecting you, and you feel very sad...");
828 if (flags.showsymbiotehp) flags.botl = TRUE;
829 } else if (u.disruptionshield && u.uen >= damu) {
830 u.uen -= damu;
831 pline("Your mana shield takes the damage for you!");
832 flags.botl = 1;
833 } else if (uhurt == 2) {
834 if (Upolyd)
835 u.mh -= damu;
836 else
837 u.uhp -= damu;
838 flags.botl = 1;
841 if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
842 if (Upolyd) {
843 if (Polymorph_control || !rn2(3)) {
844 u.uhp -= mons[u.umonnum].mlevel;
845 /*u.uhpmax -= mons[u.umonnum].mlevel;
846 if (u.uhpmax < 1) u.uhpmax = 1;*/
848 rehumanize();
849 } else {
850 u.youaredead = 1;
851 if (olet == MON_EXPLODE) {
852 /* killer handled by caller */
853 if (str != killer_buf && !generic)
854 strcpy(killer_buf, str);
855 killer_format = KILLED_BY_AN;
856 } else if (type >= 0 && olet != SCROLL_CLASS && yours) {
857 killer_format = NO_KILLER_PREFIX;
858 sprintf(killer_buf, "caught %sself in %s own %s",
859 uhim(), uhis(), str);
860 } else if (olet != BURNING_OIL) {
861 killer_format = KILLED_BY_AN;
862 strcpy(killer_buf, str);
863 } else {
864 killer_format = KILLED_BY;
865 strcpy(killer_buf, str);
867 killer = killer_buf;
868 /* Known BUG: BURNING suppresses corpse in bones data,
869 but done does not handle killer reason correctly */
870 done((adtyp == AD_FIRE) ? BURNING : DIED);
871 u.youaredead = 0;
874 exercise(A_STR, FALSE);
875 #ifdef SHOW_DMG
876 if ( (uhurt == 2) && flags.showdmg && !(DamageMeterBug || u.uprops[DAMAGE_METER_BUG].extrinsic || have_damagemeterstone()) && !DisplayDoesNotGoAtAll && !(uarmc && uarmc->oartifact == ART_CLOAK_OF_THE_CONSORT) ) pline("[-%d -> %d]", damu, (Upolyd ? u.mh : u.uhp) );
878 if (!Upolyd && ((u.uhp * 5) < u.uhpmax)) pline(isangbander ? "***LOW HITPOINT WARNING***" : "Warning: HP low!");
879 #endif
880 if (isangbander && (!Upolyd && (( (u.uhp) * 5) < u.uhpmax)) && (PlayerHearsSoundEffects)) pline(issoviet ? "Umeret' glupyy igrok ublyudka!" : "TSCHINGTSCHINGTSCHINGTSCHING!");
882 if (u.uprops[TURNLIMITATION].extrinsic || (uarmh && uarmh->oartifact == ART_TEJUS__VACANCY) || (uarmf && uarmf->oartifact == ART_OUT_OF_TIME) || (uarmu && uarmu->oartifact == ART_THERMAL_BATH) || TurnLimitation || (uarm && uarm->oartifact == ART_AMMY_S_EASYMODE) || have_limitationstone() ) {
883 if (LimitationXtra && (damu > 0)) u.ascensiontimelimit -= (damu * 10);
884 else if (damu > 0) u.ascensiontimelimit -= damu;
885 if (u.ascensiontimelimit < 1) u.ascensiontimelimit = 1;
888 if (Race_if(PM_CELTIC) && !rn2(100)) {
889 if (u.berserktime) {
890 if (!obsidianprotection()) switch (rn2(11)) {
891 case 0:
892 make_sick(Sick ? Sick/2L + 1L : (long)rn1(ACURR(A_CON),20), "celtic sickness", TRUE, SICK_NONVOMITABLE);
893 break;
894 case 1: make_blinded(Blinded + 25, TRUE);
895 break;
896 case 2: if (!Confusion)
897 You("suddenly feel %s.", FunnyHallu ? "trippy" : "confused");
898 make_confused(HConfusion + 25, TRUE);
899 break;
900 case 3: make_stunned(HStun + 25, TRUE);
901 break;
902 case 4: make_numbed(HNumbed + 25, TRUE);
903 break;
904 case 5: make_frozen(HFrozen + 25, TRUE);
905 break;
906 case 6: make_burned(HBurned + 25, TRUE);
907 break;
908 case 7: (void) adjattrib(rn2(A_MAX), -1, FALSE, TRUE);
909 break;
910 case 8: (void) make_hallucinated(HHallucination + 25, TRUE, 0L);
911 break;
912 case 9: make_feared(HFeared + 25, TRUE);
913 break;
914 case 10: make_dimmed(HDimmed + 25, TRUE);
915 break;
918 } else u.berserktime = 25;
923 if (shopdamage) {
924 pay_for_damage(adtyp == AD_FIRE ? "burn away" :
925 adtyp == AD_COLD ? "shatter" :
926 adtyp == AD_DISN ? "disintegrate" : "destroy",
927 FALSE);
930 /* explosions are noisy */
931 i = dam * dam;
932 if (i < 50) i = 50; /* in case random damage is very small */
933 wake_nearto(x, y, i);
934 #ifdef ALLEG_FX
935 if (iflags.usealleg) cleanup_explosions();
936 #endif
938 #endif /* OVL0 */
939 #ifdef OVL1
941 struct scatter_chain {
942 struct scatter_chain *next; /* pointer to next scatter item */
943 struct obj *obj; /* pointer to the object */
944 xchar ox; /* location of */
945 xchar oy; /* item */
946 schar dx; /* direction of */
947 schar dy; /* travel */
948 int range; /* range of object */
949 boolean stopped; /* flag for in-motion/stopped */
953 * scflags:
954 * VIS_EFFECTS Add visual effects to display
955 * MAY_HITMON Objects may hit monsters
956 * MAY_HITYOU Objects may hit hero
957 * MAY_HIT Objects may hit you or monsters
958 * MAY_DESTROY Objects may be destroyed at random
959 * MAY_FRACTURE Stone objects can be fractured (statues, boulders)
962 /* returns number of scattered objects */
963 long
964 scatter(sx,sy,blastforce,scflags, obj)
965 int sx,sy; /* location of objects to scatter */
966 int blastforce; /* force behind the scattering */
967 unsigned int scflags;
968 struct obj *obj; /* only scatter this obj */
970 register struct obj *otmp;
971 register int tmp;
972 int farthest = 0;
973 uchar typ;
974 long qtmp;
975 boolean used_up;
976 boolean individual_object = obj ? TRUE : FALSE;
977 struct monst *mtmp;
978 struct scatter_chain *stmp, *stmp2 = 0;
979 struct scatter_chain *schain = (struct scatter_chain *)0;
980 long total = 0L;
982 while ((otmp = individual_object ? obj : level.objects[sx][sy]) != 0) {
983 if (otmp->quan > 1L) {
984 qtmp = otmp->quan - 1;
985 if (qtmp > LARGEST_INT) qtmp = LARGEST_INT;
986 qtmp = (long)rnd((int)qtmp);
987 otmp = splitobj(otmp, qtmp);
988 } else {
989 obj = (struct obj *)0; /* all used */
991 obj_extract_self(otmp);
992 used_up = FALSE;
994 /* 9 in 10 chance of fracturing boulders or statues */
995 if ((scflags & MAY_FRACTURE)
996 && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE))
997 && rn2(10)) {
998 if (otmp->otyp == BOULDER) {
999 pline("%s apart.", Tobjnam(otmp, "break"));
1000 fracture_rock(otmp);
1001 place_object(otmp, sx, sy);
1002 if ((otmp = sobj_at(BOULDER, sx, sy)) != 0) {
1003 /* another boulder here, restack it to the top */
1004 obj_extract_self(otmp);
1005 place_object(otmp, sx, sy);
1007 } else {
1008 struct trap *trap;
1010 if ((trap = t_at(sx,sy)) && (trap->ttyp == STATUE_TRAP || trap->ttyp == SATATUE_TRAP))
1011 deltrap(trap);
1012 pline("%s.", Tobjnam(otmp, "crumble"));
1013 (void) break_statue(otmp);
1014 place_object(otmp, sx, sy); /* put fragments on floor */
1016 used_up = TRUE;
1018 /* 1 in 10 chance of destruction of obj; glass, egg destruction
1019 * Amy edit: should be way less harsh */
1020 } else if ((scflags & MAY_DESTROY) && !rn2(otmp->oerodeproof ? 100 : 20) && (!rn2(10)
1021 || (objects[otmp->otyp].oc_material == MT_GLASS
1022 || objects[otmp->otyp].oc_material == MT_OBSIDIAN
1023 || is_vitric(otmp)
1024 || otmp->otyp == EGG))) {
1025 if (breaks(otmp, (xchar)sx, (xchar)sy)) used_up = TRUE;
1028 if (!used_up) {
1029 stmp = (struct scatter_chain *)
1030 alloc(sizeof(struct scatter_chain));
1031 stmp->next = (struct scatter_chain *)0;
1032 stmp->obj = otmp;
1033 stmp->ox = sx;
1034 stmp->oy = sy;
1035 tmp = rn2(8); /* get the direction */
1036 stmp->dx = xdir[tmp];
1037 stmp->dy = ydir[tmp];
1038 tmp = blastforce - (otmp->owt/40);
1039 if (tmp < 1) tmp = 1;
1040 stmp->range = rnd(tmp); /* anywhere up to that determ. by wt */
1041 if (farthest < stmp->range) farthest = stmp->range;
1042 stmp->stopped = FALSE;
1043 if (!schain)
1044 schain = stmp;
1045 else
1046 stmp2->next = stmp;
1047 stmp2 = stmp;
1051 while (farthest-- > 0) {
1052 for (stmp = schain; stmp; stmp = stmp->next) {
1053 if ((stmp->range-- > 0) && (!stmp->stopped)) {
1054 bhitpos.x = stmp->ox + stmp->dx;
1055 bhitpos.y = stmp->oy + stmp->dy;
1056 typ = levl[bhitpos.x][bhitpos.y].typ;
1057 if(!isok(bhitpos.x, bhitpos.y)) {
1058 bhitpos.x -= stmp->dx;
1059 bhitpos.y -= stmp->dy;
1060 stmp->stopped = TRUE;
1061 } else if(!ZAP_POS(typ) ||
1062 closed_door(bhitpos.x, bhitpos.y)) {
1063 bhitpos.x -= stmp->dx;
1064 bhitpos.y -= stmp->dy;
1065 stmp->stopped = TRUE;
1066 } else if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
1067 if (scflags & MAY_HITMON) {
1068 stmp->range--;
1069 if (ohitmon((struct monst *)0, mtmp, stmp->obj, 1, FALSE)) {
1070 stmp->obj = (struct obj *)0;
1071 stmp->stopped = TRUE;
1074 } else if (bhitpos.x==u.ux && bhitpos.y==u.uy) {
1075 if (scflags & MAY_HITYOU) {
1076 int hitvalu, hitu;
1078 if (multi) nomul(0, 0, FALSE);
1079 hitvalu = 8 + stmp->obj->spe;
1080 if (bigmonst(youmonst.data)) hitvalu++;
1081 hitu = thitu(hitvalu,
1082 dmgval(stmp->obj, &youmonst),
1083 stmp->obj, (char *)0);
1084 if (hitu) {
1085 stmp->range -= 3;
1086 stop_occupation();
1089 } else {
1090 if (scflags & VIS_EFFECTS) {
1091 /* tmp_at(bhitpos.x, bhitpos.y); */
1092 /* delay_output(); */
1095 stmp->ox = bhitpos.x;
1096 stmp->oy = bhitpos.y;
1100 for (stmp = schain; stmp; stmp = stmp2) {
1101 int x,y;
1103 stmp2 = stmp->next;
1104 x = stmp->ox; y = stmp->oy;
1105 if (stmp->obj) {
1106 if ( x!=sx || y!=sy )
1107 total += stmp->obj->quan;
1108 place_object(stmp->obj, x, y);
1109 stackobj(stmp->obj);
1111 free((void *)stmp);
1112 newsym(x,y);
1115 return total;
1118 /* Amy function for exploding mini-nukes fired from a fatman */
1119 void
1120 fatman_explosion(x, y, obj)
1121 int x, y;
1122 struct obj *obj;
1124 int fatmanx, fatmany;
1125 struct monst *mtmp;
1126 boolean willraze = FALSE;
1128 if (!obj) {
1129 impossible("fatman explosion bug");
1130 return; /* bug */
1133 int fatmanrange = 8;
1134 int fatmandamage = 10;
1135 if (obj->oartifact == ART_MEGATON_LOAD) {
1136 fatmanrange += 8;
1137 fatmandamage += 6;
1139 if (obj->oartifact == ART_LITTLE_BOY) {
1140 fatmanrange -= 5;
1141 fatmandamage -= 5;
1144 zap_strike_fx(x, y, AD_FIRE - 1);
1145 FalloutEffect += 50; /* yes it's unrealistic that it persists even if you immediately levport away, shut up :P */
1147 if (distu(x, y) <= (fatmanrange * fatmanrange)) {
1148 You("are caught in a nuclear explosion!");
1149 contaminate(rnz(100), TRUE);
1150 losehp(rn1(fatmandamage, fatmandamage), "mini-nuke explosion", KILLED_BY_AN);
1153 for (fatmanx = 0; fatmanx < COLNO; fatmanx++) {
1154 for (fatmany = 0; fatmany < ROWNO; fatmany++) {
1155 if (isok(fatmanx, fatmany)) {
1157 willraze = FALSE;
1159 if (dist2(fatmanx, fatmany, x, y) <= (fatmanrange * fatmanrange)) {
1160 mtmp = m_at(fatmanx, fatmany);
1161 if (mtmp && !DEADMONSTER(mtmp)) {
1162 mtmp->mhp -= rn1(fatmandamage, fatmandamage);
1163 pline("%s is caught in a nuclear explosion!", Monnam(mtmp));
1164 if (mtmp->mhp < 1) {
1165 pline("%s dies!", Monnam(mtmp));
1166 xkilled(mtmp,0);
1167 } else {
1168 wakeup(mtmp); /* monster becomes hostile */
1172 if (levl[fatmanx][fatmany].typ == ROCKWALL) willraze = TRUE;
1173 if (IS_STWALL(levl[fatmanx][fatmany].typ) && !(levl[fatmanx][fatmany].wall_info & W_NONDIGGABLE) ) willraze = TRUE;
1174 if (levl[fatmanx][fatmany].typ == TREE && !(levl[fatmanx][fatmany].wall_info & W_NONDIGGABLE) ) willraze = TRUE;
1176 if (levl[fatmanx][fatmany].typ == FARMLAND || levl[fatmanx][fatmany].typ == MOUNTAIN || levl[fatmanx][fatmany].typ == IRONBARS || levl[fatmanx][fatmany].typ == WOODENTABLE || levl[fatmanx][fatmany].typ == CARVEDBED || levl[fatmanx][fatmany].typ == STRAWMATTRESS || levl[fatmanx][fatmany].typ == STALACTITE || levl[fatmanx][fatmany].typ == CLOUD || levl[fatmanx][fatmany].typ == BUBBLES || levl[fatmanx][fatmany].typ == RAINCLOUD) willraze = TRUE;
1178 if (willraze) {
1179 levl[fatmanx][fatmany].typ = CORR;
1180 newsym(fatmanx,fatmany);
1191 * Splatter burning oil from x,y to the surrounding area.
1193 * This routine should really take a how and direction parameters.
1194 * The how is how it was caused, e.g. kicked verses thrown. The
1195 * direction is which way to spread the flaming oil. Different
1196 * "how"s would give different dispersal patterns. For example,
1197 * kicking a burning flask will splatter differently from a thrown
1198 * flask hitting the ground.
1200 * For now, just perform a "regular" explosion.
1202 void
1203 splatter_burning_oil(x, y)
1204 int x, y;
1206 explode(x, y, ZT_SPELL(ZT_FIRE), d(4,4), BURNING_OIL, EXPL_FIERY);
1209 #define BY_OBJECT ((struct monst *)0)
1211 STATIC_DCL int
1212 dp(n, p) /* 0 <= dp(n, p) <= n */
1213 int n, p;
1215 int tmp = 0;
1216 while (n--) tmp += !rn2(p);
1217 return tmp;
1220 #define GRENADE_TRIGGER(obj) \
1221 if (((obj)->otyp == FRAG_GRENADE) && !stack_too_big(obj)) { \
1222 delquan = dp((obj)->quan, 10); \
1223 no_fiery += delquan; \
1224 } else if (((obj)->otyp == GAS_GRENADE) && !stack_too_big(obj)) { \
1225 delquan = dp((obj)->quan, 10); \
1226 no_gas += delquan; \
1227 } else if (((obj)->otyp == STICK_OF_DYNAMITE) && !stack_too_big(obj)) { \
1228 delquan = (obj)->quan; \
1229 no_fiery += (obj)->quan * 2; \
1230 no_dig += (obj)->quan; \
1231 } else if (is_bullet(obj) && !stack_too_big(obj) && (!obj->blessed || !rn2(3)) ) /* have some chance to resist --Amy */ \
1232 delquan = dp((obj)->quan, 2); \
1233 else \
1234 delquan = 0
1236 struct grenade_callback {
1237 ExplodeRegion *fiery_area, *gas_area, *dig_area;
1238 boolean isyou;
1241 STATIC_DCL void grenade_effects(struct obj *,XCHAR_P,XCHAR_P,
1242 ExplodeRegion *,ExplodeRegion *,ExplodeRegion *,BOOLEAN_P);
1244 STATIC_DCL int
1245 grenade_fiery_callback(data, x, y)
1246 void * data;
1247 int x, y;
1249 int is_accessible = ZAP_POS(levl[x][y].typ);
1250 struct grenade_callback *gc = (struct grenade_callback *)data;
1251 if (is_accessible) {
1252 add_location_to_explode_region(gc->fiery_area, x, y);
1253 grenade_effects((struct obj *)0, x, y,
1254 gc->fiery_area, gc->gas_area, gc->dig_area, gc->isyou);
1256 return !is_accessible;
1259 STATIC_DCL int
1260 grenade_gas_callback(data, x, y)
1261 void * data;
1262 int x, y;
1264 int is_accessible = ZAP_POS(levl[x][y].typ);
1265 struct grenade_callback *gc = (struct grenade_callback *)data;
1266 if (is_accessible)
1267 add_location_to_explode_region(gc->gas_area, x, y);
1268 return !is_accessible;
1271 STATIC_DCL int
1272 grenade_dig_callback(data, x, y)
1273 void * data;
1274 int x, y;
1276 struct grenade_callback *gc = (struct grenade_callback *)data;
1277 if (dig_check(BY_OBJECT, FALSE, x, y))
1278 add_location_to_explode_region(gc->dig_area, x, y);
1279 return !ZAP_POS(levl[x][y].typ);
1282 STATIC_DCL void
1283 grenade_effects(source, x, y, fiery_area, gas_area, dig_area, isyou)
1284 struct obj *source;
1285 xchar x, y;
1286 ExplodeRegion *fiery_area, *gas_area, *dig_area;
1287 boolean isyou;
1289 int i, r;
1290 struct obj *obj, *obj2;
1291 struct monst *mon;
1293 * Note: These count explosive charges in arbitary units. Grenades
1294 * are counted as 1 and sticks of dynamite as 2 fiery and 1 dig.
1296 int no_gas = 0, no_fiery = 0, no_dig = 0;
1297 int delquan;
1298 boolean shielded = FALSE, redraw;
1299 struct grenade_callback gc;
1301 int xtrasiz = 0;
1302 if (Role_if(PM_GRENADONIN)) {
1304 if (isyou) {
1306 xtrasiz += rnd(1 + (u.ulevel / 2));
1308 if (!PlayerCannotUseSkills) {
1309 switch (P_SKILL(P_FIREARM)) {
1311 case P_BASIC: xtrasiz += rnd(2); break;
1312 case P_SKILLED: xtrasiz += rnd(4); break;
1313 case P_EXPERT: xtrasiz += rnd(8); break;
1314 case P_MASTER: xtrasiz += rnd(12); break;
1315 case P_GRAND_MASTER: xtrasiz += rnd(18); break;
1316 case P_SUPREME_MASTER: xtrasiz += rnd(25); break;
1317 default: break;
1320 } else xtrasiz += rno(20);
1324 if (source) {
1325 if (source->otyp == GAS_GRENADE) {
1326 no_gas += source->quan;
1327 no_gas += xtrasiz;
1328 } else if (source->otyp == FRAG_GRENADE) {
1329 no_fiery += source->quan;
1330 no_fiery += xtrasiz;
1331 } else if (source->otyp == STICK_OF_DYNAMITE) {
1332 no_fiery += source->quan * 2;
1333 no_fiery += xtrasiz;
1334 no_dig += source->quan;
1335 no_dig += (xtrasiz / 2) + 1;
1337 redraw = source->where == OBJ_FLOOR;
1338 obj_extract_self(source);
1339 obfree(source, (struct obj *)0);
1340 if (redraw) newsym(x, y);
1342 mon = m_at(x, y);
1343 if (!mon && x == u.ux && y == u.uy)
1344 mon = u.usteed;
1345 if (mon && !DEADMONSTER(mon)) {
1346 if (resists_fire(mon) && !player_will_pierce_resistance()) {
1347 shielded = TRUE;
1348 } else {
1349 for(obj = mon->minvent; obj; obj = obj2) {
1350 obj2 = obj->nobj;
1351 if (!Role_if(PM_GRENADONIN)) GRENADE_TRIGGER(obj);
1352 if (Role_if(PM_GRENADONIN)) delquan = 0;
1353 for(i = 0; i < delquan; i++)
1354 m_useup(mon, obj);
1358 if (x == u.ux && y == u.uy) {
1360 if (uarmf && uarmf->oartifact == ART_SILVESTERBLAM) {
1361 if (uarmf->oeroded < MAX_ERODE) {
1362 Your("heels are damaged by the explosion!");
1363 uarmf->oeroded++;
1364 } else {
1365 useup(uarmf);
1366 Your("heels are destroyed by the explosion!");
1370 if (Fire_resistance || FireImmunity)
1371 shielded = TRUE;
1372 else
1373 for(obj = invent; obj; obj = obj2) {
1374 obj2 = obj->nobj;
1375 if (!Role_if(PM_GRENADONIN)) GRENADE_TRIGGER(obj);
1376 if (Role_if(PM_GRENADONIN)) delquan = 0;
1377 for(i = 0; i < delquan; i++)
1378 useup(obj);
1381 if (!shielded)
1382 for(obj = level.objects[x][y]; obj; obj = obj2) {
1383 obj2 = obj->nexthere;
1384 if (!Role_if(PM_GRENADONIN)) GRENADE_TRIGGER(obj);
1385 if (Role_if(PM_GRENADONIN)) delquan = 0;
1386 if (delquan) {
1387 if (isyou)
1388 useupf(obj, delquan);
1389 else if (delquan < obj->quan)
1390 obj->quan -= delquan;
1391 else
1392 delobj(obj);
1395 gc.fiery_area = fiery_area;
1396 gc.gas_area = gas_area;
1397 gc.dig_area = dig_area;
1398 gc.isyou = isyou;
1399 if (no_gas) {
1400 /* r = floor(log2(n))+1 */
1401 r = 0;
1402 while(no_gas) {
1403 r++;
1404 no_gas /= 2;
1406 xpathto(r, x, y, grenade_gas_callback, (void *)&gc);
1408 if (no_fiery) {
1409 /* r = floor(log2(n))+1 */
1410 r = 0;
1411 while(no_fiery) {
1412 r++;
1413 no_fiery /= 2;
1415 xpathto(r, x, y, grenade_fiery_callback, (void *)&gc);
1417 if (no_dig) {
1418 /* r = floor(log2(n))+1 */
1419 r = 0;
1420 while(no_dig) {
1421 r++;
1422 no_dig /= 2;
1424 xpathto(r, x, y, grenade_dig_callback, (void *)&gc);
1429 * Note: obj is not valid after return
1432 void
1433 grenade_explode(obj, x, y, isyou, dest)
1434 struct obj *obj;
1435 int x, y;
1436 boolean isyou;
1437 int dest;
1439 int i, ztype;
1440 boolean shop_damage = FALSE;
1441 int ox, oy;
1442 ExplodeRegion *fiery_area, *gas_area, *dig_area;
1443 struct trap *trap;
1445 int grenadedamage;
1447 grenadedamage = d(3,6);
1449 if (isyou) {
1450 u.cnd_gunpowderused++;
1451 use_skill(P_FIREARM, (obj && obj->otyp == STICK_OF_DYNAMITE) ? 5 : 1);
1452 if (obj && obj->oartifact == ART_LOITEMUP) {
1453 use_skill(P_FIREARM, 2);
1454 use_skill(P_GUN_CONTROL, 2);
1458 if (obj && obj->oartifact == ART_DOUBLE_FREE_CORRUPTION) {
1459 int nastytrapdur = (Role_if(PM_GRADUATE) ? 6 : Role_if(PM_GEEK) ? 12 : 24);
1460 if (!nastytrapdur) nastytrapdur = 24; /* fail safe */
1461 int blackngdur = (Role_if(PM_GRADUATE) ? 2000 : Role_if(PM_GEEK) ? 1000 : 500);
1462 if (!blackngdur ) blackngdur = 500; /* fail safe */
1464 randomnastytrapeffect(rnz(nastytrapdur * (monster_difficulty() + 1)), blackngdur - (monster_difficulty() * 3));
1467 if (Role_if(PM_GRENADONIN) || (uarmf && uarmf->oartifact == ART_EIMI_WA_BAKADESU) ) {
1469 if (isyou) {
1471 grenadedamage += rnd(u.ulevel * 2);
1472 if (!rn2(5)) grenadedamage += rnd(u.ulevel);
1474 if (!PlayerCannotUseSkills) {
1475 switch (P_SKILL(P_FIREARM)) {
1477 case P_BASIC: grenadedamage += rnd(6); break;
1478 case P_SKILLED: grenadedamage += rnd(13); break;
1479 case P_EXPERT: grenadedamage += rnd(24); break;
1480 case P_MASTER: grenadedamage += rnd(40); break;
1481 case P_GRAND_MASTER: grenadedamage += rnd(60); break;
1482 case P_SUPREME_MASTER: grenadedamage += rnd(80); break;
1483 default: break;
1486 } else if (Role_if(PM_GRENADONIN)) grenadedamage += d(3,6);
1490 fiery_area = create_explode_region();
1491 gas_area = create_explode_region();
1492 dig_area = create_explode_region();
1493 grenade_effects(obj, x, y, fiery_area, gas_area, dig_area, isyou);
1494 if (fiery_area->nlocations) {
1495 ztype = isyou ? ZT_SPELL(ZT_FIRE) : -ZT_SPELL(ZT_FIRE);
1496 do_explode(x, y, fiery_area, ztype, grenadedamage, WEAPON_CLASS,
1497 EXPL_FIERY, dest, isyou);
1499 wake_nearto(x, y, 400);
1500 /* Like cartoons - the explosion first, then
1501 * the world deals with the holes produced ;)
1503 for(i = 0; i < dig_area->nlocations; i++) {
1504 ox = dig_area->locations[i].x;
1505 oy = dig_area->locations[i].y;
1506 if (IS_WALL(levl[ox][oy].typ) || IS_DOOR(levl[ox][oy].typ)) {
1507 watch_dig((struct monst *)0, ox, oy, TRUE);
1508 if (*in_rooms(ox, oy, SHOPBASE)) shop_damage = TRUE;
1510 digactualhole(ox, oy, BY_OBJECT, PIT);
1512 free_explode_region(dig_area);
1513 for(i = 0; i < fiery_area->nlocations; i++) {
1514 ox = fiery_area->locations[i].x;
1515 oy = fiery_area->locations[i].y;
1516 if ((trap = t_at(ox, oy)) != 0 && trap->ttyp == LANDMINE)
1517 blow_up_landmine(trap);
1519 free_explode_region(fiery_area);
1520 if (gas_area->nlocations) {
1521 ztype = isyou ? ZT_SPELL(ZT_POISON_GAS) : -ZT_SPELL(ZT_POISON_GAS);
1522 do_explode(x, y, gas_area, ztype, d(3,6), WEAPON_CLASS,
1523 EXPL_NOXIOUS, dest, isyou);
1525 free_explode_region(gas_area);
1526 if (shop_damage) pay_for_damage("damage", FALSE);
1529 void arm_bomb(obj, yours)
1530 struct obj *obj;
1531 boolean yours;
1533 struct obj *otmp = NULL;
1535 if (!is_grenade(obj)) return;
1537 if (obj->quan > 1L && yours) {
1538 if (obj == uwep && welded(obj)) {
1539 You("can only hold one armed grenade, but can't drop any to hold only one.");
1540 return;
1542 otmp = obj;
1543 obj = splitobj(otmp, 1L);
1544 attach_bomb_blow_timeout(obj, (obj->cursed ? rn2(5) + 2 : obj->blessed ? 4 : rn2(2) + 3), yours);
1545 obj_extract_self(otmp);
1546 if (otmp)
1547 otmp = hold_another_object(otmp, "You drop %s!", doname(otmp), (const char *)0);
1548 return;
1549 } else
1550 attach_bomb_blow_timeout(obj, (obj->cursed ? rn2(5) + 2 : obj->blessed ? 4 : rn2(2) + 3), yours);
1552 /* if (is_grenade(obj)) {
1553 attach_bomb_blow_timeout(obj,
1554 (obj->cursed ? rn2(5) + 2 : obj->blessed ? 4 :
1555 rn2(2) + 3)
1556 , yours);
1558 /* Otherwise, do nothing */
1561 #endif /* OVL1 */
1563 /*explode.c*/