Exemplo n.º 1
0
boolean
inside_gas_cloud(void *p1, void *p2)
{
    struct region *reg;
    struct monst *mtmp;
    long dam;

    reg = (struct region *)p1;
    dam = (long)reg->arg;
    if (p2 == NULL) {   /* This means *YOU* Bozo! */
        if (nonliving(youmonst.data) || u.uinvulnerable)
            return FALSE;
        /* If you will unblind next turn, extend the blindness so that you do
         * not get a "You can see again!" message immediately before being
         * blinded again. */
        if (!Blind || Blinded == 1)
            make_blinded(2L, FALSE);
        if (Breathless)
            return FALSE;
        if (!Poison_resistance) {
            pline("Something is burning your %s!", makeplural(body_part(LUNG)));
            pline("You cough and spit blood!");
            losehp(rnd(dam) + 5, killer_msg(DIED, "a gas cloud"));
            return FALSE;
        } else {
            pline("You cough!");
            return FALSE;
        }
    } else {    /* A monster is inside the cloud */
        mtmp = (struct monst *)p2;

        /* Non living and non breathing monsters are not concerned */
        if (!nonliving(mtmp->data) && !breathless(mtmp->data)) {
            if (cansee(mtmp->mx, mtmp->my))
                pline("%s coughs!", Monnam(mtmp));
            setmangry(mtmp);
            if (haseyes(mtmp->data) && mtmp->mcansee) {
                mtmp->mblinded = 1;
                mtmp->mcansee = 0;
            }
            if (resists_poison(mtmp))
                return FALSE;
            mtmp->mhp -= rnd(dam) + 5;
            if (mtmp->mhp <= 0) {
                if (heros_fault(reg))
                    killed(mtmp);
                else
                    monkilled(mtmp, "gas cloud", AD_DRST);
                if (DEADMONSTER(mtmp)) {   /* not lifesaved */
                    return TRUE;
                }
            }
        }
    }
    return FALSE;       /* Monster is still alive */
}
Exemplo n.º 2
0
static void e_died(struct entity *etmp, int dest, int how)
{
	if (is_u(etmp)) {
		if (how == DROWNING) {
			killer = 0;	/* drown() sets its own killer */
			drown();
		} else if (how == BURNING) {
			killer = 0;	/* lava_effects() sets its own killer */
			lava_effects();
		} else {
			coord xy;

			/* use more specific killer if specified */
			if (!killer) {
			    killer_format = KILLED_BY_AN;
			    killer = "falling drawbridge";
			}
			done(how);
			/* So, you didn't die */
			if (!e_survives_at(etmp, etmp->ex, etmp->ey)) {
			    if (enexto(&xy, level, etmp->ex, etmp->ey, etmp->edata)) {
				pline("A %s force teleports you away...",
				      Hallucination ? "normal" : "strange");
				teleds(xy.x, xy.y, FALSE);
			    }
			    /* otherwise on top of the drawbridge is the
			     * only viable spot in the dungeon, so stay there
			     */
			}
		}
		/* we might have crawled out of the moat to survive */
		etmp->ex = u.ux,  etmp->ey = u.uy;
	} else {
		int entitycnt;

		killer = 0;
		/* fake "digested to death" damage-type suppresses corpse */
#define mk_message(dest) ((dest & 1) ? "" : NULL)
#define mk_corpse(dest)  ((dest & 2) ? AD_DGST : AD_PHYS)
		/* if monsters are moving, one of them caused the destruction */
		if (flags.mon_moving)
		    monkilled(etmp->emon, mk_message(dest), mk_corpse(dest));
		else		/* you caused it */
		    xkilled(etmp->emon, dest);
		etmp->edata = NULL;

		/* dead long worm handling */
		for (entitycnt = 0; entitycnt < ENTITIES; entitycnt++) {
		    if (etmp != &(occupants[entitycnt]) &&
			etmp->emon == occupants[entitycnt].emon)
			occupants[entitycnt].edata = NULL;
		}
#undef mk_message
#undef mk_corpse
	}
}
Exemplo n.º 3
0
/* Note: I had to choose one of three possible kinds of "type" when writing
 * this function: a wand type (like in zap.c), an adtyp, or an object type.
 * Wand types get complex because they must be converted to adtyps for
 * determining such things as fire resistance.  Adtyps get complex in that
 * they don't supply enough information--was it a player or a monster that
 * did it, and with a wand, spell, or breath weapon?  Object types share both
 * these disadvantages....
 *
 * The descr argument should be used to describe the explosion. It should be
 * a string suitable for use with an().
 * raylevel is used for explosions caused by skilled wand usage (0=no wand)
 */
