Пример #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
/* Can this monster wear a saddle? */
boolean
can_saddle(struct monst *mtmp)
{
    const struct permonst *ptr = mtmp->data;

    return (strchr(steeds, ptr->mlet) && (ptr->msize >= MZ_MEDIUM) &&
            (!humanoid(ptr) || ptr->mlet == S_CENTAUR) && !amorphous(ptr) &&
            !noncorporeal(ptr) && !is_whirly(ptr) && !unsolid(ptr));
}
Пример #3
0
const char *
stagger(const struct permonst *ptr, const char *def)
{
    int capitalize = 2 + (*def == highc(*def));

    return (is_floater(ptr) ? levitate[capitalize]
            : (is_flyer(ptr) &&
               ptr->msize <= MZ_SMALL) ? flys[capitalize] :
            (is_flyer(ptr) && ptr->msize > MZ_SMALL) ?
            flyl[capitalize] : slithy(ptr) ? slither[capitalize] :
            amorphous(ptr) ? ooze[capitalize] : !ptr->mmove ?
            immobile[capitalize] : nolimbs(ptr) ? crawl[capitalize] : def);

}
Пример #4
0
/* true iff the type of monster pass through iron bars */
boolean
passes_bars(const struct permonst * mptr)
{
    return (boolean) (passes_walls(mptr) || amorphous(mptr) || is_whirly(mptr)
                      || verysmall(mptr) || (slithy(mptr) && !bigmonst(mptr)));
}
Пример #5
0
/* returns 1 if polymorph successful */
int polymon(int mntmp)
{
	boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow,
		was_blind = !!Blind, dochange = FALSE;
	boolean could_pass_walls = Passes_walls;
	int mlvl;

	if (mvitals[mntmp].mvflags & G_GENOD) {	/* allow G_EXTINCT */
		pline("You feel rather %s-ish.",mons[mntmp].mname);
		exercise(A_WIS, TRUE);
		return 0;
	}

	/* KMH, conduct */
	u.uconduct.polyselfs++;

	if (!Upolyd) {
		/* Human to monster; save human stats */
		u.macurr = u.acurr;
		u.mamax = u.amax;
		u.mfemale = flags.female;
	} else {
		/* Monster to monster; restore human stats, to be
		 * immediately changed to provide stats for the new monster
		 */
		u.acurr = u.macurr;
		u.amax = u.mamax;
		flags.female = u.mfemale;
	}

	if (youmonst.m_ap_type) {
	    /* stop mimicking immediately */
	    if (multi < 0) unmul("");
	} else if (mons[mntmp].mlet != S_MIMIC) {
	    /* as in polyman() */
	    youmonst.m_ap_type = M_AP_NOTHING;
	}
	if (is_male(&mons[mntmp])) {
		if (flags.female) dochange = TRUE;
	} else if (is_female(&mons[mntmp])) {
		if (!flags.female) dochange = TRUE;
	} else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) {
		if (!rn2(10)) dochange = TRUE;
	}
	if (dochange) {
		flags.female = !flags.female;
		pline("You %s %s%s!",
		    (u.umonnum != mntmp) ? "turn into a" : "feel like a new",
		    (is_male(&mons[mntmp]) || is_female(&mons[mntmp])) ? "" :
			flags.female ? "female " : "male ",
		    mons[mntmp].mname);
	} else {
		if (u.umonnum != mntmp)
			pline("You turn into %s!", an(mons[mntmp].mname));
		else
			pline("You feel like a new %s!", mons[mntmp].mname);
	}
	if (Stoned && poly_when_stoned(&mons[mntmp])) {
		/* poly_when_stoned already checked stone golem genocide */
		pline("You turn to stone!");
		mntmp = PM_STONE_GOLEM;
		Stoned = 0;
		delayed_killer = 0;
	}

	u.mtimedone = rn1(500, 500);
	u.umonnum = mntmp;
	set_uasmon();

	/* New stats for monster, to last only as long as polymorphed.
	 * Currently only strength gets changed.
	 */
	if (strongmonst(&mons[mntmp])) ABASE(A_STR) = AMAX(A_STR) = STR18(100);

	if (Stone_resistance && Stoned) { /* [email protected] */
		Stoned = 0;
		delayed_killer = 0;
		pline("You no longer seem to be petrifying.");
	}
	if (Sick_resistance && Sick) {
		make_sick(0L, NULL, FALSE, SICK_ALL);
		pline("You no longer feel sick.");
	}
	if (Slimed) {
	    if (flaming(youmonst.data)) {
		pline("The slime burns away!");
		Slimed = 0L;
		iflags.botl = 1;
	    } else if (mntmp == PM_GREEN_SLIME) {
		/* do it silently */
		Slimed = 0L;
		iflags.botl = 1;
	    }
	}
	if (nohands(youmonst.data)) Glib = 0;

	/*
	mlvl = adj_lev(&mons[mntmp]);
	 * We can't do the above, since there's no such thing as an
	 * "experience level of you as a monster" for a polymorphed character.
	 */
	mlvl = (int)mons[mntmp].mlevel;
	if (youmonst.data->mlet == S_DRAGON && mntmp >= PM_GRAY_DRAGON) {
		u.mhmax = In_endgame(&u.uz) ? (8*mlvl) : (4*mlvl + dice(mlvl,4));
	} else if (is_golem(youmonst.data)) {
		u.mhmax = golemhp(mntmp);
	} else {
		if (!mlvl) u.mhmax = rnd(4);
		else u.mhmax = dice(mlvl, 8);
		if (is_home_elemental(&u.uz, &mons[mntmp])) u.mhmax *= 3;
	}
	u.mh = u.mhmax;

	if (u.ulevel < mlvl) {
	/* Low level characters can't become high level monsters for long */
		u.mtimedone = u.mtimedone * u.ulevel / mlvl;
	}

	if (uskin && mntmp != armor_to_dragon(uskin->otyp))
		skinback(FALSE);
	break_armor();
	drop_weapon(1);
	if (hides_under(youmonst.data))
		u.uundetected = OBJ_AT(u.ux, u.uy);
	else if (youmonst.data->mlet == S_EEL)
		u.uundetected = is_pool(level, u.ux, u.uy);
	else
		u.uundetected = 0;

	if (u.utraptype == TT_PIT) {
	    if (could_pass_walls && !Passes_walls) {
		u.utrap = rn1(6,2);
	    } else if (!could_pass_walls && Passes_walls) {
		u.utrap = 0;
	    }
	}
	if (was_blind && !Blind) {	/* previous form was eyeless */
	    Blinded = 1L;
	    make_blinded(0L, TRUE);	/* remove blindness */
	}
	newsym(u.ux,u.uy);		/* Change symbol */

	if (!sticky && !u.uswallow && u.ustuck && sticks(youmonst.data)) u.ustuck = 0;
	else if (sticky && !sticks(youmonst.data)) uunstick();
	if (u.usteed) {
	    if (touch_petrifies(u.usteed->data) &&
	    		!Stone_resistance && rnl(3)) {
	    	char buf[BUFSZ];

	    	pline("No longer petrifying-resistant, you touch %s.",
	    			mon_nam(u.usteed));
	    	sprintf(buf, "riding %s", an(u.usteed->data->mname));
	    	instapetrify(buf);
 	    }
	    if (!can_ride(u.usteed)) dismount_steed(DISMOUNT_POLY);
	}

	if (flags.verbose) {
	    static const char use_thec[] = "Use the command #%s to %s.";
	    static const char monsterc[] = "monster";
	    if (can_breathe(youmonst.data))
		pline(use_thec,monsterc,"use your breath weapon");
	    if (attacktype(youmonst.data, AT_SPIT))
		pline(use_thec,monsterc,"spit venom");
	    if (youmonst.data->mlet == S_NYMPH)
		pline(use_thec,monsterc,"remove an iron ball");
	    if (attacktype(youmonst.data, AT_GAZE))
		pline(use_thec,monsterc,"gaze at monsters");
	    if (is_hider(youmonst.data))
		pline(use_thec,monsterc,"hide");
	    if (is_were(youmonst.data))
		pline(use_thec,monsterc,"summon help");
	    if (webmaker(youmonst.data))
		pline(use_thec,monsterc,"spin a web");
	    if (u.umonnum == PM_GREMLIN)
		pline(use_thec,monsterc,"multiply in a fountain");
	    if (is_unicorn(youmonst.data))
		pline(use_thec,monsterc,"use your horn");
	    if (is_mind_flayer(youmonst.data))
		pline(use_thec,monsterc,"emit a mental blast");
	    if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */
		pline(use_thec,monsterc,"shriek");
	    if (lays_eggs(youmonst.data) && flags.female)
		pline(use_thec,"sit","lay an egg");
	}
	/* you now know what an egg of your type looks like */
	if (lays_eggs(youmonst.data)) {
	    learn_egg_type(u.umonnum);
	    /* make queen bees recognize killer bee eggs */
	    learn_egg_type(egg_type_from_parent(u.umonnum, TRUE));
	}
	find_ac();
	if ((!Levitation && !u.ustuck && !Flying &&
	    (is_pool(level, u.ux,u.uy) || is_lava(level, u.ux,u.uy))) ||
	   (Underwater && !Swimming))
	    spoteffects(TRUE);
	if (Passes_walls && u.utrap && u.utraptype == TT_INFLOOR) {
	    u.utrap = 0;
	    pline("The rock seems to no longer trap you.");
	} else if (likes_lava(youmonst.data) && u.utrap && u.utraptype == TT_LAVA) {
	    u.utrap = 0;
	    pline("The lava now feels soothing.");
	}
	if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) {
	    if (Punished) {
		pline("You slip out of the iron chain.");
		unpunish();
	    }
	}
	if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP) &&
		(amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data) ||
		  (youmonst.data->msize <= MZ_SMALL && u.utraptype == TT_BEARTRAP))) {
	    pline("You are no longer stuck in the %s.",
		    u.utraptype == TT_WEB ? "web" : "bear trap");
	    /* probably should burn webs too if PM_FIRE_ELEMENTAL */
	    u.utrap = 0;
	}
	if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) {
	    pline("You orient yourself on the web.");
	    u.utrap = 0;
	}
	iflags.botl = 1;
	vision_full_recalc = 1;
	see_monsters();
	exercise(A_CON, FALSE);
	exercise(A_WIS, TRUE);
	encumber_msg();
	return 1;
}
Пример #6
0
/* the tsurugi of muramasa or vorpal blade hit someone */
static boolean artifact_hit_behead(struct monst *magr, struct monst *mdef,
			           struct obj *otmp, int *dmgptr, int dieroll)
{
    boolean youattack = (magr == &youmonst);
    boolean youdefend = (mdef == &youmonst);
    boolean vis = (!youattack && magr && cansee(magr->mx, magr->my))
	|| (!youdefend && cansee(mdef->mx, mdef->my))
	|| (youattack && u.uswallow && mdef == u.ustuck && !Blind);
    const char *wepdesc;
    char hittee[BUFSZ];

