name_to_monclass (trunk only)

Move the code for determining monster class from user's input string
out of do_class_genocide() and into new routine name_to_monclass().  I'm
planning to use it when name_to_mon fails to match anything for controlled
polymorph (not ready for prime time yet).

     Also, avoid getting stuck in a loop if hangup occurs while prompting
player for class of monster to genocide.  ESC, whether deliberate or fake
input after hangup, will now be the same as specifying "none", throwing
away the genocide opportunity.
This commit is contained in:
nethack.rankin
2007-04-08 04:35:19 +00:00
parent 3c58c3b235
commit 95d693a84c
4 changed files with 96 additions and 49 deletions

View File

@@ -1335,6 +1335,7 @@ E int FDECL(max_passive_dmg, (struct monst *,struct monst *));
E boolean FDECL(same_race, (struct permonst *,struct permonst *));
E int FDECL(monsndx, (struct permonst *));
E int FDECL(name_to_mon, (const char *));
E int FDECL(name_to_monclass, (const char *,int *));
E int FDECL(gender, (struct monst *));
E int FDECL(pronoun_gender, (struct monst *));
E boolean FDECL(levl_follower, (struct monst *));

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)monsym.h 3.5 1992/10/18 */
/* SCCS Id: @(#)monsym.h 3.5 2007/04/07 */
/* Monster symbols and creation information rev 1.0 */
/* NetHack may be freely redistributed. See license for details. */
@@ -44,6 +44,7 @@
#define S_FUNGUS 32
#define S_GNOME 33
#define S_GIANT 34
#define S_invisible 35 /* non-class present in def_monsyms[] */
#define S_JABBERWOCK 36
#define S_KOP 37
#define S_LICH 38
@@ -73,11 +74,6 @@
#define MAXMCLASSES 61 /* number of monster classes */
#if 0 /* moved to decl.h so that makedefs.c won't see them */
extern const char def_monsyms[MAXMCLASSES]; /* default class symbols */
extern uchar monsyms[MAXMCLASSES]; /* current class symbols */
#endif
/*
* Default characters for monsters. These correspond to the monster classes
* above.

View File

@@ -518,6 +518,13 @@ monsndx(ptr) /* return an index into the mons array */
return(i);
}
/* for handling alternate spellings */
struct alt_spl {
const char *name;
short pm_val;
};
/* figure out what type of monster a user-supplied string is specifying */
int
name_to_mon(in_str)
const char *in_str;
@@ -562,8 +569,7 @@ const char *in_str;
slen = strlen(str); /* length possibly needs recomputing */
{
static const struct alt_spl { const char* name; short pm_val; }
names[] = {
static const struct alt_spl names[] = {
/* Alternate spellings */
{ "grey dragon", PM_GRAY_DRAGON },
{ "baby grey dragon", PM_BABY_GRAY_DRAGON },
@@ -578,6 +584,8 @@ const char *in_str;
to the rank title prefix (input has been singularized) */
{ "master thief", PM_MASTER_OF_THIEVES },
{ "master of assassin", PM_MASTER_ASSASSIN },
/* Outdated names */
{ "invisible stalker", PM_STALKER },
/* Hyphenated names */
{ "ki rin", PM_KI_RIN },
{ "uruk hai", PM_URUK_HAI },
@@ -605,7 +613,7 @@ const char *in_str;
{ "mumakil", PM_MUMAK },
{ "erinyes", PM_ERINYS },
/* end of list */
{ 0, 0 }
{ 0, NON_PM }
};
register const struct alt_spl *namep;
@@ -637,6 +645,81 @@ const char *in_str;
return mntmp;
}
/* monster class from user input; used for genocide and controlled polymorph;
returns 0 rather than MAXMCLASSES if no match is found */
int
name_to_monclass(in_str, mndx_p)
const char *in_str;
int *mndx_p;
{
/* Single letters are matched against def_monsyms[].sym; words
or phrases are first matched against def_monsyms[].explain
to check class description; if not found there, then against
mons[].mname to test individual monster types. Input can be a
substring of the full description or mname, but to be accepted,
such partial matches must start at beginning of a word. Some
class descriptions include "foo or bar" and "foo or other foo"
so we don't want to accept "or", "other", "or other" there. */
static NEARDATA const char * const falsematch[] = {
/* multiple-letter input which matches any of these gets rejected */
"an", "the", "or", "other", "or other", 0
};
static NEARDATA const struct alt_spl truematch[] = {
/* "long worm" won't match "worm" class but would accidentally match
"long worm tail" class before the comparison with monster types */
{ "long worm", PM_LONG_WORM },
/* some plausible guesses which need help */
{ "bug", -S_XAN }, /* would match bugbear... */
{ "fish", -S_EEL }, /* wouldn't match anything */
/* end of list */
{ 0, NON_PM }
};
const char *p, *x;
int i;
if (mndx_p) *mndx_p = NON_PM; /* haven't [yet] matched a specific type */
if (!in_str || !in_str[0]) {
/* empty input */
return 0;
} else if (!in_str[1]) {
/* single character */
i = def_char_to_monclass(*in_str);
if (i == MAXMCLASSES)
i = (*in_str == DEF_INVISIBLE) ? S_invisible : 0;
return i;
} else {
/* multiple characters */
in_str = makesingular(in_str);
/* check for special cases */
for (i = 0; falsematch[i]; i++)
if (!strcmpi(in_str, falsematch[i])) return 0;
for (i = 0; truematch[i].name; i++)
if (!strcmpi(in_str, truematch[i].name)) {
i = truematch[i].pm_val;
if (i < 0) return -i; /* class */
if (mndx_p) *mndx_p = i; /* monster */
return mons[i].mlet;
}
/* check monster class descriptions */
for (i = 1; i < MAXMCLASSES; i++) {
x = def_monsyms[i].explain;
if ((p = strstri(x, in_str)) != 0 && (p == x || *(p - 1) == ' '))
return i;
}
/* check individual species names; not as thorough as mon_to_name()
but our caller can call that directly if desired */
for (i = LOW_PM; i < NUMMONS; i++) {
x = mons[i].mname;
if ((p = strstri(x, in_str)) != 0 && (p == x || *(p - 1) == ' ')) {
if (mndx_p) *mndx_p = i;
return mons[i].mlet;
}
}
}
return 0;
}
/* returns 3 values (0=male, 1=female, 2=none) */
int
gender(mtmp)

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)read.c 3.5 2006/06/13 */
/* SCCS Id: @(#)read.c 3.5 2007/04/07 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1605,61 +1605,28 @@ do_class_genocide()
getlin("What class of monsters do you wish to genocide?",
buf);
(void)mungspaces(buf);
} while (buf[0]=='\033' || !buf[0]);
if (*buf == '\033') Strcpy(buf, "none"); /* hangup? */
} while (!*buf);
/* choosing "none" preserves genocideless conduct */
if (!strcmpi(buf, "none") ||
!strcmpi(buf, "nothing")) return;
if (strlen(buf) == 1) {
if (buf[0] == ILLOBJ_SYM)
buf[0] = def_monsyms[S_MIMIC].sym;
class = def_char_to_monclass(buf[0]);
} else {
char buf2[BUFSZ];
class = 0;
Strcpy(buf2, makesingular(buf));
Strcpy(buf, buf2);
}
class = name_to_monclass(buf, (int *)0);
if (class == 0 && (i = name_to_mon(buf)) != NON_PM)
class = mons[i].mlet;
immunecnt = gonecnt = goodcnt = 0;
for (i = LOW_PM; i < NUMMONS; i++) {
if (class == 0 &&
strstri(def_monsyms[(int)mons[i].mlet].explain, buf) != 0)
class = mons[i].mlet;
if (mons[i].mlet == class) {
if (!(mons[i].geno & G_GENO)) immunecnt++;
else if(mvitals[i].mvflags & G_GENOD) gonecnt++;
else goodcnt++;
}
}
/*
* If user's input doesn't match any class
* description, check individual species names.
*/
if (class == 0) {
for (i = LOW_PM; i < NUMMONS; i++) {
if (strstri(mons[i].mname, buf) != 0) {
class = mons[i].mlet;
break;
}
}
if (class != 0) {
for (i = LOW_PM; i < NUMMONS; i++) {
if (mons[i].mlet == class) {
if (!(mons[i].geno & G_GENO)) immunecnt++;
else if(mvitals[i].mvflags & G_GENOD) gonecnt++;
else goodcnt++;
}
}
}
}
if (!goodcnt && class != mons[urole.malenum].mlet &&
class != mons[urace.malenum].mlet) {
if (gonecnt)
pline("All such monsters are already nonexistent.");
else if (immunecnt ||
(buf[0] == DEF_INVISIBLE && buf[1] == '\0'))
else if (immunecnt || class == S_invisible)
You("aren't permitted to genocide such monsters.");
else
#ifdef WIZARD /* to aid in topology testing; remove pesky monsters */