void
explode(int x, int y, int type, /* the same as in zap.c */
        int dam, char olet, int expltype, const char *descr, int raylevel)
{
    int i, j, k, damu = dam;
    boolean visible, any_shield, resist_death;
    resist_death = FALSE;
    int uhurt = 0;      /* 0=unhurt, 1=items damaged, 2=you and items damaged */
    const char *str;
    const char *dispbuf = "";   /* lint suppression; I think the code's OK */
    boolean expl_needs_the = TRUE;
    int idamres, idamnonres;
    struct monst *mtmp;
    uchar adtyp;
    int explmask[3][3];

    /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
    boolean shopdamage = FALSE;

#if 0
    /* Damage reduction from wand explosions */
    if (olet == WAND_CLASS)     /* retributive strike */
        switch (Role_switch) {
        case PM_PRIEST:
        case PM_MONK:
        case PM_WIZARD:
            damu /= 5;
            break;
        case PM_HEALER:
        case PM_KNIGHT:
            damu /= 2;
            break;
        default:
            break;
        }
#endif
    if (olet == MON_EXPLODE) {
        str = descr;
        adtyp = AD_PHYS;
        if (Hallucination) {
            int name = rndmonidx();

            dispbuf = msgcat(s_suffix(monnam_for_index(name)), " explosion");
            expl_needs_the = !monnam_is_pname(name);
        } else {
            dispbuf = str;
        }
    } else {
        int whattype = abs(type) % 10;

        adtyp = whattype + 1;
        boolean done = FALSE, hallu = Hallucination;

        if (hallu) {
            do {
                whattype = rn2(8);
            } while (whattype == 3);
        }
tryagain:
        switch (whattype) {
        case 0:
            str = "magical blast";
            break;
        case 1:
            str =
                olet == BURNING_OIL ? "burning oil" : olet ==
                SCROLL_CLASS ? "tower of flame" : "fireball";
            break;
        case 2:
            str = "ball of cold";
            break;
        case 3:
            str = "sleeping gas";
            break;
        case 4:
            str = (olet == WAND_CLASS) ? "death field" : "disintegration field";
            break;
        case 5:
            str = "ball of lightning";
            break;
        case 6:
            str = "poison gas cloud";
            break;
        case 7:
            str = "splash of acid";
            break;
        default:
            impossible("explosion base type %d?", type);
            return;
        }
        if (!done) {
            dispbuf = str;
            done = TRUE;
            if (hallu) {
                whattype = adtyp - 1;
                goto tryagain;
            }
        }
    }

    any_shield = visible = FALSE;
    for (i = 0; i < 3; i++)
        for (j = 0; j < 3; j++) {
            if (!isok(i + x - 1, j + y - 1)) {
                explmask[i][j] = 2;
                continue;
            } else
                explmask[i][j] = 0;

            if (i + x - 1 == u.ux && j + y - 1 == u.uy) {
                switch (adtyp) {
                case AD_PHYS:
                    explmask[i][j] = 0;
                    break;
                case AD_MAGM:
                    explmask[i][j] = !!(raylevel >= P_EXPERT || Antimagic);
                    break;
                case AD_FIRE:
                    explmask[i][j] = !!Fire_resistance;
                    break;
                case AD_COLD:
                    explmask[i][j] = !!Cold_resistance;
                    break;
                case AD_SLEE:
                    explmask[i][j] = !!Sleep_resistance;
                    break;
                case AD_DISN:
                    if (raylevel == P_UNSKILLED && Drain_resistance)
                        resist_death = TRUE;
                    /* why MR doesn't resist general deathfields is beyond me, but... */
                    if (nonliving(youmonst.data) ||
                            is_demon(youmonst.data))
                        resist_death = TRUE;
                    if (raylevel && Antimagic)
                        resist_death = TRUE;
                    if (raylevel >= P_EXPERT && !Drain_resistance)
                        resist_death = FALSE;
                    explmask[i][j] =
                        (olet == WAND_CLASS) ? !!resist_death :
                        !!Disint_resistance;
                    break;
                case AD_ELEC:
                    explmask[i][j] = !!Shock_resistance;
                    break;
                case AD_DRST:
                    explmask[i][j] = !!Poison_resistance;
                    break;
                case AD_ACID:
                    explmask[i][j] = !!Acid_resistance;
                    break;
                default:
                    impossible("explosion type %d?", adtyp);
                    break;
                }
            }
            /* can be both you and mtmp if you're swallowed */
            mtmp = m_at(level, i + x - 1, j + y - 1);
            if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy)
                mtmp = u.usteed;
            if (mtmp) {
                if (mtmp->mhp < 1)
                    explmask[i][j] = 2;
                else
                    switch (adtyp) {
                    case AD_PHYS:
                        break;
                    case AD_MAGM:
                        explmask[i][j] |= (raylevel >= 4 || resists_magm(mtmp));
                        break;
                    case AD_FIRE:
                        explmask[i][j] |= resists_fire(mtmp);
                        break;
                    case AD_COLD:
                        explmask[i][j] |= resists_cold(mtmp);
                        break;
                    case AD_SLEE:
                        explmask[i][j] |= resists_sleep(mtmp);
                    case AD_DISN:
                        if (raylevel == P_UNSKILLED && resists_drli(mtmp))
                            resist_death = TRUE;
                        if (nonliving(mtmp->data) ||
                                is_demon(mtmp->data))
                            resist_death = TRUE;
                        if (raylevel && resists_magm(mtmp))
                            resist_death = TRUE;
                        if (raylevel >= P_EXPERT && !resists_drli(mtmp))
                            resist_death = FALSE;
                        explmask[i][j] |=
                            (olet == WAND_CLASS) ? resist_death :
                            resists_disint(mtmp);
                        break;
                    case AD_ELEC:
                        explmask[i][j] |= resists_elec(mtmp);
                        break;
                    case AD_DRST:
                        explmask[i][j] |= resists_poison(mtmp);
                        break;
                    case AD_ACID:
                        explmask[i][j] |= resists_acid(mtmp);
                        break;
                    default:
                        impossible("explosion type %d?", adtyp);
                        break;
                    }
            }
            reveal_monster_at(i + x - 1, j + y - 1, TRUE);

            if (cansee(i + x - 1, j + y - 1))
                visible = TRUE;
            if (explmask[i][j] == 1)
                any_shield = TRUE;
        }

    if (visible) {
        struct tmp_sym *tsym = tmpsym_init(DISP_BEAM, 0);

        /* Start the explosion */
        for (i = 0; i < 3; i++)
            for (j = 0; j < 3; j++) {
                if (explmask[i][j] == 2)
                    continue;
                tmpsym_change(tsym, dbuf_explosion(expltype, explosion[i][j]));
                tmpsym_at(tsym, i + x - 1, j + y - 1);
            }
        flush_screen(); /* will flush screen and output */

        if (any_shield && flags.sparkle) {      /* simulate shield effect */
            for (k = 0; k < SHIELD_COUNT; k++) {
                for (i = 0; i < 3; i++)
                    for (j = 0; j < 3; j++) {
                        if (explmask[i][j] == 1)
                            /*
                             * Bypass tmpsym_at() and send the shield glyphs
                             * directly to the buffered screen.  tmpsym_at()
                             * will clean up the location for us later.
                             */
                            dbuf_set_effect(i + x - 1, j + y - 1,
                                            dbuf_effect(E_MISC,
                                                        shield_static[k]));
                    }
                flush_screen(); /* will flush screen and output */
                win_delay_output();
            }

            /* Cover last shield glyph with blast symbol. */
            for (i = 0; i < 3; i++)
                for (j = 0; j < 3; j++) {
                    if (explmask[i][j] == 1)
                        dbuf_set_effect(i + x - 1, j + y - 1,
                                        dbuf_explosion(expltype,
                                                       explosion[i][j]));
                }

        } else {        /* delay a little bit. */
            win_delay_output();
            win_delay_output();
        }

        tmpsym_end(tsym);       /* clear the explosion */
    } else {
        if (olet == MON_EXPLODE) {
            str = "explosion";
        }
        You_hear("a blast.");
    }

    if (dam)
        for (i = 0; i < 3; i++)
            for (j = 0; j < 3; j++) {
                if (explmask[i][j] == 2)
                    continue;
                if (i + x - 1 == u.ux && j + y - 1 == u.uy)
                    uhurt = (explmask[i][j] == 1) ? 1 : 2;
                idamres = idamnonres = 0;
                if (type >= 0)
                    zap_over_floor((xchar) (i + x - 1), (xchar) (j + y - 1),
                                   type, &shopdamage);

                mtmp = m_at(level, i + x - 1, j + y - 1);
                if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy)
                    mtmp = u.usteed;
                if (!mtmp)
                    continue;
                if (Engulfed && mtmp == u.ustuck) {
                    if (is_animal(u.ustuck->data))
                        pline("%s gets %s!", Monnam(u.ustuck),
                              (adtyp == AD_FIRE) ? "heartburn" :
                              (adtyp == AD_COLD) ? "chilly" :
                              (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ?
                                                    "irradiated by pure energy"
                                                    : "perforated") :
                              (adtyp == AD_ELEC) ? "shocked" :
                              (adtyp == AD_DRST) ? "poisoned" :
                              (adtyp == AD_ACID) ? "an upset stomach" :
                              "fried");
                    else
                        pline("%s gets slightly %s!", Monnam(u.ustuck),
                              (adtyp == AD_FIRE) ? "toasted" :
                              (adtyp == AD_COLD) ? "chilly" :
                              (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ?
                                                    "overwhelmed by pure energy"
                                                    : "perforated") :
                              (adtyp == AD_ELEC) ? "shocked" :
                              (adtyp == AD_DRST) ? "intoxicated" :
                              (adtyp == AD_ACID) ? "burned" : "fried");
                } else if (cansee(i + x - 1, j + y - 1)) {
                    if (mtmp->m_ap_type) seemimic(mtmp);
                    pline("%s is caught in %s%s!", Monnam(mtmp),
                          expl_needs_the ? "the " : "", dispbuf);
                }

                idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int)adtyp);
                idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int)adtyp);
                idamnonres += destroy_mitem(mtmp, POTION_CLASS, (int)adtyp);
                idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int)adtyp);
                idamnonres += destroy_mitem(mtmp, RING_CLASS, (int)adtyp);

                if (explmask[i][j] == 1) {
                    golemeffects(mtmp, (int)adtyp, dam + idamres);
                    mtmp->mhp -= idamnonres;
                } else {
                    /* call resist with 0 and do damage manually so 1) we can
                       get out the message before doing the damage, and 2) we
                       can call mondied, not killed, if it's not your blast */
                    int mdam = dam;

                    if (resist(mtmp, olet, 0, FALSE)) {
                        if (cansee(i + x - 1, j + y - 1))
                            pline("%s resists %s%s!", Monnam(mtmp),
                                  expl_needs_the ? "the " : "", dispbuf);
                        mdam = dam / 2;
                    }
                    if (mtmp == u.ustuck)
                        mdam *= 2;
                    if (resists_cold(mtmp) && adtyp == AD_FIRE)
                        mdam *= 2;
                    else if (resists_fire(mtmp) && adtyp == AD_COLD)
                        mdam *= 2;
                    if (adtyp == AD_MAGM && raylevel >= P_EXPERT && resists_magm(mtmp))
                        mdam = (mdam + 1) / 2;
                    if (adtyp == AD_SLEE && raylevel) {
                        sleep_monst(mtmp, mdam, WAND_CLASS);
                        mdam = 0;
                    }
                    if (adtyp == AD_DISN && raylevel) {
                        if (nonliving(mtmp->data) ||
                                is_demon(mtmp->data) ||
                                resists_magm(mtmp) ||
                                raylevel == P_UNSKILLED) {
                            /* monster is deathresistant or raylevel==unskilled,
                               since monster apparently failed to resist earlier,
                               monster must be vulnerable to drli */
                            /* FIXME: make a generic losexp() for monsters */
                            mdam = dice(2, 6);
                            if (cansee(i + x - 1, j + y - 1))
                                pline("%s suddenly seems weaker!", Monnam(mtmp));
                            mtmp->mhpmax -= mdam;
                            if (mtmp->m_lev == 0)
                                mdam = mtmp->mhp;
                            else
                                mtmp->m_lev--;
                        } else
                            mdam = mtmp->mhp; /* instadeath */
                    }
                    mtmp->mhp -= mdam;
                    mtmp->mhp -= (idamres + idamnonres);
                }
                if (mtmp->mhp <= 0) {
                    /* KMH -- Don't blame the player for pets killing gas
                       spores */
                    if (!flags.mon_moving)
                        killed(mtmp);
                    else
                        monkilled(mtmp, "", (int)adtyp);
                } else if (!flags.mon_moving)
                    setmangry(mtmp);
            }

    /* Do your injury last */
    if (uhurt) {
        if ((type >= 0 || adtyp == AD_PHYS) &&  /* gas spores */
                flags.verbose && olet != SCROLL_CLASS)
            pline("You are caught in %s%s!", expl_needs_the ? "the " : "",
                  dispbuf);
        /* do property damage first, in case we end up leaving bones */
        if (adtyp == AD_FIRE)
            burn_away_slime();
        if (u.uinvulnerable) {
            damu = 0;
            pline("You are unharmed!");
        } else if (Half_physical_damage && adtyp == AD_PHYS)
            damu = (damu + 1) / 2;
        else if (raylevel) {
            if (adtyp == AD_MAGM && Antimagic)
                damu = (damu + 1) / 2;
            if (adtyp == AD_SLEE) {
                helpless(damu, hr_asleep, "sleeping", NULL);
                damu = 0;
            }
            if (adtyp == AD_DISN) {
                if (nonliving(youmonst.data) ||
                        is_demon(youmonst.data) ||
                        Antimagic ||
                        raylevel == P_UNSKILLED) {
                    losexp("drained by a death field",FALSE);
                    damu = 0;
                } else {
                    done(DIED, "killed by a death field");
                    damu = 0; /* lifesaved */
                }
            }
        }
        if (adtyp == AD_FIRE) {
            burnarmor(&youmonst);
            set_candles_afire();
        }
        destroy_item(SCROLL_CLASS, (int)adtyp);
        destroy_item(SPBOOK_CLASS, (int)adtyp);
        destroy_item(POTION_CLASS, (int)adtyp);
        destroy_item(RING_CLASS, (int)adtyp);
        destroy_item(WAND_CLASS, (int)adtyp);

        ugolemeffects((int)adtyp, damu);
        if (uhurt == 2) {
            if (Upolyd)
                u.mh -= damu;
            else
                u.uhp -= damu;
        }

        if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
            int death = adtyp == AD_FIRE ? BURNING : DIED;
            const char *killer;

            if (olet == MON_EXPLODE) {
                killer = killer_msg(death, an(str));
            } else if (type >= 0 && olet != SCROLL_CLASS) {
                /* check whether or not we were the source of the explosion */
                if (!flags.mon_moving)
                    killer = msgprintf("caught %sself in %s own %s", uhim(),
                                       uhis(), str);
                else
                    killer = msgprintf("killed by a %s", str);
            } else if (!strcmp(str, "burning oil")) {
                /* This manual check hack really sucks */
                killer = killer_msg(death, str);
            } else {
                killer = killer_msg(death, an(str));
            }
            /* Known BUG: BURNING suppresses corpse in bones data, but done
               does not handle killer reason correctly */
            if (Upolyd) {
                rehumanize(death, killer);
            } else {
                done(death, killer);
            }
        }
        exercise(A_STR, FALSE);
    }

    if (shopdamage) {
        pay_for_damage(adtyp == AD_FIRE ? "burn away" : adtyp ==
                       AD_COLD ? "shatter" : adtyp ==
                       AD_DISN ? "disintegrate" : "destroy", FALSE);
    }

    /* explosions are noisy */
    i = dam * dam;
    if (i < 50)
        i = 50; /* in case random damage is very small */
    wake_nearto(x, y, i);
}