    strcpy(hittee, youdefend ? "you" : mon_nam(mdef));
    
    /* We really want "on a natural 20" but Nethack does it in reverse from AD&D. */
    if (otmp->oartifact == ART_TSURUGI_OF_MURAMASA && dieroll == 1) {
	wepdesc = "The razor-sharp blade";
	/* not really beheading, but so close, why add another SPFX */
	if (youattack && u.uswallow && mdef == u.ustuck) {
	    pline("You slice %s wide open!", mon_nam(mdef));
	    *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
	    return TRUE;
	}
	if (!youdefend) {
		/* allow normal cutworm() call to add extra damage */
		if (notonhead)
		    return FALSE;

		if (bigmonst(mdef->data)) {
			if (youattack)
				pline("You slice deeply into %s!",
					mon_nam(mdef));
			else if (vis)
				pline("%s cuts deeply into %s!",
					Monnam(magr), hittee);
			*dmgptr *= 2;
			return TRUE;
		}
		*dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
		pline("%s cuts %s in half!", wepdesc, mon_nam(mdef));
		otmp->dknown = TRUE;
		return TRUE;
	} else {
		if (bigmonst(youmonst.data)) {
			pline("%s cuts deeply into you!",
				magr ? Monnam(magr) : wepdesc);
			*dmgptr *= 2;
			return TRUE;
		}

		/* Players with negative AC's take less damage instead
		    * of just not getting hit.  We must add a large enough
		    * value to the damage so that this reduction in
		    * damage does not prevent death.
		    */
		*dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
		pline("%s cuts you in half!", wepdesc);
		otmp->dknown = TRUE;
		return TRUE;
	}
    } else if (otmp->oartifact == ART_VORPAL_BLADE &&
		(dieroll == 1 || mdef->data == &mons[PM_JABBERWOCK])) {
	static const char * const behead_msg[2] = {
		"%s beheads %s!",
		"%s decapitates %s!"
	};

	if (youattack && u.uswallow && mdef == u.ustuck)
		return FALSE;
	wepdesc = artilist[ART_VORPAL_BLADE].name;
	if (!youdefend) {
		if (!has_head(mdef->data) || notonhead || u.uswallow) {
			if (youattack)
				pline("Somehow, you miss %s wildly.",
					mon_nam(mdef));
			else if (vis)
				pline("Somehow, %s misses wildly.",
					mon_nam(magr));
			*dmgptr = 0;
			return (boolean)(youattack || vis);
		}
		if (noncorporeal(mdef->data) || amorphous(mdef->data)) {
			pline("%s slices through %s %s.", wepdesc,
				s_suffix(mon_nam(mdef)),
				mbodypart(mdef,NECK));
			return TRUE;
		}
		*dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
		pline(behead_msg[rn2(SIZE(behead_msg))],
			wepdesc, mon_nam(mdef));
		otmp->dknown = TRUE;
		return TRUE;
	} else {
		if (!has_head(youmonst.data)) {
			pline("Somehow, %s misses you wildly.",
				magr ? mon_nam(magr) : wepdesc);
			*dmgptr = 0;
			return TRUE;
		}
		if (noncorporeal(youmonst.data) || amorphous(youmonst.data)) {
			pline("%s slices through your %s.",
				wepdesc, body_part(NECK));
			return TRUE;
		}
		*dmgptr = 2 * (Upolyd ? u.mh : u.uhp)
			    + FATAL_DAMAGE_MODIFIER;
		pline(behead_msg[rn2(SIZE(behead_msg))],
			wepdesc, "you");
		otmp->dknown = TRUE;
		/* Should amulets fall off? */
		return TRUE;
	}
    }
    return FALSE;
}