예제 #1
0
/*
 * Is (x,y) a good position of mtmp?  If mtmp is NULL, then is (x,y) good
 * for an object?
 *
 * This function will only look at mtmp->mdat, so makemon, mplayer, etc can
 * call it to generate new monster positions with fake monster structures.
 */
boolean goodpos(struct level *lev, int x, int y, struct monst *mtmp, unsigned gpflags)
{
	const struct permonst *mdat = NULL;
	boolean ignorewater = ((gpflags & MM_IGNOREWATER) != 0);

	if (!isok(x, y)) return FALSE;

	/* in many cases, we're trying to create a new monster, which
	 * can't go on top of the player or any existing monster.
	 * however, occasionally we are relocating engravings or objects,
	 * which could be co-located and thus get restricted a bit too much.
	 * oh well.
	 */
	if (mtmp != &youmonst && x == u.ux && y == u.uy
			&& (!u.usteed || mtmp != u.usteed))
		return FALSE;

	if (mtmp) {
	    struct monst *mtmp2 = m_at(lev, x,y);

	    /* Be careful with long worms.  A monster may be placed back in
	     * its own location.  Normally, if m_at() returns the same monster
	     * that we're trying to place, the monster is being placed in its
	     * own location.  However, that is not correct for worm segments,
	     * because all the segments of the worm return the same m_at().
	     * Actually we overdo the check a little bit--a worm can't be placed
	     * in its own location, period.  If we just checked for mtmp->mx
	     * != x || mtmp->my != y, we'd miss the case where we're called
	     * to place the worm segment and the worm's head is at x,y.
	     */
	    if (mtmp2 && (mtmp2 != mtmp || mtmp->wormno))
		return FALSE;

	    mdat = mtmp->data;
	    if (is_pool(lev, x,y) && !ignorewater) {
		if (mtmp == &youmonst)
			return !!(HLevitation || Flying || Wwalking ||
					Swimming || Amphibious);
		else	return (is_flyer(mdat) || is_swimmer(mdat) ||
							is_clinger(mdat));
	    } else if (mdat->mlet == S_EEL && rn2(13) && !ignorewater) {
		return FALSE;
	    } else if (is_lava(lev, x,y)) {
		if (mtmp == &youmonst)
		    return !!HLevitation;
		else
		    return is_flyer(mdat) || likes_lava(mdat);
	    }
	    if (passes_walls(mdat) && may_passwall(lev, x,y)) return TRUE;
	}
	if (!ACCESSIBLE(lev->locations[x][y].typ)) {
		if (!(is_pool(lev, x,y) && ignorewater)) return FALSE;
	}

	if (closed_door(lev, x, y) && (!mdat || !amorphous(mdat)))
		return FALSE;
	if (sobj_at(BOULDER, lev, x, y) && (!mdat || !throws_rocks(mdat)))
		return FALSE;
	return TRUE;
}
예제 #2
0
파일: pager.c 프로젝트: FredrIQ/nethack4
void
nh_describe_pos(int x, int y, struct nh_desc_buf *bufs, int *is_in)
{
    bufs->bgdesc[0] = '\0';
    bufs->trapdesc[0] = '\0';
    bufs->objdesc[0] = '\0';
    bufs->mondesc[0] = '\0';
    bufs->invisdesc[0] = '\0';
    bufs->effectdesc[0] = '\0';
    bufs->feature_described = FALSE;
    bufs->objcount = -1;

    if (is_in)
        *is_in = 0;

    if (!program_state.game_running || !isok(x, y))
        return;

    API_ENTRY_CHECKPOINT_RETURN_VOID_ON_ERROR();

    if (is_in) {
        if (IS_ROCK(level->locations[x][y].typ) || closed_door(level, x, y))
            *is_in = 1;
        else
            *is_in = 0;
    }

    int monid = dbuf_get_mon(x, y);
    int mem_bg = level->locations[x][y].mem_bg;

    describe_bg(x, y, mem_bg, bufs->bgdesc);

    int tt = level->locations[x][y].mem_trap;

    if (tt) {
        strcpy(bufs->trapdesc,
               trapexplain[level->locations[x][y].mem_trap - 1]);
        if (tt != BEAR_TRAP && tt != WEB && tt != STATUE_TRAP && mem_bg &&
            is_in)
            *is_in = 1;
    }

    bufs->objcount =
        describe_object(x, y, level->locations[x][y].mem_obj - 1, bufs->objdesc,
                        mem_bg && is_in, &bufs->feature_described);

    describe_mon(x, y, monid - 1, bufs->mondesc);

    if (level->locations[x][y].mem_invis)
        strcpy(bufs->invisdesc, invisexplain);

    if (Engulfed && (x != u.ux || y != u.uy)) {
        /* all locations when swallowed other than the hero are the monster */
        snprintf(bufs->effectdesc, SIZE(bufs->effectdesc), "interior of %s",
                Blind ? "a monster" : a_monnam(u.ustuck));
    }

    API_EXIT();
}
예제 #3
0
파일: pager.c 프로젝트: DanielT/NitroHack
static int describe_object(int x, int y, int votyp, char *buf)
{
    int num_objs = 0;
    struct obj *otmp;
    if (votyp == -1)
	return -1;
    
    otmp = vobj_at(x,y);
    
    if (!otmp || otmp->otyp != votyp) {
	if (votyp != STRANGE_OBJECT) {
	    otmp = mksobj(level, votyp, FALSE, FALSE);
	    if (otmp->oclass == COIN_CLASS)
		otmp->quan = 1L; /* to force pluralization off */
	    else if (otmp->otyp == SLIME_MOLD)
		otmp->spe = current_fruit;	/* give the fruit a type */
	    strcpy(buf, distant_name(otmp, xname));
	    dealloc_obj(otmp);
	    otmp = vobj_at(x,y); /* make sure we don't point to the temp obj any more */
	}
    } else
	strcpy(buf, distant_name(otmp, xname));
    
    if (level->locations[x][y].typ == STONE || level->locations[x][y].typ == SCORR)
	strcat(buf, " embedded in stone");
    else if (IS_WALL(level->locations[x][y].typ) || level->locations[x][y].typ == SDOOR)
	strcat(buf, " embedded in a wall");
    else if (closed_door(level, x,y))
	strcat(buf, " embedded in a door");
    else if (is_pool(level, x,y))
	strcat(buf, " in water");
    else if (is_lava(level, x,y))
	strcat(buf, " in molten lava");	/* [can this ever happen?] */
    
    if (!cansee(x, y))
	return -1; /* don't disclose the number of objects for location out of LOS */
    
    if (!otmp)
	/* There is no object here. Since the player sees one it must be a mimic */
	return 1;
    
    if (otmp->otyp != votyp)
	/* Hero sees something other than the actual top object. Probably a mimic */
	num_objs++;
    
    for ( ; otmp; otmp = otmp->nexthere)
	num_objs++;
    
    return num_objs;
}
예제 #4
0
/*
 * Try to find a dismount point adjacent to the steed's location.
 * If all else fails, try enexto().  Use enexto() as a last resort because
 * enexto() chooses its point randomly, possibly even outside the
 * room's walls, which is not what we want.
 * Adapted from mail daemon code.
 */
