From 95d693a84cb8ada260b9912815e64ef406a10c19 Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Sun, 8 Apr 2007 04:35:19 +0000 Subject: [PATCH] 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. --- include/extern.h | 1 + include/monsym.h | 8 ++--- src/mondata.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++-- src/read.c | 47 ++++--------------------- 4 files changed, 96 insertions(+), 49 deletions(-) diff --git a/include/extern.h b/include/extern.h index fe81c19d9..cef64a9d8 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/include/monsym.h b/include/monsym.h index 6c7f11c9f..ea18d8f9f 100644 --- a/include/monsym.h +++ b/include/monsym.h @@ -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. diff --git a/src/mondata.c b/src/mondata.c index 86981c519..3cd514041 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -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) diff --git a/src/read.c b/src/read.c index 25b86ea51..fcf5c3b91 100644 --- a/src/read.c +++ b/src/read.c @@ -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 */