E boolean FDECL(hideunder, (struct monst *));
E void FDECL(hide_monst, (struct monst *));
E void FDECL(mon_animal_list, (BOOLEAN_P));
+E boolean FDECL(valid_vampshiftform, (int, int));
E boolean FDECL(validvamp, (struct monst *, int *, int));
E int FDECL(select_newcham_form, (struct monst *));
E void FDECL(mgender_from_permonst, (struct monst *, struct permonst *));
E char *FDECL(short_oname,
(struct obj *, char *(*)(OBJ_P), char *(*)(OBJ_P), unsigned));
E const char *FDECL(singular, (struct obj *, char *(*)(OBJ_P)));
+E char *FDECL(just_an, (char *, const char *));
E char *FDECL(an, (const char *));
E char *FDECL(An, (const char *));
E char *FDECL(The, (const char *));
#define is_vampshifter(mon) \
((mon)->cham == PM_VAMPIRE || (mon)->cham == PM_VAMPIRE_LORD \
|| (mon)->cham == PM_VLAD_THE_IMPALER)
+#define vampshifted(mon) (is_vampshifter((mon)) && !is_vampire((mon)->data))
/* mimic appearances that block vision/light */
#define is_lightblocker_mappear(mon) \
xchar skill_record[P_SKILL_LIMIT]; /* skill advancements */
struct skills weapon_skills[P_NUM_SKILLS];
boolean twoweap; /* KMH -- Using two-weapon combat */
+ short mcham; /* vampire mndx if shapeshifted to bat/cloud */
}; /* end of `struct you' */
if (obj->cursed && !rn2(2)) {
if (!Blind)
pline_The("%s fogs up and doesn't reflect!", mirror);
+ else
+ pline("Nothing seems to happen.");
return 1;
}
if (!u.dx && !u.dy && !u.dz) {
}
g.nomovemsg = 0; /* default, "you can move again" */
}
- } else if (g.youmonst.data->mlet == S_VAMPIRE)
+ } else if (is_vampire(g.youmonst.data)
+ || is_vampshifter(&g.youmonst)) {
You("don't have a reflection.");
- else if (u.umonnum == PM_UMBER_HULK) {
+ } else if (u.umonnum == PM_UMBER_HULK) {
pline("Huh? That doesn't look like you!");
make_confused(HConfusion + d(3, 4), FALSE);
- } else if (Hallucination)
+ } else if (Hallucination) {
You(look_str, hcolor((char *) 0));
- else if (Sick)
+ } else if (Sick) {
You(look_str, "peaked");
- else if (u.uhs >= WEAK)
+ } else if (u.uhs >= WEAK) {
You(look_str, "undernourished");
- else
+ } else if (Upolyd) {
+ You("look like %s.", an(mons[u.umonnum].mname));
+ } else {
You("look as %s as ever.", uvisage);
+ }
}
return 1;
}
/* couldsee(mtmp->mx, mtmp->my) is implied by the fact that bhit()
targetted it, so we can ignore possibility of X-ray vision */
vis = canseemon(mtmp);
-/* ways to directly see monster (excludes X-ray vision, telepathy,
- extended detection, type-specific warning) */
+ /* ways to directly see monster (excludes X-ray vision, telepathy,
+ extended detection, type-specific warning) */
#define SEENMON (MONSEEN_NORMAL | MONSEEN_SEEINVIS | MONSEEN_INFRAVIS)
how_seen = vis ? howmonseen(mtmp) : 0;
/* whether monster is able to use its vision-based capabilities */
if (vis)
You("discern no obvious reaction from %s.", mon_nam(mtmp));
else
- You_feel("a bit silly gesturing the mirror in that direction.");
+ You_feel(
+ "a bit silly gesturing the mirror in that direction.");
do_react = FALSE;
}
if (do_react) {
pline("Unfortunately sound does not carry well through rock.");
else
aggravate();
- } else if (g.youmonst.data->mlet == S_VAMPIRE)
+ } else if (is_vampire(g.youmonst.data) || is_vampshifter(&g.youmonst)) {
return dopoly();
- else if (Upolyd)
+ } else if (Upolyd) {
pline("Any special ability you may have is purely reflexive.");
- else
+ } else {
You("don't have a special ability in your normal form!");
+ }
return 0;
}
(with countdown timer appended for wizard mode); we really want
the player to know he's not a samurai at the moment... */
if (Upolyd) {
+ char anbuf[20]; /* includes trailing space; [4] suffices */
struct permonst *uasmon = g.youmonst.data;
+ boolean altphrasing = vampshifted(&g.youmonst);
tmpbuf[0] = '\0';
/* here we always use current gender, not saved role gender */
if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon))
Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj);
- Sprintf(buf, "%sin %s%s form", !final ? "currently " : "", tmpbuf,
- uasmon->mname);
+ if (altphrasing)
+ Sprintf(eos(tmpbuf), "%s in ", mons[g.youmonst.cham].mname);
+ Sprintf(buf, "%s%s%s%s form", !final ? "currently " : "",
+ altphrasing ? just_an(anbuf, tmpbuf) : "in ",
+ tmpbuf, uasmon->mname);
you_are(buf, "");
}
&& !(final == ENL_GAMEOVERDEAD
&& u.umonnum == PM_GREEN_SLIME && !Unchanging)) {
/* foreign shape (except were-form which is handled below) */
- Sprintf(buf, "polymorphed into %s", an(g.youmonst.data->mname));
+ if (!vampshifted(&g.youmonst))
+ Sprintf(buf, "polymorphed into %s", an(g.youmonst.data->mname));
+ else
+ Sprintf(buf, "polymorphed into %s in %s form",
+ an(mons[g.youmonst.cham].mname), g.youmonst.data->mname);
if (wizard)
Sprintf(eos(buf), " (%d)", u.mtimedone);
you_are(buf, "");
maxelen = BUFSZ - 1;
if (oep)
oetype = oep->engr_type;
- if (is_demon(g.youmonst.data) || g.youmonst.data->mlet == S_VAMPIRE)
+ if (is_demon(g.youmonst.data) || is_vampire(g.youmonst.data))
type = ENGR_BLOOD;
/* Can the adventurer engrave at all? */
return TRUE; /* potential new form is ok */
}
-/* prevent wizard mode user from specifying invalid vampshifter shape */
+/* used for hero polyself handling */
+boolean
+valid_vampshiftform(base, form)
+int base, form;
+{
+ if (base >= LOW_PM && is_vampire(&mons[base])) {
+ if (form == PM_VAMPIRE_BAT || form == PM_FOG_CLOUD
+ || (form == PM_WOLF && base != PM_VAMPIRE))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* prevent wizard mode user from specifying invalid vampshifter shape
+ when using monpolycontrol to assign a new form to a vampshifter */
boolean
validvamp(mon, mndx_p, monclass)
struct monst *mon;
STATIC_DCL char *FDECL(minimal_xname, (struct obj *));
STATIC_DCL void FDECL(add_erosion_words, (struct obj *, char *));
STATIC_DCL char *FDECL(doname_base, (struct obj *obj, unsigned));
-STATIC_DCL char *FDECL(just_an, (char *str, const char *));
STATIC_DCL boolean FDECL(singplur_lookup, (char *, char *, BOOLEAN_P,
const char *const *));
STATIC_DCL char *FDECL(singplur_compound, (char *));
}
/* pick "", "a ", or "an " as article for 'str'; used by an() and doname() */
-STATIC_OVL char *
+char *
just_an(outbuf, str)
char *outbuf;
const char *str;
set_uasmon()
{
struct permonst *mdat = &mons[u.umonnum];
+ boolean was_vampshifter = valid_vampshiftform(g.youmonst.cham, u.umonnum);
set_mon_data(&g.youmonst, mdat);
+ if (Protection_from_shape_changers)
+ g.youmonst.cham = NON_PM;
+ else if (is_vampire(g.youmonst.data))
+ g.youmonst.cham = g.youmonst.mnum;
+ /* assume hero-as-chameleon/doppelganger/sandestin doesn't change shape */
+ else if (!was_vampshifter)
+ g.youmonst.cham = NON_PM;
+ u.mcham = g.youmonst.cham; /* for save/restore since youmonst isn't */
+
#define PROPSET(PropIndx, ON) \
do { \
if (ON) \
if (u.uhp <= 0)
u.uhp = 1;
} else {
- dead: /* we come directly here if their experience level went to 0 or
- less */
+ dead: /* we come directly here if experience level went to 0 or less */
Your("new form doesn't seem healthy enough to survive.");
g.killer.format = KILLED_BY_AN;
Strcpy(g.killer.name, "unsuccessful polymorph");
{
char buf[BUFSZ] = DUMMY;
int old_light, new_light, mntmp, class, tryct;
- boolean forcecontrol = (psflags == 1), monsterpoly = (psflags == 2),
+ boolean forcecontrol = (psflags == 1),
+ monsterpoly = (psflags == 2),
+ formrevert = (psflags == 3),
draconian = (uarm && Is_dragon_armor(uarm)),
- iswere = (u.ulycn >= LOW_PM), isvamp = is_vampire(g.youmonst.data),
+ iswere = (u.ulycn >= LOW_PM),
+ isvamp = (is_vampire(g.youmonst.data)
+ || is_vampshifter(&g.youmonst)),
controllable_poly = Polymorph_control && !(Stunned || Unaware);
if (Unchanging) {
old_light = emits_light(g.youmonst.data);
mntmp = NON_PM;
+ if (formrevert){
+ mntmp = g.youmonst.cham;
+ monsterpoly = TRUE;
+ controllable_poly = FALSE;
+ }
if (monsterpoly && isvamp)
goto do_vampyr;
class = 0;
mntmp = name_to_mon(buf);
if (mntmp < LOW_PM) {
- by_class:
+ by_class:
class = name_to_monclass(buf, &mntmp);
if (class && mntmp == NON_PM)
mntmp = mkclass_poly(class);
} else if (draconian || iswere || isvamp) {
/* special changes that don't require polyok() */
if (draconian) {
- do_merge:
+ do_merge:
mntmp = armor_to_dragon(uarm->otyp);
if (!(g.mvitals[mntmp].mvflags & G_GENOD)) {
/* allow G_EXTINCT */
update_inventory();
}
} else if (iswere) {
- do_shift:
+ do_shift:
if (Upolyd && were_beastie(mntmp) != u.ulycn)
mntmp = PM_HUMAN; /* Illegal; force newman() */
else
mntmp = u.ulycn;
} else if (isvamp) {
- do_vampyr:
- if (mntmp < LOW_PM || (mons[mntmp].geno & G_UNIQ))
- mntmp = (g.youmonst.data != &mons[PM_VAMPIRE] && !rn2(10))
+ do_vampyr:
+ if (mntmp < LOW_PM || (mons[mntmp].geno & G_UNIQ)) {
+ mntmp = (g.youmonst.data == &mons[PM_VAMPIRE_LORD] && !rn2(10))
? PM_WOLF
: !rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT;
+ if (g.youmonst.cham >= LOW_PM
+ && !is_vampire(g.youmonst.data) && !rn2(2))
+ mntmp = g.youmonst.cham;
+ }
if (controllable_poly) {
Sprintf(buf, "Become %s?", an(mons[mntmp].mname));
if (yn(buf) != 'y')
}
g.sex_change_ok--; /* reset */
-made_change:
+ made_change:
new_light = emits_light(g.youmonst.data);
if (old_light != new_light) {
if (old_light)
pline(use_thec, monsterc, "emit a mental blast");
if (g.youmonst.data->msound == MS_SHRIEK) /* worthless, actually */
pline(use_thec, monsterc, "shriek");
- if (is_vampire(g.youmonst.data))
+ if (is_vampire(g.youmonst.data) || is_vampshifter(&g.youmonst))
pline(use_thec, monsterc, "change shape");
if (lays_eggs(g.youmonst.data) && flags.female &&
}
}
+/* return to original form, usually either due to polymorph timing out
+ or dying from loss of hit points while being polymorphed */
void
rehumanize()
{
}
}
+ /*
+ * Right now, dying while being a shifted vampire (bat, cloud, wolf)
+ * reverts to human rather than to vampire.
+ */
+
if (emits_light(g.youmonst.data))
del_light_source(LS_MONSTER, monst_to_any(&g.youmonst));
polyman("return to %s form!", g.urace.adj);
{
struct permonst *savedat = g.youmonst.data;
- if (is_vampire(g.youmonst.data)) {
+ if (is_vampire(g.youmonst.data) || is_vampshifter(&g.youmonst)) {
polyself(2);
if (savedat != g.youmonst.data) {
You("transform into %s.", an(g.youmonst.data->mname));
return (u.uconduct.gnostic == 1);
}
if ((u.ualign.type != A_CHAOTIC
- && (is_demon(g.youmonst.data) || is_undead(g.youmonst.data)))
+ && (is_demon(g.youmonst.data)
+ || is_undead(g.youmonst.data) || is_vampshifter(&g.youmonst)))
|| u.ugangr > 6) { /* "Die, mortal!" */
pline("For some reason, %s seems to ignore you.", Gname);
aggravate();
kill_genocided_monsters();
update_inventory(); /* eggs & tins */
pline("Wiped out all %s.", nam);
+ if (Upolyd && vampshifted(&g.youmonst)
+ /* current shifted form or base vampire form */
+ && (i == u.umonnum || i == g.youmonst.cham))
+ polyself(3); /* vampshifter back to vampire */
if (Upolyd && i == u.umonnum) {
u.mh = -1;
if (Unchanging) {
continue;
}
ptr = &mons[mndx];
+ /* first revert if current shifted form or base vampire form */
+ if (Upolyd && vampshifted(&g.youmonst)
+ && (mndx == u.umonnum || mndx == g.youmonst.cham))
+ polyself(3); /* vampshifter (bat, &c) back to vampire */
/* Although "genus" is Latin for race, the hero benefits
* from both race and role; thus genocide affects either.
*/
amii_setpens(amii_numcolors); /* use colors from save file */
#endif
mread(fd, (genericptr_t) &u, sizeof(struct you));
+ g.youmonst.cham = u.mcham;
#define ReadTimebuf(foo) \
mread(fd, (genericptr_t) timebuf, 14); \
sysflags = newgamesysflags;
#endif
g.context = newgamecontext;
+ g.youmonst = cg.zeromonst;
return FALSE;
}
/* in case hangup save occurred in midst of level change */