static boolean
landing_spot(coord * spot,      /* landing position (we fill it in) */
             int reason, int forceit)
{
    int i = 0, x, y, distance, min_distance = -1;
    boolean found = FALSE;
    struct trap *t;

    /* avoid known traps (i == 0) and boulders, but allow them as a backup */
    if (reason != DISMOUNT_BYCHOICE || Stunned || Confusion || Fumbling)
        i = 1;
    for (; !found && i < 2; ++i) {
        for (x = u.ux - 1; x <= u.ux + 1; x++)
            for (y = u.uy - 1; y <= u.uy + 1; y++) {
                if (!isok(x, y) || (x == u.ux && y == u.uy))
                    continue;

                if (ACCESSIBLE(level->locations[x][y].typ) &&
                    !MON_AT(level, x, y) && !closed_door(level, x, y)) {
                    distance = distu(x, y);
                    if (min_distance < 0 || distance < min_distance ||
                        (distance == min_distance && rn2(2))) {
                        if (i > 0 ||
                            (((t = t_at(level, x, y)) == 0 || !t->tseen) &&
                             (!sobj_at(BOULDER, level, x, y) ||
                              throws_rocks(youmonst.data)))) {
                            spot->x = x;
                            spot->y = y;
                            min_distance = distance;
                            found = TRUE;
                        }
                    }
                }
            }
    }

    /* If we didn't find a good spot and forceit is on, try enexto(). */
    if (forceit && min_distance < 0 &&
        !enexto(spot, level, u.ux, u.uy, youmonst.data))
        return FALSE;

    return found;
}
예제 #5
0
파일: pager.c 프로젝트: FredrIQ/nethack4
static int
describe_object(int x, int y, int votyp, char *buf, int known_embed,
                boolean *feature_described)
{
    int num_objs = 0;
    struct obj *otmp;
    int typ;

    *feature_described = FALSE;

    if (votyp == -1)
        return -1;

    otmp = vobj_at(x, y);

    if (!otmp || otmp->otyp != votyp) {
        /* We have a mimic. */
        if (votyp == STRANGE_OBJECT) {
            strcpy(buf, "strange object");
        } else {
            otmp = mktemp_sobj(level, votyp);
            otmp->corpsenm = PM_TENGU;
            /* (basic object only, no random features) */
            if (otmp->oclass == COIN_CLASS)
                otmp->quan = 1L;        /* to force pluralization off */
            else if (otmp->otyp == SLIME_MOLD)
                otmp->spe = gamestate.fruits.current;/* give the fruit a type */
            strcpy(buf, distant_name(otmp, xname));
            dealloc_obj(otmp);
            otmp = vobj_at(x, y);       /* make sure we don't point to the temp 
                                           obj any more */
        }
    } else
        strcpy(buf, distant_name(otmp, xname));

    typ = level->locations[x][y].typ;
    if (known_embed && IS_TREE(typ))
        strcat(buf, " stuck");
    else if (known_embed && (IS_ROCK(typ) || closed_door(level, x, y)))
        strcat(buf, " embedded");
    else if (IS_TREE(typ)) {
        strcat(buf, " stuck in a tree");
        *feature_described = TRUE;
    } else if (typ == STONE || typ == SCORR) {
        strcat(buf, " embedded in stone");
        *feature_described = TRUE;
    } else if (IS_WALL(typ) || typ == SDOOR) {
        strcat(buf, " embedded in a wall");
        *feature_described = TRUE;
    } else if (closed_door(level, x, y)) {
        strcat(buf, " embedded in a door");
        *feature_described = TRUE;
    } else if (is_pool(level, x, y)) {
        strcat(buf, " in water");
        *feature_described = TRUE;
    } else if (is_lava(level, x, y)) {
        strcat(buf, " in molten lava"); /* [can this ever happen?] */
        *feature_described = TRUE;
    }

    if (!cansee(x, y))
        return -1;      /* don't disclose the number of objects for location
                           out of LOS */

    if (!otmp)
        /* There is no object here. Since the player sees one it must be a
           mimic */
        return 1;

    if (otmp->otyp != votyp)
        /* Hero sees something other than the actual top object. Probably a
           mimic */
        num_objs++;

    for (; otmp; otmp = otmp->nexthere)
        num_objs++;

    return num_objs;
}
예제 #6
0
파일: mthrowu.c 프로젝트: FredrIQ/nhfourk
/* Is a monster willing to fire with a compass beam in a given direction?

   Returns TRUE and sets *mdef to a monster if there's a monster it wants to
   target (and no monster it doesn't want to attack). *mdef can be &youmonst.

   If mdef is non-NULL, also set the tbx/tby globals for backwards compatibility
   (clobbered on a *mdef == NULL return, appropriately otherwise). TODO: get rid
   of these, they're really spaghetti.

   Returns TRUE and sets *mdef to NULL if there's no reason it wants to fire
   in that direction, but no reason it dislikes firing in that direction
   either.

   Returns FALSE if the monster refuses to fire in that direction. In this
   case, *mdef will be a monster that we don't want to target (possibly
   &youmonst).
   
   helpful determines whether or not a beam gives a positive effect */
