fix #U1233 - controlled polymorph by hero with lycanthropy (trunk only)

<email deleted> reported a long list
of inconsistencies and suggestions.  This attempts to address the ones
about werecritters and vampires.

> Polymorphed player does not get werecreature changes. (intentional?)
> Player in were form does not turn into werehuman form, ever.  (Previous
> bugged behavior was that player turned into a plain human)
> Player afflicted with a were cannot polymorph into werecreature or
> werehuman form.

     The first guess is right; being polymorphed blocks lycanthropy state
changes.  The second is not a bug either; hero is either a <werecritter>
when in beast form or a <role> when in human form, never human werecritter
monster.  The last one feels more like a bug though; it happened because
all lycanthrope monster entries are marked NOPOLY.  This patch extends
an earlier post-3.4.3 change to allow player with polymorph control to
explicitly specify werecritter when in role form or human werecritter when
in beast form to toggle shape.  It also allows closely related monsters
to toggle from role to beast (ie, "giant rat" yields wererat).

> Vampire or Werecreature changing form may change sex.

     Now the three semi-controlled changes--becoming a dragon due to armor,
toggling were form, and vampire into various critters--are prevented from
having the 10% chance of sex change kick in.  For monsters, lycanthropy
didn't apply (sex never toggles) and vampire shifting is now covered but
turning into a dragon due to scales/mail remains susceptible to sex change.

     Also, post-3.4.3 code made polymorphing into a vampire enable the
