Esempio n. 1
0
int
dothrow()
{
	register struct obj *obj;

	if(check_capacity(NULL)) return(0);
	obj = getobj(toss_objs, "throw");
	/* it is also possible to throw food */
	/* (or jewels, or iron balls... ) */

	if(!obj || !getdir(NULL)) {       /* ask "in what direction?" */
		if (obj && obj->oclass == GOLD_CLASS) {
		    u.ugold += obj->quan;
		    flags.botl = 1;
		    dealloc_obj(obj);
		}
		return(0);
	}

	if(obj->oclass == GOLD_CLASS) return(throw_gold(obj));

	if(!canletgo(obj,"throw"))
		return(0);
	if (obj->oartifact == ART_MJOLLNIR && obj != uwep) {
		You("must be wielding %s in order to throw it.", xname(obj));
		return(0);
	}
	if ((obj->oartifact == ART_MJOLLNIR && ACURR(A_STR) != 125)
	   || (obj->otyp == BOULDER
#ifdef POLYSELF
					&& !throws_rocks(uasmon)
#endif
								)) {
		pline("It's too heavy.");
		return(1);
	}
	if(!u.dx && !u.dy && !u.dz) {
		You("cannot throw an object at yourself.");
		return(0);
	}
	u_wipe_engr(2);

	if(obj == uwep) {
	    if(welded(obj)) {
		weldmsg(obj, FALSE);
		return(1);
	    }
	    if(obj->quan > 1L)
		setuwep(splitobj(obj, 1L));
	    else {
		setuwep((struct obj *)0);
		if (uwep) return(1); /* unwielded, died, rewielded */
	    }
	}
	else if(obj->quan > 1L)
		(void) splitobj(obj, 1L);
	freeinv(obj);
	return(throwit(obj));
}
Esempio n. 2
0
/* 
 * Steal gold coins only.  Leprechauns don't care for lesser coins.
 */