boolean
m_beam_ok(struct monst *magr, int dx, int dy, struct monst **mdef, boolean helpful)
{
    int x = magr->mx;
    int y = magr->my;
    int i;
    struct monst *mat;

    if (mdef)
        *mdef = NULL;

    for (i = 0; i < BOLT_LIM; i++) {
        x += dx;
        y += dy;

        /* Will the beam stop at this point? As usual, assume that monsters have
           perfect knowledge of the level layout. */
        if (!isok(x, y) || !ZAP_POS(level->locations[x][y].typ) ||
            closed_door(level, x, y))
            break;

        /* Will the monster think that the beam will hit the player? Does it
           care? Hostiles like to attack the player; peacefuls don't want to.
           Pets have perfect knowledge of their master's location even if they
           can't sense their master. Confused monsters can't aim a beam at the
           player (or to avoid the player); no monster can hit an engulfed
           player with a beam. */
        if ((x == magr->mux && y == magr->muy && msensem(magr, &youmonst)) ||
            (magr->mtame && x == u.ux && y == u.uy)) {
            if (!Engulfed && !magr->mconf) {
                if (mdef) {
                    *mdef = &youmonst;
                    tbx = x - magr->mx;
                    tby = y - magr->my;
                }

                if (!Conflict) {
                    if ((!helpful && magr->mpeaceful) ||
                        (helpful && !magr->mpeaceful))
                    return FALSE;
                }
            }
        }

        mat = m_at(level, x, y);

        /* special case: make sure we don't hit the quest leader with stray
           beams, as it can make the game unwinnable; do this regardless of LOS
           or hostility or Conflict or confusion or anything like that */
        if (mat && mat->data->msound == MS_LEADER && !helpful) {
            if (mdef)
                *mdef = mat;
            return FALSE;
        }

        /* Confused monsters aren't trying to target anything in particular,
           because they don't have full control of their actions. Monsters won't
           intentionally aim at or to avoid a monster they can't see (apart from
           the above MS_LEADER case). */
        if (mat && (msensem(magr, mat) & ~MSENSE_ITEMMIMIC) && !magr->mconf) {
            /* Note: the couldsee() here is an LOE check and has nothing to
               do with vision; it determines conflict radius */
            if (Conflict && !resist(magr, RING_CLASS, 0, 0) &&
                couldsee(magr->mx, magr->my) &&
                distu(magr->mx, magr->my) <= BOLT_LIM * BOLT_LIM) {
                /* we're conflicted, anything is a valid target */
                if (mdef) {
                    *mdef = mat;
                    tbx = x - magr->mx;
                    tby = y - magr->my;
                }
            } else if (mm_aggression(magr, mat) & ALLOW_M && !helpful) {
                /* we want to target this monster */
                if (mdef) {
                    *mdef = mat;
                    tbx = x - magr->mx;
                    tby = y - magr->my;
                }
            } else if (!helpful && (magr->mpeaceful || mat->mpeaceful)) {
                /* we don't want to attack this monster; peacefuls (including
                   pets) should avoid collateral damage; also handles the
                   pet_attacks_up_to_difficulty checks; symmetrised so that
                   hostiles won't attack pets who won't attack them */
                if (mdef)
                    *mdef = mat;
                return FALSE;
            } else if (helpful &&
                       ((magr->mpeaceful && mat->mpeaceful) ||
                        (!magr->mpeaceful && !mat->mpeaceful))) {
                /* helpful beam, hostile-hostile or peaceful/tame-peaceful/tame
                   is OK; the peaceful checks aren't redundant with above since
                   pet_attacks_up_to_difficulty is a thing */
                if (mdef) {
                    *mdef = mat;
                    tbx = x - magr->mx;
                    tby = y - magr->my;
                }
            }
        }
    }

    return TRUE;
}
예제 #7
0
파일: mthrowu.c 프로젝트: FredrIQ/nhfourk
void
m_throw(struct monst *mon, int x, int y, int dx, int dy, int range,
        struct obj *obj, boolean verbose)
{
    struct monst *mtmp;
    struct obj *singleobj;
    struct tmp_sym *tsym = 0;
    int hitu, blindinc = 0;