#monster command but neglected to tell the player.
This commit is contained in:
nethack.rankin
2007-04-08 01:51:57 +00:00
parent b90a811a19
commit 3c58c3b235
5 changed files with 88 additions and 35 deletions
+3
View File
@@ -212,6 +212,9 @@ stone-to-flesh which activates shop-owned figurine entails shop charges
make giants be less likely to be randomly generated in Sokoban make giants be less likely to be randomly generated in Sokoban
bear traps dish out some damage on initial entrapment bear traps dish out some damage on initial entrapment
bear traps and webs are harmless to water elementals bear traps and webs are harmless to water elementals
hero with polymorph control and inflicted with lycanthropy can specify own
werecritter or human werecritter monster types as polymorph target
hero undergoing semi-controlled polymorph won't also undergo sex chance
Platform- and/or Interface-Specific Fixes Platform- and/or Interface-Specific Fixes
+1
View File
@@ -2498,6 +2498,7 @@ E void FDECL(skill_init, (const struct def_skill *));
E void FDECL(were_change, (struct monst *)); E void FDECL(were_change, (struct monst *));
E int FDECL(counter_were, (int)); E int FDECL(counter_were, (int));
E int FDECL(were_beastie, (int));
E void FDECL(new_were, (struct monst *)); E void FDECL(new_were, (struct monst *));
E int FDECL(were_summon, (struct permonst *,BOOLEAN_P,int *,char *)); E int FDECL(were_summon, (struct permonst *,BOOLEAN_P,int *,char *));
E void NDECL(you_were); E void NDECL(you_were);
+7
View File
@@ -2508,6 +2508,8 @@ decide_to_shapeshift(mon, shiftflags)
struct monst *mon; struct monst *mon;
int shiftflags; int shiftflags;
{ {
struct permonst *ptr;
unsigned was_female = mon->female;
boolean msg = FALSE; boolean msg = FALSE;
if ((shiftflags & SHIFT_MSG) || if ((shiftflags & SHIFT_MSG) ||
@@ -2527,6 +2529,10 @@ int shiftflags;
!rn2(6) && (mon->mhp > mon->mhpmax - ((mon->mhpmax / 10) + 1))) { !rn2(6) && (mon->mhp > mon->mhpmax - ((mon->mhpmax / 10) + 1))) {
(void) newcham(mon, (struct permonst *)0, FALSE, msg); (void) newcham(mon, (struct permonst *)0, FALSE, msg);
} }
/* override the 10% chance for sex change */
ptr = mon->data;
if (!is_male(ptr) && !is_female(ptr) && !is_neuter(ptr))
mon->female = was_female;
} }
int int
@@ -3042,6 +3048,7 @@ struct permonst *mdat;
if (!olfaction(youmonst.data)) return FALSE; if (!olfaction(youmonst.data)) return FALSE;
mndx = monsndx(mdat); mndx = monsndx(mdat);
switch (mndx) { switch (mndx) {
case PM_ROTHE:
case PM_MINOTAUR: case PM_MINOTAUR:
You("notice a bovine smell."); You("notice a bovine smell.");
msg_given = TRUE; msg_given = TRUE;
+49 -34
View File
@@ -1,4 +1,4 @@
/* SCCS Id: @(#)polyself.c 3.5 2007/03/19 */ /* SCCS Id: @(#)polyself.c 3.5 2007/04/07 */
/* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */
/* NetHack may be freely redistributed. See license for details. */ /* NetHack may be freely redistributed. See license for details. */
@@ -32,6 +32,10 @@ STATIC_DCL boolean FDECL(polysense,(struct permonst *));
STATIC_VAR const char no_longer_petrify_resistant[] = STATIC_VAR const char no_longer_petrify_resistant[] =
"No longer petrify-resistant, you"; "No longer petrify-resistant, you";
/* controls whether taking on new form or becoming new man can also
change sex (ought to be an arg to polymon() and newman() instead) */
STATIC_VAR int sex_change_ok = 0;
/* update the youmonst.data structure pointer */ /* update the youmonst.data structure pointer */
void void
set_uasmon() set_uasmon()
@@ -166,7 +170,7 @@ newman()
if (u.ulevelmax < newlvl) u.ulevelmax = newlvl; if (u.ulevelmax < newlvl) u.ulevelmax = newlvl;
u.ulevel = newlvl; u.ulevel = newlvl;
if (!rn2(10)) change_sex(); if (sex_change_ok && !rn2(10)) change_sex();
adjabil(oldlvl, (int)u.ulevel); adjabil(oldlvl, (int)u.ulevel);
reset_rndmonst(NON_PM); /* new monster generation criteria */ reset_rndmonst(NON_PM); /* new monster generation criteria */
@@ -251,11 +255,11 @@ polyself(psflags)
int psflags; int psflags;
{ {
char buf[BUFSZ]; char buf[BUFSZ];
int old_light, new_light, mntmp, tries; int old_light, new_light, mntmp, tryct;
boolean forcecontrol = (psflags == 1), boolean forcecontrol = (psflags == 1),
monsterpoly = (psflags == 2), monsterpoly = (psflags == 2),
draconian = (uarm && Is_dragon_armor(uarm)), draconian = (uarm && Is_dragon_armor(uarm)),
iswere = (u.ulycn >= LOW_PM || is_were(youmonst.data)), iswere = (u.ulycn >= LOW_PM),
isvamp = (youmonst.data->mlet == S_VAMPIRE); isvamp = (youmonst.data->mlet == S_VAMPIRE);
if (Unchanging) { if (Unchanging) {
@@ -274,16 +278,20 @@ int psflags;
old_light = emits_light(youmonst.data); old_light = emits_light(youmonst.data);
mntmp = NON_PM; mntmp = NON_PM;
if ((Polymorph_control || forcecontrol) && !monsterpoly) { if (monsterpoly && isvamp)
tries = 0; goto do_vampyr;
if (Polymorph_control || forcecontrol) {
tryct = 5;
do { do {
getlin("Become what kind of monster? [type the name]", getlin("Become what kind of monster? [type the name]",
buf); buf);
mntmp = name_to_mon(buf); mntmp = name_to_mon(buf);
if (mntmp < LOW_PM) if (mntmp < LOW_PM)
pline("I've never heard of such monsters."); pline("I've never heard of such monsters.");
else if (u.ulycn && (mntmp == u.ulycn || else if (iswere && (were_beastie(mntmp) == u.ulycn ||
mntmp == counter_were(u.ulycn))) mntmp == counter_were(u.ulycn) ||
(Upolyd && mntmp == PM_HUMAN)))
goto do_shift; goto do_shift;
/* Note: humans are illegal as monsters, but an /* Note: humans are illegal as monsters, but an
* illegal monster forces newman(), which is what we * illegal monster forces newman(), which is what we
@@ -295,12 +303,15 @@ int psflags;
mntmp == urole.femalenum)) mntmp == urole.femalenum))
You("cannot polymorph into that."); You("cannot polymorph into that.");
else break; else break;
} while(++tries < 5); } while (--tryct > 0);
if (tries==5) pline(thats_enough_tries); if (!tryct) pline(thats_enough_tries);
/* allow skin merging, even when polymorph is controlled */ /* allow skin merging, even when polymorph is controlled */
if (draconian && if (draconian && (!tryct ||
(mntmp == armor_to_dragon(uarm->otyp) || tries == 5)) mntmp == armor_to_dragon(uarm->otyp)))
goto do_merge; goto do_merge;
if (isvamp && (!tryct || mntmp == PM_WOLF ||
mntmp == PM_FOG_CLOUD || is_bat(&mons[mntmp])))
goto do_vampyr;
} else if (draconian || iswere || isvamp) { } else if (draconian || iswere || isvamp) {
/* special changes that don't require polyok() */ /* special changes that don't require polyok() */
if (draconian) { if (draconian) {
@@ -316,53 +327,53 @@ int psflags;
} }
} else if (iswere) { } else if (iswere) {
do_shift: do_shift:
if (is_were(youmonst.data)) if (Upolyd && were_beastie(mntmp) != u.ulycn)
mntmp = PM_HUMAN; /* Illegal; force newman() */ mntmp = PM_HUMAN; /* Illegal; force newman() */
else else
mntmp = u.ulycn; mntmp = u.ulycn;
} else { } else if (isvamp) {
if (youmonst.data->mlet == S_VAMPIRE) { do_vampyr:
if (mntmp < LOW_PM || (mons[mntmp].geno & G_UNIQ))
mntmp = (youmonst.data != &mons[PM_VAMPIRE] && mntmp = (youmonst.data != &mons[PM_VAMPIRE] &&
!rn2(10)) ? PM_WOLF : !rn2(10)) ? PM_WOLF :
!rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT; !rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT;
if (Polymorph_control) { if (Polymorph_control) {
char buf[BUFSZ]; Sprintf(buf, "Become %s?",
Sprintf(buf, "Become %s?", an(mons[mntmp].mname));
an(mons[mntmp].mname)); if (yn(buf) != 'y') return;
if (yn(buf) != 'y') return;
}
if (Unchanging) {
pline("You fail to transform!");
return;
}
} }
} }
/* if polymon fails, "you feel" message has been given /* if polymon fails, "you feel" message has been given
so don't follow up with another polymon or newman */ so don't follow up with another polymon or newman;
if (mntmp == PM_HUMAN) newman(); /* werecritter */ sex_change_ok left disabled here */
else (void) polymon(mntmp); if (mntmp == PM_HUMAN)
newman(); /* werecritter */
else
(void)polymon(mntmp);
goto made_change; /* maybe not, but this is right anyway */ goto made_change; /* maybe not, but this is right anyway */
} }
if (mntmp < LOW_PM) { if (mntmp < LOW_PM) {
tries = 0; tryct = 200;
do { do {
/* randomly pick an "ordinary" monster */ /* randomly pick an "ordinary" monster */
mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM); mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
if (polyok(&mons[mntmp]) && if (polyok(&mons[mntmp]) &&
!is_placeholder(&mons[mntmp])) break; !is_placeholder(&mons[mntmp])) break;
} while (++tries < 200); } while (--tryct > 0);
} }
/* The below polyok() fails either if everything is genocided, or if /* The below polyok() fails either if everything is genocided, or if
* we deliberately chose something illegal to force newman(). * we deliberately chose something illegal to force newman().
*/ */
sex_change_ok++;
if (!polyok(&mons[mntmp]) || if (!polyok(&mons[mntmp]) ||
(!forcecontrol && !rn2(5)) || your_race(&mons[mntmp])) { (!forcecontrol && !rn2(5)) || your_race(&mons[mntmp])) {
newman(); newman();
} else if (!polymon(mntmp)) { } else {
return; /* failed to change */ (void)polymon(mntmp);
} }
sex_change_ok--; /* reset */
made_change: made_change:
new_light = emits_light(youmonst.data); new_light = emits_light(youmonst.data);
@@ -425,7 +436,7 @@ int mntmp;
} else if (is_female(&mons[mntmp])) { } else if (is_female(&mons[mntmp])) {
if(!flags.female) dochange = TRUE; if(!flags.female) dochange = TRUE;
} else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) { } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) {
if(!rn2(10)) dochange = TRUE; if (sex_change_ok && !rn2(10)) dochange = TRUE;
} }
if (dochange) { if (dochange) {
flags.female = !flags.female; flags.female = !flags.female;
@@ -537,6 +548,7 @@ int mntmp;
if (flags.verbose) { if (flags.verbose) {
static const char use_thec[] = "Use the command #%s to %s."; static const char use_thec[] = "Use the command #%s to %s.";
static const char monsterc[] = "monster"; static const char monsterc[] = "monster";
if (can_breathe(youmonst.data)) if (can_breathe(youmonst.data))
pline(use_thec,monsterc,"use your breath weapon"); pline(use_thec,monsterc,"use your breath weapon");
if (attacktype(youmonst.data, AT_SPIT)) if (attacktype(youmonst.data, AT_SPIT))
@@ -559,6 +571,9 @@ int mntmp;
pline(use_thec,monsterc,"emit a mental blast"); pline(use_thec,monsterc,"emit a mental blast");
if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */ if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */
pline(use_thec,monsterc,"shriek"); pline(use_thec,monsterc,"shriek");
if (youmonst.data->mlet == S_VAMPIRE)
pline(use_thec,monsterc,"change shape");
if (lays_eggs(youmonst.data) && flags.female) if (lays_eggs(youmonst.data) && flags.female)
pline(use_thec,"sit","lay an egg"); pline(use_thec,"sit","lay an egg");
} }
+28 -1
View File
@@ -1,4 +1,4 @@
/* SCCS Id: @(#)were.c 3.5 2006/05/27 */ /* SCCS Id: @(#)were.c 3.5 2007/04/07 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */ /* NetHack may be freely redistributed. See license for details. */
@@ -48,6 +48,33 @@ int pm;
} }
} }
/* convert monsters similar to werecritters into appropriate werebeast */
int
were_beastie(pm)
int pm;
{
switch (pm) {
case PM_WERERAT:
case PM_SEWER_RAT:
case PM_GIANT_RAT:
case PM_RABID_RAT:
return PM_WERERAT;
case PM_WEREJACKAL:
case PM_JACKAL:
case PM_FOX:
case PM_COYOTE:
return PM_WEREJACKAL;
case PM_WEREWOLF:
case PM_WOLF:
case PM_WARG:
case PM_WINTER_WOLF:
return PM_WEREWOLF;
default:
break;
}
return NON_PM;
}
void void
new_were(mon) new_were(mon)
register struct monst *mon; register struct monst *mon;