/* * 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; }
/* 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)); }
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); }
/* 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))); }
/* 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; }
/* 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; }