    bhitpos.x = x;
    bhitpos.y = y;

    if (obj->quan == 1L) {
        /*
         * Remove object from minvent.  This cannot be done later on;
         * what if the player dies before then, leaving the monster
         * with 0 daggers?  (This caused the infamous 2^32-1 orcish
         * dagger bug).
         *
         * VENOM is not in minvent - it should already be OBJ_FREE.
         * The extract below does nothing.
         */

        /* not possibly_unwield, which checks the object's */
        /* location, not its existence */
        if (MON_WEP(mon) == obj) {
            setmnotwielded(mon, obj);
            MON_NOWEP(mon);
        }
        obj_extract_self(obj);
        singleobj = obj;
        obj = NULL;
    } else {
        singleobj = splitobj(obj, 1L);
        obj_extract_self(singleobj);
    }

    singleobj->owornmask = 0;   /* threw one of multiple weapons in hand? */
    singleobj->olev = level;    /* object is on the same level as monster */

    if ((singleobj->cursed || singleobj->greased) && (dx || dy) && !rn2(7)) {
        if (canseemon(mon) && flags.verbose) {
            if (is_ammo(singleobj))
                pline("%s misfires!", Monnam(mon));
            else
                pline("%s as %s throws it!", Tobjnam(singleobj, "slip"),
                      mon_nam(mon));
        }
        dx = rn2(3) - 1;
        dy = rn2(3) - 1;
        /* check validity of new direction */
        if (!dx && !dy) {
            drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
            return;
        }
    }

