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. */
9 /* ExplodeRegions share some commonalities with NhRegions, but not enough to
10 * make it worth trying to create a common implementation.
14 /*xchar*/int blast
; /* blast symbol */
15 xchar shielded
; /* True if this location is shielded */
19 ExplodeLocation
*locations
;
20 short nlocations
, alocations
;
23 STATIC_DCL ExplodeRegion
*
24 create_explode_region()
28 reg
= (ExplodeRegion
*)alloc(sizeof(ExplodeRegion
));
29 reg
->locations
= (ExplodeLocation
*)0;
36 add_location_to_explode_region(reg
, x
, y
)
42 for(i
= 0; i
< reg
->nlocations
; i
++)
43 if (reg
->locations
[i
].x
== x
&& reg
->locations
[i
].y
== y
)
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
);
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; */
62 compare_explode_location(loc1
, loc2
)
63 ExplodeLocation
*loc1
, *loc2
;
65 return loc1
->y
== loc2
->y
? loc1
->x
- loc2
->x
: loc1
->y
- loc2
->y
;
69 set_blast_symbols(reg
)
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
++) {
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)
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 */
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
];
115 free_explode_region(reg
)
118 free((void *)reg
->locations
);
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/
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
144 * Your spell FIRE SCROLL FIERY Reading a scroll of fire
145 * Your spell FIRE WAND FIERY Zap yourself with wand/
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
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
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
179 * Nature is encoded as (abs(type) % 10) and src is determined using the
182 * -30 - -39 Mon's wand
183 * -20 - -29 Mon's breath
184 * -10 - -19 Mon's spell
188 * 20 - 29 Your breath
190 * There is only one special type currently defined:
191 * -1 Exploding gas spore
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 */
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
);
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 */
216 int type
; /* the same as in zap.c */
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 */
228 int idamres
, idamnonres
;
232 boolean shopdamage
= FALSE
;
233 boolean generic
= FALSE
;
234 boolean silent
= FALSE
, remote
= FALSE
;
237 if (dest
> 0) silent
= TRUE
;
238 if (dest
== 2) remote
= TRUE
;
240 if (olet
== WAND_CLASS
) /* retributive strike */
241 switch (Role_switch
) {
243 /*WAC add Flame, Ice mages, Necromancer */
247 case PM_WIZARD
: damu
/= 5;
250 case PM_KNIGHT
: damu
/= 2;
255 if (olet
== MON_EXPLODE
) {
257 killer
= 0; /* set again later as needed */
260 switch (abs(type
) % 10) {
261 case 0: str
= "magical blast";
264 case 1: str
= olet
== BURNING_OIL
? "burning oil" :
265 olet
== SCROLL_CLASS
? "tower of flame" :
269 case 2: str
= "ball of cold";
272 case 3: str
= "ball of sleep";
275 /* Assume that wands are death, others are disintegration */
276 case 4: str
= (olet
== WAND_CLASS
) ? "death field" :
277 "disintegration field";
280 case 5: str
= "ball of lightning";
283 case 6: str
= "poison gas cloud";
286 case 7: str
= "splash of acid";
289 case 8: str
= "pure energy irradiation";
292 case 9: str
= "psionic energy";
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);
306 any_shield
= visible
= FALSE
;
307 for(i
= 0; i
< area
->nlocations
; i
++) {
309 xi
= area
->locations
[i
].x
;
310 yi
= area
->locations
[i
].y
;
311 if (xi
== u
.ux
&& yi
== u
.uy
) {
318 explmask
= !!Antimagic
;
321 explmask
= !!Fire_resistance
;
324 explmask
= !!Cold_resistance
;
327 explmask
= !!Sleep_resistance
;
330 explmask
= (olet
== WAND_CLASS
) ?
331 !!(nonliving(youmonst
.data
) || is_demon(youmonst
.data
) || Death_resistance
) :
335 explmask
= !!Shock_resistance
;
338 explmask
= !!Poison_resistance
;
341 explmask
= !!Acid_resistance
;
344 impossible("explosion type %d?", adtyp
);
350 if (!mtmp
&& xi
== u
.ux
&& yi
== u
.uy
)
359 explmask
|= resists_magm(mtmp
);
362 explmask
|= (resists_fire(mtmp
) && !player_will_pierce_resistance() );
365 explmask
|= (resists_cold(mtmp
) && !player_will_pierce_resistance() );
368 explmask
|= resists_sleep(mtmp
);
371 explmask
|= (olet
== WAND_CLASS
) ?
372 (nonliving(mtmp
->data
) || is_demon(mtmp
->data
)) :
373 resists_disint(mtmp
);
376 explmask
|= (resists_elec(mtmp
) && !player_will_pierce_resistance() );
379 explmask
|= (resists_poison(mtmp
) && !player_will_pierce_resistance()) ;
382 explmask
|= (resists_acid(mtmp
) && !player_will_pierce_resistance()) ;
385 impossible("explosion type %d?", adtyp
);
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
);
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
;
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
);
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
);
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 */
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. */
456 tmp_at(DISP_END
, 0); /* clear the explosion */
460 } else if (!remote
) {
461 if (olet
== MON_EXPLODE
) {
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
);
481 if (!mtmp
&& xi
== u
.ux
&& yi
== u
.uy
)
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!",
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" :
498 if (!silent
) pline("%s gets slightly %s!",
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" :
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
;
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
530 if (resist(mtmp
, olet
, 0, FALSE
)) {
531 if (!silent
&& cansee(xi
,yi
))
532 pline("%s resists the %s!", Monnam(mtmp
), str
);
535 if (mtmp
== u
.ustuck
)
537 if (resists_cold(mtmp
) && adtyp
== AD_FIRE
)
539 else if (resists_fire(mtmp
) && adtyp
== AD_COLD
)
542 mtmp
->mhp
-= (idamres
+ idamnonres
);
544 if (mtmp
->mhp
> 0 && !remote
)
545 showdmg(mdam
+ idamres
+ idamnonres
);
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);
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
;
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
) &&
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) {
587 if (damu
< 1) damu
= 1;
589 if (uarms
&& uarms
->oartifact
== ART_OF_NULLING
&& damu
> 0) {
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) {
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;
623 if (damu
< 1) damu
= 1;
626 if (Race_if(PM_ITAQUE
) && damu
> 0) {
628 damu
*= (100 - u
.ulevel
);
630 if (damu
< 1) damu
= 1;
633 if (is_sand(u
.ux
,u
.uy
) && damu
> 0) {
637 if (damu
< 1) damu
= 1;
640 if (Race_if(PM_CARTHAGE
) && u
.usteed
&& (mcalcmove(u
.usteed
) < 12) && damu
> 0) {
644 if (damu
< 1) damu
= 1;
647 if (StrongDetect_monsters
&& damu
> 0) {
651 if (damu
< 1) damu
= 1;
654 if (Race_if(PM_VIKING
) && damu
> 0) {
659 if (Race_if(PM_SPARD
) && damu
> 0) {
664 if (Race_if(PM_MAYMES
) && uwep
&& weapon_type(uwep
) == P_FIREARM
&& damu
> 0) {
668 if (damu
< 1) damu
= 1;
671 if (damu
> 0 && uarmf
&& itemhasappearance(uarmf
, APP_MARJI_SHOES
)) {
675 if (damu
< 1) damu
= 1;
678 if (damu
> 0 && uarm
&& uarm
->oartifact
== ART_KRAH_HOOOOO
) {
682 if (damu
< 1) damu
= 1;
685 if (damu
> 0 && uarms
&& uarms
->oartifact
== ART_AL_UD
) {
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;
706 if (StrongWinceState
) {
710 if (damu
<= damutemp
) damu
++;
712 if (ACURR(A_CON
) == 2) {
716 if (ACURR(A_CON
) == 1) {
720 if (uarmh
&& uarmh
->oartifact
== ART_HEAD_W
) {
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)) ) {
738 You("are unharmed!");
739 } else if (StrongWonderlegs
&& !rn2(10) && Wounded_legs
) {
741 You("are unharmed!");
742 } else if (u
.metalguard
) {
745 Your("metal guard prevents the damage!");
746 } else if (uarms
&& uarms
->otyp
== NULLIFICATION_SHIELD
&& !rn2(20) ) {
748 Your("shield nullifies the damage!");
749 } else if (uimplant
&& uimplant
->oartifact
== ART_GLEN_HOSPITAL
&& !rn2(10)) {
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
)) ) {
754 Your("cone heels nullify the damage!");
755 } else if (u
.twoweap
&& uswapwep
&& uswapwep
->oartifact
== ART_SHIELD_TONFA
&& !rn2(10)) {
757 Your("tonfa nullifies the damage!");
758 } else if (uarm
&& uarm
->oartifact
== ART_SUSA_MAIL
&& !rn2(10)) {
760 Your("armor nullifies the damage!");
761 } else if (uarmf
&& uarmf
->oartifact
== ART_ELENA_S_EPITOME
&& !rn2(10)) {
763 Your("pair of heels nullifies the damage!");
764 } else if (uarmf
&& uarmf
->oartifact
== ART_IRMA_S_CHOICE
&& !rn2(10)) {
766 Your("pair of heels nullifies the damage!");
767 } else if (uarms
&& uarms
->oartifact
== ART_PLANK_OF_CARNEADES
&& !rn2(10)) {
769 Your("shield nullifies the damage!");
770 } else if (uarms
&& uarms
->oartifact
== ART_AL_UD
&& !rn2(10)) {
772 Your("shield nullifies the damage!");
773 } else if (uarms
&& uarms
->oartifact
== ART_DOUBLEBLANK
&& !rn2(10)) {
775 Your("shield nullifies the damage!");
776 } else if (uwep
&& uwep
->oartifact
== ART_ETERNAL_LONGING
&& !rn2(10)) {
778 Your("soft lady shoe nullifies the damage!");
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) {
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
);
815 u
.usymbiote
.mhp
= u
.usymbiote
.mhpmax
;
816 Your("symbiote glows, and your amulet crumbles to dust!");
818 u
.usymbiote
.active
= 0;
819 u
.usymbiote
.mnum
= PM_PLAYERMON
;
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
) {
831 pline("Your mana shield takes the damage for you!");
833 } else if (uhurt
== 2) {
841 if (u
.uhp
<= 0 || (Upolyd
&& u
.mh
<= 0)) {
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;*/
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
);
864 killer_format
= KILLED_BY
;
865 strcpy(killer_buf
, str
);
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
);
874 exercise(A_STR
, FALSE
);
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!");
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)) {
890 if (!obsidianprotection()) switch (rn2(11)) {
892 make_sick(Sick
? Sick
/2L + 1L : (long)rn1(ACURR(A_CON
),20), "celtic sickness", TRUE
, SICK_NONVOMITABLE
);
894 case 1: make_blinded(Blinded
+ 25, TRUE
);
896 case 2: if (!Confusion
)
897 You("suddenly feel %s.", FunnyHallu
? "trippy" : "confused");
898 make_confused(HConfusion
+ 25, TRUE
);
900 case 3: make_stunned(HStun
+ 25, TRUE
);
902 case 4: make_numbed(HNumbed
+ 25, TRUE
);
904 case 5: make_frozen(HFrozen
+ 25, TRUE
);
906 case 6: make_burned(HBurned
+ 25, TRUE
);
908 case 7: (void) adjattrib(rn2(A_MAX
), -1, FALSE
, TRUE
);
910 case 8: (void) make_hallucinated(HHallucination
+ 25, TRUE
, 0L);
912 case 9: make_feared(HFeared
+ 25, TRUE
);
914 case 10: make_dimmed(HDimmed
+ 25, TRUE
);
918 } else u
.berserktime
= 25;
924 pay_for_damage(adtyp
== AD_FIRE
? "burn away" :
925 adtyp
== AD_COLD
? "shatter" :
926 adtyp
== AD_DISN
? "disintegrate" : "destroy",
930 /* explosions are noisy */
932 if (i
< 50) i
= 50; /* in case random damage is very small */
933 wake_nearto(x
, y
, i
);
935 if (iflags
.usealleg
) cleanup_explosions();
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 */
946 schar dx
; /* direction of */
947 schar dy
; /* travel */
948 int range
; /* range of object */
949 boolean stopped
; /* flag for in-motion/stopped */
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 */
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
;
976 boolean individual_object
= obj
? TRUE
: FALSE
;
978 struct scatter_chain
*stmp
, *stmp2
= 0;
979 struct scatter_chain
*schain
= (struct scatter_chain
*)0;
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
);
989 obj
= (struct obj
*)0; /* all used */
991 obj_extract_self(otmp
);
994 /* 9 in 10 chance of fracturing boulders or statues */
995 if ((scflags
& MAY_FRACTURE
)
996 && ((otmp
->otyp
== BOULDER
) || (otmp
->otyp
== STATUE
))
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
);
1010 if ((trap
= t_at(sx
,sy
)) && (trap
->ttyp
== STATUE_TRAP
|| trap
->ttyp
== SATATUE_TRAP
))
1012 pline("%s.", Tobjnam(otmp
, "crumble"));
1013 (void) break_statue(otmp
);
1014 place_object(otmp
, sx
, sy
); /* put fragments on floor */
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
1024 || otmp
->otyp
== EGG
))) {
1025 if (breaks(otmp
, (xchar
)sx
, (xchar
)sy
)) used_up
= TRUE
;
1029 stmp
= (struct scatter_chain
*)
1030 alloc(sizeof(struct scatter_chain
));
1031 stmp
->next
= (struct scatter_chain
*)0;
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
;
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
) {
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
) {
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);
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
) {
1104 x
= stmp
->ox
; y
= stmp
->oy
;
1106 if ( x
!=sx
|| y
!=sy
)
1107 total
+= stmp
->obj
->quan
;
1108 place_object(stmp
->obj
, x
, y
);
1109 stackobj(stmp
->obj
);
1118 /* Amy function for exploding mini-nukes fired from a fatman */
1120 fatman_explosion(x
, y
, obj
)
1124 int fatmanx
, fatmany
;
1126 boolean willraze
= FALSE
;
1129 impossible("fatman explosion bug");
1133 int fatmanrange
= 8;
1134 int fatmandamage
= 10;
1135 if (obj
->oartifact
== ART_MEGATON_LOAD
) {
1139 if (obj
->oartifact
== ART_LITTLE_BOY
) {
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
)) {
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
));
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
;
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.
1203 splatter_burning_oil(x
, y
)
1206 explode(x
, y
, ZT_SPELL(ZT_FIRE
), d(4,4), BURNING_OIL
, EXPL_FIERY
);
1209 #define BY_OBJECT ((struct monst *)0)
1212 dp(n
, p
) /* 0 <= dp(n, p) <= n */
1216 while (n
--) tmp
+= !rn2(p
);
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); \
1236 struct grenade_callback
{
1237 ExplodeRegion
*fiery_area
, *gas_area
, *dig_area
;
1241 STATIC_DCL
void grenade_effects(struct obj
*,XCHAR_P
,XCHAR_P
,
1242 ExplodeRegion
*,ExplodeRegion
*,ExplodeRegion
*,BOOLEAN_P
);
1245 grenade_fiery_callback(data
, 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
;
1260 grenade_gas_callback(data
, x
, y
)
1264 int is_accessible
= ZAP_POS(levl
[x
][y
].typ
);
1265 struct grenade_callback
*gc
= (struct grenade_callback
*)data
;
1267 add_location_to_explode_region(gc
->gas_area
, x
, y
);
1268 return !is_accessible
;
1272 grenade_dig_callback(data
, 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
);
1283 grenade_effects(source
, x
, y
, fiery_area
, gas_area
, dig_area
, isyou
)
1286 ExplodeRegion
*fiery_area
, *gas_area
, *dig_area
;
1290 struct obj
*obj
, *obj2
;
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;
1298 boolean shielded
= FALSE
, redraw
;
1299 struct grenade_callback gc
;
1302 if (Role_if(PM_GRENADONIN
)) {
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;
1320 } else xtrasiz
+= rno(20);
1325 if (source
->otyp
== GAS_GRENADE
) {
1326 no_gas
+= source
->quan
;
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
);
1343 if (!mon
&& x
== u
.ux
&& y
== u
.uy
)
1345 if (mon
&& !DEADMONSTER(mon
)) {
1346 if (resists_fire(mon
) && !player_will_pierce_resistance()) {
1349 for(obj
= mon
->minvent
; obj
; obj
= obj2
) {
1351 if (!Role_if(PM_GRENADONIN
)) GRENADE_TRIGGER(obj
);
1352 if (Role_if(PM_GRENADONIN
)) delquan
= 0;
1353 for(i
= 0; i
< delquan
; i
++)
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!");
1366 Your("heels are destroyed by the explosion!");
1370 if (Fire_resistance
|| FireImmunity
)
1373 for(obj
= invent
; obj
; obj
= obj2
) {
1375 if (!Role_if(PM_GRENADONIN
)) GRENADE_TRIGGER(obj
);
1376 if (Role_if(PM_GRENADONIN
)) delquan
= 0;
1377 for(i
= 0; i
< delquan
; i
++)
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;
1388 useupf(obj
, delquan
);
1389 else if (delquan
< obj
->quan
)
1390 obj
->quan
-= delquan
;
1395 gc
.fiery_area
= fiery_area
;
1396 gc
.gas_area
= gas_area
;
1397 gc
.dig_area
= dig_area
;
1400 /* r = floor(log2(n))+1 */
1406 xpathto(r
, x
, y
, grenade_gas_callback
, (void *)&gc
);
1409 /* r = floor(log2(n))+1 */
1415 xpathto(r
, x
, y
, grenade_fiery_callback
, (void *)&gc
);
1418 /* r = floor(log2(n))+1 */
1424 xpathto(r
, x
, y
, grenade_dig_callback
, (void *)&gc
);
1429 * Note: obj is not valid after return
1433 grenade_explode(obj
, x
, y
, isyou
, dest
)
1440 boolean shop_damage
= FALSE
;
1442 ExplodeRegion
*fiery_area
, *gas_area
, *dig_area
;
1447 grenadedamage
= d(3,6);
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
) ) {
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;
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
)
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.");
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
);
1547 otmp
= hold_another_object(otmp
, "You drop %s!", doname(otmp
), (const char *)0);
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 :
1558 /* Otherwise, do nothing */