OSDN Git Service

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