    /* pre-check for doors, walls and boundaries. Also need to pre-check for
       bars regardless of direction; the random chance for small objects
       hitting bars is skipped when reaching them at point blank range */
    if (!isok(bhitpos.x + dx, bhitpos.y + dy)
        || IS_ROCK(level->locations[bhitpos.x + dx][bhitpos.y + dy].typ)
        || closed_door(level, bhitpos.x + dx, bhitpos.y + dy)
        || (level->locations[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS &&
            hits_bars(&singleobj, bhitpos.x, bhitpos.y, 0, 0))) {
        drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
        return;
    }

    /* Note: drop_throw may destroy singleobj.  Since obj must be destroyed
       early to avoid the dagger bug, anyone who modifies this code should be
       careful not to use either one after it's been freed. */
    tsym = tmpsym_initobj(singleobj);

    while (range-- > 0) {      /* Actually the loop is always exited by break */
        bhitpos.x += dx;
        bhitpos.y += dy;
        if ((mtmp = m_at(level, bhitpos.x, bhitpos.y)) != 0) {
            if (ohitmon(mtmp, singleobj, range, verbose))
                break;
        } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
            action_interrupted();

            if (singleobj->oclass == GEM_CLASS &&
                singleobj->otyp <= LAST_GEM + 9
                /* 9 glass colors */
                && is_unicorn(youmonst.data) && !u_helpless(hm_all)) {
                if (singleobj->otyp > LAST_GEM) {
                    pline("You catch the %s.", xname(singleobj));
                    pline("You are not interested in %s junk.",
                          s_suffix(mon_nam(mon)));
                    makeknown(singleobj->otyp);
                    dropy(singleobj);
                } else {
                    pline("You accept %s gift in the spirit in which it was "
                          "intended.", s_suffix(mon_nam(mon)));
                    hold_another_object(singleobj, "You catch, but drop, %s.",
                                        xname(singleobj), "You catch:");
                }
                break;
            }
            if (singleobj->oclass == POTION_CLASS) {
                if (!Blind)
                    singleobj->dknown = 1;
                potionhit(&youmonst, singleobj, FALSE);
                break;
            }
            switch (singleobj->otyp) {
                int dam, hitv;

            case EGG:
                if (!touch_petrifies(&mons[singleobj->corpsenm])) {
                    impossible("monster throwing egg type %d",
                               singleobj->corpsenm);
                    hitu = 0;
                    break;
                }
                /* fall through */
            case CREAM_PIE:
            case BLINDING_VENOM:
                hitu = thitu(8, 0, singleobj, NULL);
                break;
            default:
                dam = dmgval(singleobj, &youmonst);
                hitv = 3 - distmin(u.ux, u.uy, mon->mx, mon->my);
                if (hitv < -4)
                    hitv = -4;
                if (is_elf(mon->data) &&
                    objects[singleobj->otyp].oc_skill == P_BOW) {
                    hitv++;
                    if (MON_WEP(mon) && MON_WEP(mon)->otyp == ELVEN_BOW)
                        hitv++;
                    if (singleobj->otyp == ELVEN_ARROW)
                        dam++;
                }
                if (bigmonst(youmonst.data))
                    hitv++;
                hitv += 8 + singleobj->spe;
                if (dam < 1)
                    dam = 1;
                if (objects[singleobj->otyp].oc_class == WEAPON_CLASS ||
                    objects[singleobj->otyp].oc_class == VENOM_CLASS) {
                    hitv += objects[singleobj->otyp].oc_hitbon;
                }
                hitu = thitu(hitv, dam, singleobj, NULL);
            }
            if (hitu && singleobj->opoisoned && is_poisonable(singleobj)) {
                poisoned(xname(singleobj), A_STR,
                         killer_msg_obj(POISONING, singleobj), -10);
            }
            if (hitu &&
                can_blnd(NULL, &youmonst,
                         (uchar) (singleobj->otyp ==
                                  BLINDING_VENOM ? AT_SPIT : AT_WEAP),
                         singleobj)) {
                blindinc = rnd(25);
                if (singleobj->otyp == CREAM_PIE) {
                    if (!Blind)
                        pline("Yecch!  You've been creamed.");
                    else
                        pline("There's something sticky all over your %s.",
                              body_part(FACE));
                } else if (singleobj->otyp == BLINDING_VENOM) {
                    int num_eyes = eyecount(youmonst.data);

                    /* venom in the eyes */
                    if (!Blind)
                        pline("The venom blinds you.");
                    else
                        pline("Your %s sting%s.",
                              (num_eyes ==
                               1) ? body_part(EYE) : makeplural(body_part(EYE)),
                              (num_eyes == 1) ? "s" : "");
                }
            }
            if (hitu && singleobj->otyp == VAMPIRE_BLOOD) {
                if (!Drain_resistance) {
                    losexp("vampire blood", FALSE);
                }
            }
            if (hitu && singleobj->otyp == EGG) {
                if (touched_monster(singleobj->corpsenm))
                    Stoned = 5;
            }
            action_interrupted();
            if (hitu || !range) {
                drop_throw(singleobj, hitu, u.ux, u.uy);
                break;
            }
        } else if (!range       /* reached end of path */
                   /* missile hits edge of screen */
                   || !isok(bhitpos.x + dx, bhitpos.y + dy)
                   /* missile hits the wall */
                   || IS_ROCK(level->
                              locations[bhitpos.x + dx][bhitpos.y + dy].typ)
                   /* missile hit closed door */
                   || closed_door(level, bhitpos.x + dx, bhitpos.y + dy)
                   /* missile might hit iron bars */
                   || (level->locations[bhitpos.x + dx][bhitpos.y + dy].typ ==
                       IRONBARS &&
                       hits_bars(&singleobj, bhitpos.x, bhitpos.y, !rn2(5), 0))
                   /* Thrown objects "sink" */
                   || IS_SINK(level->locations[bhitpos.x][bhitpos.y].typ)) {
            if (singleobj)      /* hits_bars might have destroyed it */
                drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
            break;
        }
        tmpsym_at(tsym, bhitpos.x, bhitpos.y);
        win_delay_output();
    }
    tmpsym_at(tsym, bhitpos.x, bhitpos.y);
    win_delay_output();
    tmpsym_end(tsym);

