monpolycontrol tweak (trunk only)
I) When testing the shopkeeper polymorph fix a couple of days ago, I
tried to pick a form without a head (which is now rejected for shk) by
specifying "trapper" via #monpolycontrol. It worked, in the sense that
the shopkeeper didn't take that form, but it also retried with "lurker
above". After trapper was rejected, it matched "trapper or lurker above"
for monster class and picked at random from that class (and in this case,
the second try was rejected for being headless too). The code I added
(a couple of years ago?) to prevent picking invalid forms for vampire
shapeshifting was a bit sloppy with its retry attempts.
II) I also noticed that monpolycontrol would let you pick forms that
newcham subsequently rejected, like placeholder monster "giant", so that
select_newcham_form got called again, resulting in a new prompt from
monpolycontrol to choose a form without seeing any explanation why the
first try was ignored. Now you'll get the same "it can't become that"
feedback and immediate reprompt as for an invalid monster, instead of
returning newcham a form that it won't accept.
III) The change in shopkeeper polymorph was rejecting forms without hands.
That was way too restrictive; the notake() filter is sufficient.
This is all modification of post-3.4.3 stuff, so no fixes entry.
[Not 100% true. newcham() doesn't explicitly reject humans anymore,
relying on polyok() test instead. Kops are the only humans who pass it.
If they're not supposed too, they need M2_NOPOLY in their definition.]
This commit is contained in:
94
src/mon.c
94
src/mon.c
@@ -27,6 +27,7 @@ STATIC_DCL boolean FDECL(validspecmon, (struct monst *,int));
|
||||
#ifdef WIZARD
|
||||
STATIC_DCL boolean FDECL(validvamp, (struct monst *,int *,int));
|
||||
#endif
|
||||
STATIC_DCL struct permonst *FDECL(accept_newcham_form, (int));
|
||||
|
||||
#ifdef REINCARNATION
|
||||
#define LEVEL_SPECIFIC_NOCORPSE(mdat) \
|
||||
@@ -2602,18 +2603,24 @@ validspecmon(mon, mndx)
|
||||
struct monst *mon;
|
||||
int mndx;
|
||||
{
|
||||
if (isspecmon(mon) && mndx >= LOW_PM) {
|
||||
if (mndx == NON_PM) return TRUE; /* caller wants random */
|
||||
|
||||
if (!accept_newcham_form(mndx)) return FALSE; /* geno'd or !polyok */
|
||||
|
||||
if (isspecmon(mon)) {
|
||||
struct permonst *ptr = &mons[mndx];
|
||||
|
||||
if (notake(ptr) || nohands(ptr) || !has_head(ptr)) return FALSE;
|
||||
/* reject notake because object manipulation is expected
|
||||
and nohead because speech capability is expected */
|
||||
if (notake(ptr) || !has_head(ptr)) return FALSE;
|
||||
/* [should we check ptr->msound here too?] */
|
||||
}
|
||||
return TRUE; /* potential new form is ok (or still NON_PM) */
|
||||
return TRUE; /* potential new form is ok */
|
||||
}
|
||||
|
||||
#ifdef WIZARD
|
||||
/* prevent wizard mode user from specifying invalid vampshifter shape */
|
||||
static boolean
|
||||
STATIC_OVL boolean
|
||||
validvamp(mon, mndx_p, monclass)
|
||||
struct monst *mon;
|
||||
int *mndx_p, monclass;
|
||||
@@ -2715,17 +2722,35 @@ struct monst *mon;
|
||||
monclass = 0;
|
||||
getlin(pprompt, buf);
|
||||
mungspaces(buf);
|
||||
if (*buf == '\033' || !strcmp(buf, "*") ||
|
||||
!strcmp(buf, "random")) break; /* mndx == NON_PM */
|
||||
/* for ESC, take form selected above (might be NON_PM) */
|
||||
if (*buf == '\033') break;
|
||||
/* for "*", use NON_PM to pick an arbitrary shape below */
|
||||
if (!strcmp(buf, "*") || !strcmp(buf, "random")) {
|
||||
mndx == NON_PM;
|
||||
break;
|
||||
}
|
||||
mndx = name_to_mon(buf);
|
||||
if (mndx >= LOW_PM && validvamp(mon, &mndx, monclass)) break;
|
||||
monclass = name_to_monclass(buf, &mndx);
|
||||
if (monclass && mndx == NON_PM)
|
||||
mndx = mkclass_poly(monclass);
|
||||
if (mndx >= LOW_PM && validvamp(mon, &mndx, monclass)) break;
|
||||
if (mndx >= LOW_PM) {
|
||||
/* got a specific type of monster; use it if we can,
|
||||
otherwise drop down to "can"t" and try again */
|
||||
if (validvamp(mon, &mndx, monclass)) break;
|
||||
/* revert to random in case we exhaust tryct */
|
||||
mndx = NON_PM;
|
||||
} else {
|
||||
/* didn't get a type, so check whether it's a class
|
||||
(single letter or text match with def_monsyms[]);
|
||||
text match might already force a specific type,
|
||||
otherwise pick something from within the class */
|
||||
monclass = name_to_monclass(buf, &mndx);
|
||||
if (monclass && mndx == NON_PM)
|
||||
mndx = mkclass_poly(monclass);
|
||||
if (mndx >= LOW_PM) {
|
||||
if (validvamp(mon, &mndx, monclass)) break;
|
||||
mndx = NON_PM; /* revert to random */
|
||||
}
|
||||
}
|
||||
|
||||
pline("It can't become that.");
|
||||
mndx = NON_PM;
|
||||
} while (--tryct > 0);
|
||||
if (!tryct) pline(thats_enough_tries);
|
||||
if (is_vampshifter(mon) && !validvamp(mon, &mndx, monclass))
|
||||
@@ -2733,6 +2758,7 @@ struct monst *mon;
|
||||
}
|
||||
#endif /*WIZARD*/
|
||||
|
||||
/* if no form was specified above, pick one at random now */
|
||||
if (mndx == NON_PM) {
|
||||
tryct = 50;
|
||||
do {
|
||||
@@ -2742,7 +2768,28 @@ struct monst *mon;
|
||||
return mndx;
|
||||
}
|
||||
|
||||
/* make a chameleon look like a new monster; returns 1 if it actually changed */
|
||||
/* this used to be inline within newcham() but monpolycontrol needs it too */
|
||||
STATIC_OVL struct permonst *
|
||||
accept_newcham_form(mndx)
|
||||
int mndx;
|
||||
{
|
||||
struct permonst *mdat;
|
||||
|
||||
if (mndx == NON_PM) return 0;
|
||||
mdat = &mons[mndx];
|
||||
if ((mvitals[mndx].mvflags & G_GENOD) != 0) return 0;
|
||||
if (is_placeholder(mdat)) return 0;
|
||||
/* select_newcham_form() might deliberately pick a player
|
||||
character type (random selection never does) which
|
||||
polyok() rejects, so we need a special case here */
|
||||
if (is_mplayer(mdat)) return mdat;
|
||||
/* polyok() rules out M2_PNAME, M2_WERE, and all humans except Kops */
|
||||
return polyok(mdat) ? mdat : 0;
|
||||
}
|
||||
|
||||
/* make a chameleon take on another shape, or a polymorph target
|
||||
(possibly self-infliced) become a different monster;
|
||||
returns 1 if it actually changes form */
|
||||
int
|
||||
newcham(mtmp, mdat, polyspot, msg)
|
||||
struct monst *mtmp;
|
||||
@@ -2766,21 +2813,16 @@ boolean msg; /* "The oldmon turns into a newmon!" */
|
||||
}
|
||||
|
||||
/* mdat = 0 -> caller wants a random monster shape */
|
||||
tryct = 0;
|
||||
if (mdat == 0) {
|
||||
while (++tryct <= 100) {
|
||||
/* select_newcham_form() loops when resorting to random but
|
||||
it doesn't always pick that so we still retry here too */
|
||||
tryct = 20;
|
||||
do {
|
||||
mndx = select_newcham_form(mtmp);
|
||||
mdat = &mons[mndx];
|
||||
if ((mvitals[mndx].mvflags & G_GENOD) != 0 ||
|
||||
is_placeholder(mdat)) continue;
|
||||
/* polyok rules out all M2_PNAME and M2_WERE's;
|
||||
select_newcham_form might deliberately pick a player
|
||||
character type, so we can't arbitrarily rule out all
|
||||
human forms any more */
|
||||
if (is_mplayer(mdat) || (!is_human(mdat) && polyok(mdat)))
|
||||
break;
|
||||
}
|
||||
if (tryct > 100) return 0; /* Should never happen */
|
||||
mdat = accept_newcham_form(mndx);
|
||||
if (mdat) break;
|
||||
} while (--tryct > 0);
|
||||
if (!tryct) return 0;
|
||||
} else if (mvitals[monsndx(mdat)].mvflags & G_GENOD)
|
||||
return(0); /* passed in mdat is genocided */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user