]> granicus.if.org Git - nethack/commitdiff
name_to_monclass (trunk only)
authornethack.rankin <nethack.rankin>
Sun, 8 Apr 2007 04:35:19 +0000 (04:35 +0000)
committernethack.rankin <nethack.rankin>
Sun, 8 Apr 2007 04:35:19 +0000 (04:35 +0000)
     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
include/monsym.h
src/mondata.c
src/read.c

index fe81c19d9aae91d8c999e1a4ed642f6040c4bac1..cef64a9d83f1172dbbd460cdfb97c3c1bef79ceb 100644 (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 *));
index 6c7f11c9ff30f0935b0eb649bbd5650531748e5d..ea18d8f9fcbac2a6961f14ef41d12cfa89142c39 100644 (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
 
 #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.
index 86981c51997bac9314f36c0e215d6074804cab63..3cd514041fb03d8a61974c458125d18b28b73fb6 100644 (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)
index 25b86ea511daf9e0406af83efb450b357655e1ec..fcf5c3b914295d6616238dfbde49536882cf551e 100644 (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 */