    if (blindinc) {
        u.ucreamed += blindinc;
        make_blinded(Blinded + (long)blindinc, FALSE);
        if (!Blind)
            pline("Your vision quickly clears.");
        else if (flags.verbose)
            pline("Use the command #wipe to clean your %s.", body_part(FACE));
    }
}
예제 #8
0
파일: explode.c 프로젝트: FredrIQ/nhfourk
/* returns number of scattered objects */
long
scatter(int sx, int sy, /* location of objects to scatter */
        int blastforce, /* force behind the scattering */
        unsigned int scflags, struct obj *obj)
{   /* only scatter this obj */
    struct obj *otmp;
    struct level *lev = obj ? obj->olev : level;
    int tmp;
    int farthest = 0;
    uchar typ;
    long qtmp;
    boolean used_up;
    boolean individual_object = obj ? TRUE : FALSE;
    struct monst *mtmp;
    struct scatter_chain *stmp, *stmp2 = 0;
    struct scatter_chain *schain = NULL;
    long total = 0L;
    boolean visible = (lev == level && cansee(sx, sy));

    while ((otmp = individual_object ? obj : lev->objects[sx][sy]) != 0) {
        if (otmp->quan > 1L) {
            qtmp = otmp->quan - 1;
            if (qtmp > LARGEST_INT)
                qtmp = LARGEST_INT;
            qtmp = (long)rnd((int)qtmp);
            otmp = splitobj(otmp, qtmp);
        } else {
            obj = NULL; /* all used */
        }
        obj_extract_self(otmp);
        used_up = FALSE;

        /* 9 in 10 chance of fracturing boulders or statues */
        if ((scflags & MAY_FRACTURE)
                && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE))
                && rn2(10)) {
            if (otmp->otyp == BOULDER) {
                if (visible)
                    pline("%s apart.", Tobjnam(otmp, "break"));
                fracture_rock(otmp);
                place_object(otmp, lev, sx, sy);
                if ((otmp = sobj_at(BOULDER, lev, sx, sy)) != 0) {
                    /* another boulder here, restack it to the top */
                    obj_extract_self(otmp);
                    place_object(otmp, lev, sx, sy);
                }
            } else {
                struct trap *trap;

                if ((trap = t_at(lev, sx, sy)) && trap->ttyp == STATUE_TRAP)
                    deltrap(lev, trap);
                if (visible)
                    pline("%s.", Tobjnam(otmp, "crumble"));
                break_statue(otmp);
                place_object(otmp, lev, sx, sy); /* put fragments on floor */
            }
            used_up = TRUE;

            /* 1 in 10 chance of destruction of obj; glass, egg destruction */
        } else if ((scflags & MAY_DESTROY) &&
                   (!rn2(10) || (objects[otmp->otyp].oc_material == GLASS ||
                                 otmp->otyp == EGG))) {
            if (breaks(otmp, (xchar) sx, (xchar) sy))
                used_up = TRUE;
        }

        if (!used_up) {
            stmp = malloc(sizeof (struct scatter_chain));
            stmp->next = NULL;
            stmp->obj = otmp;
            stmp->ox = sx;
            stmp->oy = sy;
            tmp = rn2(8);       /* get the direction */
            stmp->dx = xdir[tmp];
            stmp->dy = ydir[tmp];
            tmp = blastforce - (otmp->owt / 40);
            if (tmp < 1)
                tmp = 1;
            stmp->range = rnd(tmp);     /* anywhere up to that determ. by wt */
            if (farthest < stmp->range)
                farthest = stmp->range;
            stmp->stopped = FALSE;
            if (!schain)
                schain = stmp;
            else
                stmp2->next = stmp;
            stmp2 = stmp;
        }
    }

    while (farthest-- > 0) {
        for (stmp = schain; stmp; stmp = stmp->next) {
            if ((stmp->range-- > 0) && (!stmp->stopped)) {
                bhitpos.x = stmp->ox + stmp->dx;
                bhitpos.y = stmp->oy + stmp->dy;
                typ = lev->locations[bhitpos.x][bhitpos.y].typ;
                if (!isok(bhitpos.x, bhitpos.y)) {
                    bhitpos.x -= stmp->dx;
                    bhitpos.y -= stmp->dy;
                    stmp->stopped = TRUE;
                } else if (!ZAP_POS(typ) ||
                           closed_door(lev, bhitpos.x, bhitpos.y)) {
                    bhitpos.x -= stmp->dx;
                    bhitpos.y -= stmp->dy;
                    stmp->stopped = TRUE;
                } else if ((mtmp = m_at(lev, bhitpos.x, bhitpos.y)) != 0) {
                    if (scflags & MAY_HITMON) {
                        stmp->range--;
                        if (ohitmon(mtmp, stmp->obj, 1, FALSE)) {
                            stmp->obj = NULL;
                            stmp->stopped = TRUE;
                        }
                    }
                } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
                    if (scflags & MAY_HITYOU) {
                        int hitvalu, hitu;

                        action_interrupted();

                        hitvalu = 8 + stmp->obj->spe;
                        if (bigmonst(youmonst.data))
                            hitvalu++;
                        hitu =
                            thitu(hitvalu, dmgval(stmp->obj, &youmonst),
                                  stmp->obj, NULL);
                        if (hitu)
                            stmp->range -= 3;
                    }
                } else {
                    if (scflags & VIS_EFFECTS) {
                        /* tmpsym_at(bhitpos.x, bhitpos.y); */
                        /* delay_output(); */
                    }
                }
                stmp->ox = bhitpos.x;
                stmp->oy = bhitpos.y;
            }
        }
    }
    for (stmp = schain; stmp; stmp = stmp2) {
        int x, y;

        stmp2 = stmp->next;
        x = stmp->ox;
        y = stmp->oy;
        if (stmp->obj) {
            if (x != sx || y != sy)
                total += stmp->obj->quan;
            place_object(stmp->obj, lev, x, y);
            stackobj(stmp->obj);
        }
        free(stmp);
        if (lev == level)
            newsym(x, y);
    }

    return total;
}