void stealgold(struct monst *mtmp)
{
	struct obj *fgold = gold_at(level, u.ux, u.uy);
	struct obj *ygold;
	long tmp;

        /* skip lesser coins on the floor */        
        while (fgold && fgold->otyp != GOLD_PIECE) fgold = fgold->nexthere; 

        /* Do you have real gold? */
        ygold = findgold(invent);

	if (fgold && ( !ygold || fgold->quan > ygold->quan || !rn2(5))) {
            obj_extract_self(fgold);
	    add_to_minv(mtmp, fgold);
	    newsym(u.ux, u.uy);
	    pline("%s quickly snatches some gold from between your %s!",
		    Monnam(mtmp), makeplural(body_part(FOOT)));
	    if (!ygold || !rn2(5)) {
		if (!tele_restrict(mtmp)) rloc(level, mtmp, FALSE);
		monflee(mtmp, 0, FALSE, FALSE);
	    }
	} else if (ygold) {
            const int gold_price = objects[GOLD_PIECE].oc_cost;
	    tmp = (somegold(money_cnt(invent)) + gold_price - 1) / gold_price;
	    tmp = min(tmp, ygold->quan);
            if (tmp < ygold->quan) ygold = splitobj(ygold, tmp);
            freeinv(ygold);
            add_to_minv(mtmp, ygold);
	    pline("Your purse feels lighter.");
	    if (!tele_restrict(mtmp)) rloc(level, mtmp, FALSE);
	    monflee(mtmp, 0, FALSE, FALSE);
	    iflags.botl = 1;
	}
}
Esempio n. 3
0
int doeat(void)
{
    struct obj *otmp;
    int tmp;

    // Is there some food (probably a heavy corpse) here on the ground?
    if (!Levitation)
	for (otmp = _level->objects; otmp; otmp = otmp->nobj) {
	    if (otmp->ox == _u.ux && otmp->oy == _u.uy && otmp->olet == FOOD_SYM) {
		pline("There %s %s here; eat %s? [ny] ", (otmp->quan == 1) ? "is" : "are", doname(otmp), (otmp->quan == 1) ? "it" : "one");
		if (readchar() == 'y') {
		    if (otmp->quan != 1)
			(void) splitobj(otmp, 1);
		    freeobj(otmp);
		    otmp = addinv(otmp);
		    addtobill(otmp);
		    goto gotit;
		}
	    }
	}
    otmp = getobj("%", "eat");
    if (!otmp)
	return 0;
  gotit:
    if (otmp->otyp == TIN) {
	if (uwep) {
	    switch (uwep->otyp) {
		case CAN_OPENER:
		    tmp = 1;
		    break;
		case DAGGER:
		    tmp = 3;
		    break;
		case PICK_AXE:
		case AXE:
		    tmp = 6;
		    break;
		default:
		    goto no_opener;
	    }
	    pline("Using your %s you try to open the tin.", aobjnam(uwep, NULL));
	} else {
	  no_opener:
	    pline("It is not so easy to open this tin.");
	    if (Glib) {
		pline("The tin slips out of your hands.");
		if (otmp->quan > 1) {
		    struct obj *obj;

		    obj = splitobj(otmp, 1);
		    if (otmp == uwep)
			setuwep(obj);
		}
		dropx(otmp);
		return 1;
	    }
	    tmp = 10 + rn2(1 + 500 / ((int) (_u.ulevel + _u.ustr)));
	}
	tin.reqtime = tmp;
	tin.usedtime = 0;
	tin.tin = otmp;
	occupation = opentin;
	occtxt = "opening the tin";
	return 1;
    }
    const struct objclass* ftmp = &c_Objects[otmp->otyp];
    multi = -ftmp->oc_delay;
    if (otmp->otyp >= CORPSE && eatcorpse(otmp))
	goto eatx;
    if (!rn2(7) && otmp->otyp != FORTUNE_COOKIE) {
	pline("Blecch!  Rotten food!");
	if (!rn2(4)) {
	    pline("You feel rather light headed.");
	    Confusion += d(2, 4);
	} else if (!rn2(4) && !Blind) {
	    pline("Everything suddenly goes dark.");
	    Blind = d(2, 10);
	    seeoff(0);
	} else if (!rn2(3)) {
	    if (Blind)
		pline("The world spins and you slap against the floor.");
	    else
		pline("The world spins and goes dark.");
	    nomul(-rnd(10));
	    nomovemsg = "You are conscious again.";
	}
	lesshungry(ftmp->nutrition / 4);
    } else {
	if (_u.uhunger >= 1500) {
	    pline("You choke over your food.");
	    pline("You die...");
	    killer = ftmp->oc_name;
	    done("choked");
	}
	switch (otmp->otyp) {
	    case FOOD_RATION:
		if (_u.uhunger <= 200)
		    pline("That food really hit the spot!");
		else if (_u.uhunger <= 700)
		    pline("That satiated your stomach!");
		else {
		    pline("You're having a hard time getting all that food down.");
		    multi -= 2;
		}
		lesshungry(ftmp->nutrition);
		if (multi < 0)
		    nomovemsg = "You finished your meal.";
		break;
	    case TRIPE_RATION:
		pline("Yak - dog food!");
		more_experienced(1, 0);
		_wflags.botl = 1;
		if (rn2(2)) {
		    pline("You vomit.");
		    morehungry(20);
		    if (Sick) {
			Sick = 0;	// David Neves
			pline("What a relief!");
		    }
		} else
		    lesshungry(ftmp->nutrition);
		break;
	    default:
		if (otmp->otyp >= CORPSE)
		    pline("That %s tasted terrible!", ftmp->oc_name);
		else
		    pline("That %s was delicious!", ftmp->oc_name);
		lesshungry(ftmp->nutrition);
		if (otmp->otyp == DEAD_LIZARD && (Confusion > 2))
		    Confusion = 2;
		else if (otmp->otyp == FORTUNE_COOKIE) {
		    if (Blind) {
			pline("This cookie has a scrap of paper inside!");
			pline("What a pity, that you cannot read it!");
		    } else
			print_rumor();
		} else if (otmp->otyp == LUMP_OF_ROYAL_JELLY) {
		    // This stuff seems to be VERY healthy!
		    if (_u.ustrmax < 118)
			++_u.ustrmax;
		    if (_u.ustr < _u.ustrmax)
			++_u.ustr;
		    _u.uhp += rnd(20);
		    if (_u.uhp > _u.uhpmax) {
			if (!rn2(17))
			    ++_u.uhpmax;
			_u.uhp = _u.uhpmax;
		    }
		    heal_legs();
		}
		break;
	}
    }
  eatx:
    if (multi < 0 && !nomovemsg) {
	static char msgbuf[BUFSZ];
	sprintf(msgbuf, "You finished eating the %s.", ftmp->oc_name);
	nomovemsg = msgbuf;
    }
    useup(otmp);
    return 1;
}
Esempio n. 4
0
int
dothrow()
{
	struct obj *obj;
	struct monst *mon;
	int tmp;

	obj = getobj("#)", "throw");   /* it is also possible to throw food */
				       /* (or jewels, or iron balls ... ) */
	if(!obj || !getdir(1))	       /* ask "in what direction?" */
		return(0);
	if(obj->owornmask & (W_ARMOR | W_RING)){
		pline("You can't throw something you are wearing.");
		return(0);
	}

	u_wipe_engr(2);

	if(obj == uwep){
		if(obj->cursed){
			pline("Your weapon is welded to your hand.");
			return(1);
		}
		if(obj->quan > 1)
			setuwep(splitobj(obj, 1));
		else
			setuwep((struct obj *) 0);
	}
	else if(obj->quan > 1)
		(void) splitobj(obj, 1);
	freeinv(obj);
	if(u.uswallow) {
		mon = u.ustuck;
		bhitpos.x = mon->mx;
		bhitpos.y = mon->my;
	} else if(u.dz) {
	  if(u.dz < 0) {
	    pline("%s hits the ceiling, then falls back on top of your head.",
		Doname(obj));		/* note: obj->quan == 1 */
	    if(obj->olet == POTION_SYM)
		potionhit(&youmonst, obj);
	    else {
		if(uarmh) pline("Fortunately, you are wearing a helmet!");
		losehp(uarmh ? 1 : rnd((int)(obj->owt)), "falling object");
		dropy(obj);
	    }
	  } else {
	    pline("%s hits the floor.", Doname(obj));
	    if(obj->otyp == EXPENSIVE_CAMERA) {
		pline("It is shattered in a thousand pieces!");
		obfree(obj, Null(obj));
	    } else if(obj->otyp == EGG) {
		pline("\"Splash!\"");
		obfree(obj, Null(obj));
	    } else if(obj->olet == POTION_SYM) {
		pline("The flask breaks, and you smell a peculiar odor ...");
		potionbreathe(obj);
		obfree(obj, Null(obj));
	    } else {
		dropy(obj);
	    }
	  }
	  return(1);
	} else if(obj->otyp == BOOMERANG) {
		mon = boomhit(u.dx, u.dy);
		if(mon == &youmonst) {		/* the thing was caught */
			(void) addinv(obj);
			return(1);
		}
	} else {
		if(obj->otyp == PICK_AXE && shkcatch(obj))
		    return(1);

		mon = bhit(u.dx, u.dy, (obj->otyp == ICE_BOX) ? 1 :
			(!Punished || obj != uball) ? 8 : !u.ustuck ? 5 : 1,
			obj->olet, NULL, NULL, obj);
	}
	if(mon) {
		/* awake monster if sleeping */
		wakeup(mon);

		if(obj->olet == WEAPON_SYM) {
			tmp = -1+u.ulevel+mon->data->ac+abon();
			if(obj->otyp < ROCK) {
				if(!uwep ||
				    uwep->otyp != obj->otyp+(BOW-ARROW))
					tmp -= 4;
				else {
					tmp += uwep->spe;
				}
			} else
			if(obj->otyp == BOOMERANG) tmp += 4;
			tmp += obj->spe;
			if(u.uswallow || tmp >= rnd(20)) {
				if(hmon(mon,obj,1) == TRUE){
				  /* mon still alive */
#ifndef NOWORM
				  cutworm(mon,bhitpos.x,bhitpos.y,obj->otyp);
#endif /* NOWORM */
				} else mon = 0;
				/* weapons thrown disappear sometimes */
				if(obj->otyp < BOOMERANG && rn2(3)) {
					/* check bill; free */
					obfree(obj, (struct obj *) 0);
					return(1);
				}
			} else miss(objects[obj->otyp].oc_name, mon);
		} else if(obj->otyp == HEAVY_IRON_BALL) {
			tmp = -1+u.ulevel+mon->data->ac+abon();
			if(!Punished || obj != uball) tmp += 2;
			if(u.utrap) tmp -= 2;
			if(u.uswallow || tmp >= rnd(20)) {
				if(hmon(mon,obj,1) == FALSE)
					mon = 0;	/* he died */
			} else miss("iron ball", mon);
		} else if(obj->olet == POTION_SYM && u.ulevel > rn2(15)) {
			potionhit(mon, obj);
			return(1);
		} else {
			if(cansee(bhitpos.x,bhitpos.y))
				pline("You miss %s.",monnam(mon));
			else pline("You miss it.");
			if(obj->olet == FOOD_SYM && mon->data->mlet == 'd')
				if(tamedog(mon,obj)) return(1);
			if(obj->olet == GEM_SYM && mon->data->mlet == 'u' &&
				!mon->mtame){
			 if(obj->dknown && objects[obj->otyp].oc_name_known){
			  if(objects[obj->otyp].g_val > 0){
			    u.uluck += 5;
			    goto valuable;
			  } else {
			    pline("%s is not interested in your junk.",
				Monnam(mon));
			  }
			 } else { /* value unknown to @ */
			    u.uluck++;
			valuable:
			    if(u.uluck > LUCKMAX)	/* dan@ut-ngp */
				u.uluck = LUCKMAX;
			    pline("%s graciously accepts your gift.",
				Monnam(mon));
			    mpickobj(mon, obj);
			    rloc(mon);
			    return(1);
			 }
			}
		}
	}
		/* the code following might become part of dropy() */
	if(obj->otyp == CRYSKNIFE)
		obj->otyp = WORM_TOOTH;
	obj->ox = bhitpos.x;
	obj->oy = bhitpos.y;
	obj->nobj = fobj;
	fobj = obj;
	/* prevent him from throwing articles to the exit and escaping */
	/* subfrombill(obj); */
	stackobj(obj);
	if(Punished && obj == uball &&
		(bhitpos.x != u.ux || bhitpos.y != u.uy)){
		freeobj(uchain);
		unpobj(uchain);
		if(u.utrap){
			if(u.utraptype == TT_PIT)
				pline("The ball pulls you out of the pit!");
			else {
			    long side =
				rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
			    pline("The ball pulls you out of the bear trap.");
			    pline("Your %s leg is severely damaged.",
				(side == LEFT_SIDE) ? "left" : "right");
			    set_wounded_legs(side, 500+rn2(1000));
			    losehp(2, "thrown ball");
			}
			u.utrap = 0;
		}
		unsee();
		uchain->nobj = fobj;
		fobj = uchain;
		u.ux = uchain->ox = bhitpos.x - u.dx;
		u.uy = uchain->oy = bhitpos.y - u.dy;
		setsee();
		(void) inshop();
	}
	if(cansee(bhitpos.x, bhitpos.y)) prl(bhitpos.x,bhitpos.y);
	return(1);
}
Esempio n. 5
0
/* return 1 if action took 1 (or more) moves, 0 if error or aborted */
static int
doengrave_core(const struct nh_cmd_arg *arg, int auto_elbereth)
{
    boolean dengr = FALSE;      /* TRUE if we wipe out the current engraving */
    boolean doblind = FALSE;    /* TRUE if engraving blinds the player */
    boolean doknown = FALSE;    /* TRUE if we identify the stylus */
    boolean doknown_after = FALSE;      /* TRUE if we identify the stylus after
                                           successfully engraving. */
    boolean eow = FALSE;        /* TRUE if we are overwriting oep */
    boolean jello = FALSE;      /* TRUE if we are engraving in slime */
    boolean ptext = TRUE;       /* TRUE if we must prompt for engrave text */
    boolean teleengr = FALSE;   /* TRUE if we move the old engraving */
    boolean zapwand = FALSE;    /* TRUE if we remove a wand charge */
    xchar type = DUST;          /* Type of engraving made */
    const char *buf;            /* Buffer for final/poly engraving text */
    const char *ebuf;           /* Buffer for initial engraving text */
    const char *qbuf;           /* Buffer for query text */
    const char *post_engr_text; /* Text displayed after engraving prompt */
    const char *everb;          /* Present tense of engraving type */
    const char *eloc;           /* Where the engraving is (ie dust/floor/...) */
    const char *esp;            /* Iterator over ebuf; mostly handles spaces */
    char *sp;                   /* Ditto for mutable copies of ebuf */
    int len;                    /* # of nonspace chars of new engraving text */
    int maxelen;                /* Max allowable length of engraving text */
    int helpless_time;          /* Temporary for calculating helplessness */
    const char *helpless_endmsg;/* Temporary for helpless end message */
    struct engr *oep = engr_at(level, u.ux, u.uy);
    struct obj *otmp;
    int cramps = 0;             /* How much your hand is cramping up from writing */

    /* The current engraving */
    const char *writer;

    buf = "";
    ebuf = "";
    post_engr_text = "";
    maxelen = 255; /* same value as in 3.4.3 */

    if (is_demon(youmonst.data) || youmonst.data->mlet == S_VAMPIRE)
        type = ENGR_BLOOD;

    /* Can the adventurer engrave at all? */

    if (Engulfed) {
        if (is_animal(u.ustuck->data)) {
            pline("What would you write?  \"Jonah was here\"?");
            return 0;
        } else if (is_whirly(u.ustuck->data)) {
            pline("You can't reach the %s.", surface(u.ux, u.uy));
            return 0;
        } else
            jello = TRUE;
    } else if (is_lava(level, u.ux, u.uy)) {
        pline("You can't write on the lava!");
        return 0;
    } else if (Underwater) {
        pline("You can't write underwater!");
        return 0;
    } else if (is_pool(level, u.ux, u.uy) ||
               IS_FOUNTAIN(level->locations[u.ux][u.uy].typ)) {
        pline("You can't write on the water!");
        return 0;
    }
    if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) /* in bubble */ ) {
        pline("You can't write in thin air!");
        return 0;
    } else if (u.utrap && u.utraptype == TT_INFLOOR) {
        pline ("You can't write on the %s while embedded therein.",
               surface(u.ux, u.uy));
        return 0;
    } else if (u.utrap && u.utraptype == TT_ICEBLOCK) {
        pline("You cannot write on the %s while embedded in a block of %s.",
              surface(u.ux,u.uy),
              (level->locations[u.ux][u.uy].typ == ICE) ? "it" : "ice");
        return 0;
    } else if (!accessible(level, u.ux, u.uy)) {
        /* stone, tree, wall, secret corridor, pool, lava, bars */
        pline("You can't write here.");
        return 0;
    }
    if (cantwield(youmonst.data)) {
        pline("You can't even hold anything!");
        return 0;
    }
    if (check_capacity(NULL))
        return 0;

    /* One may write with finger, or weapon, or wand, or..., or... Edited by
       GAN 10/20/86 so as not to change weapon wielded. */

    otmp = getargobj(arg, styluses, "write with");
    if (!otmp)
        return 0;       /* otmp == &zeroobj if fingers */

    if (otmp == &zeroobj)
        writer = makeplural(body_part(FINGER));
    else
        writer = xname(otmp);

    /* There's no reason you should be able to write with a wand while both
       your hands are tied up. */
    if (!freehand() && otmp != uwep && !otmp->owornmask) {
        pline("You have no free %s to write with!", body_part(HAND));
        return 0;
    }

    if (jello) {
        pline("You tickle %s with your %s.", mon_nam(u.ustuck), writer);
        pline("Your message dissolves...");
        return 0;
    }
    if (otmp->oclass != WAND_CLASS && !can_reach_floor()) {
        pline("You can't reach the %s!", surface(u.ux, u.uy));
        return 0;
    }
    if (IS_ALTAR(level->locations[u.ux][u.uy].typ)) {
        pline("You make a motion towards the altar with your %s.", writer);
        altar_wrath(u.ux, u.uy);
        return 0;
    }
    if (IS_GRAVE(level->locations[u.ux][u.uy].typ)) {
        if (otmp == &zeroobj) { /* using only finger */
            pline("You would only make a small smudge on the %s.",
                  surface(u.ux, u.uy));
            return 0;
        } else if (!level->locations[u.ux][u.uy].disturbed) {
            pline("You disturb the undead!");
            level->locations[u.ux][u.uy].disturbed = 1;
            makemon(&mons[PM_GHOUL], level, u.ux, u.uy, NO_MM_FLAGS);
            return 1;
        }
    }

    /* SPFX for items */

    switch (otmp->oclass) {
    default:
    case AMULET_CLASS:
    case CHAIN_CLASS:
    case POTION_CLASS:
    case COIN_CLASS:
        break;

    case RING_CLASS:
        /* "diamond" rings and others should work */
    case GEM_CLASS:
        /* diamonds & other hard gems should work */
        if (objects[otmp->otyp].oc_tough) {
            type = ENGRAVE;
            break;
        }
        break;

    case ARMOR_CLASS:
        if (is_boots(otmp)) {
            type = DUST;
            break;
        }
        /* fall through */
        /* Objects too large to engrave with */
    case BALL_CLASS:
    case ROCK_CLASS:
        pline("You can't engrave with such a large object!");
        ptext = FALSE;
        break;

        /* Objects too silly to engrave with */
    case FOOD_CLASS:
    case SCROLL_CLASS:
    case SPBOOK_CLASS:
        pline("Your %s would get %s.", xname(otmp),
              is_ice(level, u.ux, u.uy) ? "all frosty" : "too dirty");
        ptext = FALSE;
        break;

    case RANDOM_CLASS: /* This should mean fingers */
        break;

        /* The charge is removed from the wand before prompting for the
           engraving text, because all kinds of setup decisions and
           pre-engraving messages are based upon knowing what type of engraving 
           the wand is going to do.  Also, the player will have potentially
           seen "You wrest .." message, and therefore will know they are using
           a charge. */
    case WAND_CLASS:
        if (zappable(otmp)) {
            check_unpaid(otmp);
            zapwand = TRUE;
            if (Levitation)
                ptext = FALSE;

            switch (otmp->otyp) {
                /* DUST wands */
            default:
                break;

                /* NODIR wands */
            case WAN_LIGHT:
            case WAN_SECRET_DOOR_DETECTION:
            case WAN_CREATE_MONSTER:
            case WAN_WISHING:
            case WAN_ENLIGHTENMENT:
                zapnodir(otmp);
                break;

                /* IMMEDIATE wands */
                /* If wand is "IMMEDIATE", remember to affect the previous
                   engraving even if turning to dust. */
            case WAN_STRIKING:
                post_engr_text =
                    "The wand unsuccessfully fights your attempt to write!";
                doknown_after = TRUE;
                break;
            case WAN_SLOW_MONSTER:
                if (!Blind) {
                    post_engr_text = msgprintf("The bugs on the %s slow down!",
                                               surface(u.ux, u.uy));
                    doknown_after = TRUE;
                }
                break;
            case WAN_SPEED_MONSTER:
                if (!Blind) {
                    post_engr_text = msgprintf("The bugs on the %s speed up!",
                                               surface(u.ux, u.uy));
                    doknown_after = TRUE;
                }
                break;
            case WAN_POLYMORPH:
                if (oep) {
                    if (!Blind) {
                        type = (xchar) 0;       /* random */
                        buf = random_engraving(rng_main);
                        doknown = TRUE;
                    }
                    dengr = TRUE;
                }
                break;
            case WAN_NOTHING:
            case WAN_UNDEAD_TURNING:
            case WAN_OPENING:
            case WAN_LOCKING:
            case WAN_PROBING:
                break;

                /* RAY wands */
            case WAN_MAGIC_MISSILE:
                ptext = TRUE;
                if (!Blind) {
                    post_engr_text = msgprintf(
                        "The %s is riddled by bullet holes!",
                        surface(u.ux, u.uy));
                    doknown_after = TRUE;
                }
                break;

                /* can't tell sleep from death - Eric Backus */
            case WAN_SLEEP:
            case WAN_DEATH:
                if (!Blind) {
                    post_engr_text = msgprintf(
                        "The bugs on the %s stop moving!", surface(u.ux, u.uy));
                }
                break;

            case WAN_COLD:
                if (!Blind) {
                    post_engr_text = "A few ice cubes drop from the wand.";
                    doknown_after = TRUE;
                }
                if (!oep || (oep->engr_type != BURN))
                    break;
            case WAN_CANCELLATION:
            case WAN_MAKE_INVISIBLE:
                if (oep && oep->engr_type != HEADSTONE) {
                    if (!Blind)
                        pline("The engraving on the %s vanishes!",
                              surface(u.ux, u.uy));
                    dengr = TRUE;
                }
                break;
            case WAN_TELEPORTATION:
                if (oep && oep->engr_type != HEADSTONE) {
                    if (!Blind)
                        pline("The engraving on the %s vanishes!",
                              surface(u.ux, u.uy));
                    teleengr = TRUE;
                }
                break;

                /* type = ENGRAVE wands */
            case WAN_DIGGING:
                ptext = TRUE;
                type = ENGRAVE;
                if (!objects[otmp->otyp].oc_name_known) {
                    if (flags.verbose)
                        pline("This %s is a wand of digging!", xname(otmp));
                    doknown = TRUE;
                }
                if (!Blind) {
                    post_engr_text =
                        IS_GRAVE(level->locations[u.ux][u.uy].typ) ?
                        "Chips fly out from the headstone." :
                        is_ice(level, u.ux, u.uy) ?
                        "Ice chips fly up from the ice surface!" :
                        (level->locations[u.ux][u.uy].typ == DRAWBRIDGE_DOWN) ?
                        "Splinters fly up from the bridge." :
                        "Gravel flies up from the floor.";
                }
                else
                    post_engr_text = "You hear drilling!";
                break;

                /* type = BURN wands */
            case WAN_FIRE:
                ptext = TRUE;
                type = BURN;
                if (!objects[otmp->otyp].oc_name_known) {
                    if (flags.verbose)
                        pline("This %s is a wand of fire!", xname(otmp));
                    doknown = TRUE;
                }
                post_engr_text = Blind ?
                    "You feel the wand heat up." :
                    "Flames fly from the wand.";
                break;
            case WAN_LIGHTNING:
                ptext = TRUE;
                type = BURN;
                if (!objects[otmp->otyp].oc_name_known) {
                    if (flags.verbose)
                        pline("This %s is a wand of lightning!", xname(otmp));
                    doknown = TRUE;
                }
                if (!Blind) {
                    post_engr_text = "Lightning arcs from the wand.";
                    doblind = TRUE;
                } else
                    post_engr_text = "You hear crackling!";
                break;
            
                /* type = MARK wands */
                /* type = ENGR_BLOOD wands */
            }
        } else /* end if zappable */ if (!can_reach_floor()) {
                pline("You can't reach the %s!", surface(u.ux, u.uy));
                /* If it's a wrestable wand, the player wasted a turn trying. */
                if (wrestable(otmp))
                    return 1;
                else
                    return 0;
            }
        break;
    
    case WEAPON_CLASS:
        if (is_blade(otmp)) {
            if ((int)otmp->spe > -3)
                type = ENGRAVE;
            else
                pline("Your %s too dull for engraving.", aobjnam(otmp, "are"));
        }
        break;

    case TOOL_CLASS:
        if (otmp == ublindf) {
            pline("That is a bit difficult to engrave with, don't you think?");
            return 0;
        }
        switch (otmp->otyp) {
        case MAGIC_MARKER:
            if (otmp->spe <= 0)
                pline("Your marker has dried out.");
            else
                type = MARK;
            break;
        case TOWEL:
            /* Can't really engrave with a towel */
            ptext = FALSE;
            if (oep)
                if ((oep->engr_type == DUST) || (oep->engr_type == ENGR_BLOOD)
                    || (oep->engr_type == MARK)) {
                    if (!Blind)
                        pline("You wipe out the message here.");
                    else
                        pline("Your %s %s %s.", xname(otmp),
                              otense(otmp, "get"),
                              is_ice(level, u.ux, u.uy) ? "frosty" : "dusty");
                    dengr = TRUE;
                } else
                    pline("Your %s can't wipe out this engraving.",
                          xname(otmp));
            else
                pline("Your %s %s %s.", xname(otmp), otense(otmp, "get"),
                      is_ice(level, u.ux, u.uy) ? "frosty" : "dusty");
            break;
        default:
            break;
        }
        break;

    case VENOM_CLASS:
    case ILLOBJ_CLASS:
        impossible("You're engraving with an illegal object!");
        break;
    }

    if (IS_GRAVE(level->locations[u.ux][u.uy].typ)) {
        if (type == ENGRAVE || type == 0)
            type = HEADSTONE;
        else {
            /* ensures the "cannot wipe out" case */
            type = DUST;
            dengr = FALSE;
            teleengr = FALSE;
            buf = "";
        }
    }

    /* End of implement setup */

    /* If engraving with wand, blow up the wand if cursed+unskilled */
    if (zapwand && !getwandlevel(&youmonst, otmp)) {
        backfire(otmp);
        exercise(A_STR, FALSE);
        return 1;
    }

    /* Identify stylus */
    if (doknown) {
        makeknown(otmp->otyp);
        more_experienced(0, 10);
    }

    if (teleengr) {
        rloc_engr(oep);
        oep = NULL;
    }

    if (dengr) {
        del_engr(oep, level);
        oep = NULL;
    }

    /* Something has changed the engraving here */
    if (*buf) {
        make_engr_at(level, u.ux, u.uy, buf, moves, type);
        pline("The engraving now reads: \"%s\".", buf);
        ptext = FALSE;
    }

    if (zapwand && (otmp->spe < 0)) {
        pline("%s %sturns to dust.", The(xname(otmp)),
              Blind ? "" : "glows violently, then ");
        if (!IS_GRAVE(level->locations[u.ux][u.uy].typ))
            pline("You are not going to get anywhere trying to write in the "
                  "%s with your dust.",
                  is_ice(level, u.ux, u.uy) ? "frost" : "dust");
        useup(otmp);
        ptext = FALSE;
    }

    if (!ptext) {       /* Early exit for some implements. */
        if (otmp->oclass == WAND_CLASS && !can_reach_floor())
            pline("You can't reach the %s!", surface(u.ux, u.uy));
        return 1;
    }

    /* Special effects should have deleted the current engraving (if possible)
       by now. */

    if (oep) {
        char c = 'n';

        /* Give player the choice to add to engraving. */

        if (type == HEADSTONE) {
            /* no choice, only append */
            c = 'y';
        } else if ((type == oep->engr_type) &&
                   (!Blind || (oep->engr_type == BURN) ||
                    (oep->engr_type == ENGR_LIGHTS) ||
                    (oep->engr_type == ENGRAVE))) {
            if (auto_elbereth)
                c = 'y';
            else
                c = yn_function("Do you want to add to the current engraving?",
                                ynqchars, 'y');
            if (c == 'q') {
                pline("Never mind.");
                return 0;
            }
        }

        if (c == 'n' || Blind) {

            if ((oep->engr_type == DUST) || (oep->engr_type == ENGR_BLOOD) ||
                (oep->engr_type == MARK)) {
                if (!Blind) {
                    pline("You wipe out the message that was %s here.",
                          ((oep->engr_type == DUST) ? "written in the dust" :
                           ((oep->engr_type == ENGR_BLOOD) ?
                            "scrawled in blood" : "written")));
                    del_engr(oep, level);
                    oep = NULL;
                } else
                    /* Don't delete engr until after we *know* we're engraving
                       */
                    eow = TRUE;
            } else if ((type == DUST) || (type == MARK) ||
                       (type == ENGR_BLOOD)) {
                pline("You cannot wipe out the message that is %s the %s here.",
                      oep->engr_type == BURN ? (is_ice(level, u.ux, u.uy) ?
                                                "melted into" : "burned into") :
                      "engraved in",
                      surface(u.ux, u.uy));
                return 1;
            } else if (oep->engr_type == ENGR_LIGHTS) {
                if (type == BURN) {
                    pline("The colored lights short out and go dark.");
                    eow = TRUE;
                } else {
                    pline("The colored lights distract you, and nothing "
                          "really gets written.");
                    return 1;
                }
            } else if ((type != oep->engr_type) || (c == 'n')) {
                if (!Blind || can_reach_floor())
                    pline("You will overwrite the current message.");
                eow = TRUE;
            }
        }
    }

    eloc = surface(u.ux, u.uy);
    switch (type) {
    default:
        everb = (oep &&
                 !eow ? "add to the weird writing on" : "write strangely on");
        break;
    case DUST:
        everb = (oep && !eow ? "add to the writing in" : "write in");
        eloc = is_ice(level, u.ux, u.uy) ? "frost" : "dust";
        break;
    case HEADSTONE:
        everb = (oep && !eow ? "add to the epitaph on" : "engrave on");
        break;
    case ENGRAVE:
        everb = (oep && !eow ? "add to the engraving in" : "engrave in");
        break;
    case BURN:
        everb = (oep && !eow ? (is_ice(level, u.ux, u.uy) ?
                                "add to the text melted into" :
                                "add to the text burned into") :
                 (is_ice(level, u.ux, u.uy) ? "melt into" : "burn into"));
        break;
    case MARK:
        everb = (oep && !eow ? "add to the graffiti on" : "scribble on");
        break;
    case ENGR_BLOOD:
        everb = (oep && !eow ? "add to the scrawl on" : "scrawl on");
        break;
    case ENGR_LIGHTS:
        /* This probably can't actually happen. */
        everb = (oep && !eow ? "add to the writing in" : "write in");
        eloc  = (oep && !eow ? "the colored lights"    : "colored lights");
        break;
    }

    /* Tell adventurer what is going on */
    if (otmp != &zeroobj)
        pline("You %s the %s with %s.", everb, eloc, doname(otmp));
    else
        pline("You %s the %s with your %s.", everb, eloc,
              makeplural(body_part(FINGER)));

    /* Prompt for engraving! */
    qbuf = msgprintf("What do you want to %s the %s here?", everb, eloc);
    if (auto_elbereth)
        ebuf = "Elbereth";
    else
        ebuf = getarglin(arg, qbuf);

    /* Count the actual # of chars engraved not including spaces */
    len = strlen(ebuf);
    for (esp = ebuf; *esp; esp++)
        if (isspace(*esp))
            len -= 1;

    if (len == 0 || strchr(ebuf, '\033')) {
        if (zapwand) {
            if (!Blind)
                pline("%s, then %s.", Tobjnam(otmp, "glow"),
                      otense(otmp, "fade"));
            return 1;
        } else {
            pline("Never mind.");
            if (otmp && otmp->oclass == WAND_CLASS && wrestable(otmp))
                return 1;       /* disallow zero turn wrest */
            else
                return 0;
        }
    }

    /* A single `x' is the traditional signature of an illiterate person */
    if (len != 1 || (!strchr(ebuf, 'x') && !strchr(ebuf, 'X')))
        break_conduct(conduct_illiterate);

    /* Degrade any existing text: */
    u_wipe_engr(rnd(3));

    /* Mix up the new text we are engraving if surface or state of mind is
       unsound. Note: this won't add or remove any spaces. */
    char ebuf_copy[strlen(ebuf) + 1];
    strcpy(ebuf_copy, ebuf);
    for (sp = ebuf_copy; *sp; sp++) {
        if (isspace(*sp))
            continue;
        /* NetHack Fourk balance adjustment:  writing Elbereth a whole lot of
         * times makes it harder to continue writing anything successfully.  */
        if (!rn2((((type == DUST || type == ENGR_BLOOD)) ? 150
                  : (type == ENGRAVE) ? 350 : 1500)
                 * u.ulevel / (1 + u.uconduct[conduct_elbereth])) ||
            (Blind && !rn2(11)) || (Confusion && !rn2(7)) ||
            (Stunned && !rn2(4)) || (Hallucination && !rn2(2))) {
            *sp = ' ' + rnd(96 - 2);    /* ASCII '!' thru '~' (excludes ' ' and 
                                           DEL) */
            cramps++;
        }
    }
    if ((Blind || Confusion || Hallucination || Stunned) && (cramps > 0))
        pline("You have difficulty writing in your present condition.");
    else if (cramps > 6)
        pline("Your entire %s is cramping up.  You simply cannot write any more right now.", body_part(ARM));
    else if (cramps > 4)
        pline("Your %s is cramping up very severely.", body_part(HAND));
    else if (cramps > 2)
        pline("Your %s is really cramping up.", body_part(HAND));
    else if (cramps > 1)
        pline("Your %s is cramping up.", body_part(HAND));
    else if (cramps > 0)
        pline("Your writing %s is beginning to cramp.", body_part(HAND));

    /* Previous engraving is overwritten */
    if (eow) {
        del_engr(oep, level);
        oep = NULL;
    }

    /* Figure out how long it took to engrave, and if player has engraved too
       much. */
    helpless_time = len / 10;
    helpless_endmsg = NULL;
    switch (type) {
    default:
        helpless_endmsg = "You finish your weird engraving.";
        break;
    case DUST:
        helpless_endmsg = "You finish writing in the dust.";
        break;
    case HEADSTONE:
    case ENGRAVE:
        if ((otmp->oclass == WEAPON_CLASS) &&
            ((otmp->otyp != ATHAME) || otmp->cursed)) {
            helpless_time = len;
            maxelen = ((otmp->spe + 3) * 2) + 1;
            /* -2 = 3, -1 = 5, 0 = 7, +1 = 9, +2 = 11 Note: this does not allow 
               a +0 anything (except an athame) to engrave "Elbereth" all at
               once.  However, you could now engrave "Elb", then "ere", then
               "th". */
            if (otmp->quan > 1L)
                otmp = splitobj(otmp, 1L);
            pline("Your %s dull.", aobjnam(otmp, "get"));
            if (otmp->unpaid) {
                struct monst *shkp = shop_keeper(level, *u.ushops);

                if (shkp) {
                    pline("You damage it, you pay for it!");
                    bill_dummy_object(otmp);
                }
            }
            if (len > maxelen) {
                helpless_time = maxelen;
                otmp->spe = -3;
            } else if (len > 1)
                otmp->spe -= len >> 1;
            else
                otmp->spe -= 1; /* Prevent infinite engraving */
        } else if ((otmp->oclass == RING_CLASS) || (otmp->oclass == GEM_CLASS))
Esempio n. 6
0
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));
    }
}
Esempio n. 7
0
/* 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;
}