From 5f34c8118749dc027ddb1a1ae5dd62dafac47901 Mon Sep 17 00:00:00 2001 From: Nate Choe Date: Sat, 31 Jan 2026 06:52:48 -0600 Subject: [PATCH 1/2] remove fuzzy string matching for uncursed and cursed scrolls of genocide --- src/read.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/read.c b/src/read.c index 8b8f6bc799..849b44d3ae 100644 --- a/src/read.c +++ b/src/read.c @@ -2780,7 +2780,7 @@ do_genocide( int i, killplayer = 0; int mndx; struct permonst *ptr; - const char *which; + const char *which, *rem; if (how & PLAYER) { mndx = u.umonster; /* non-polymorphed mon num */ @@ -2834,7 +2834,24 @@ do_genocide( continue; } - mndx = name_to_mon(buf, (int *) 0); + mndx = name_to_monplus(buf, &rem, (int *) 0); + + /* ensure that the monster name encompasses the full string. without + * this check, "master mnd flayer" would be parsed as "master" and + * eliminate monks, with rem set to " mnd flayer" + * + * the first check is there because name_to_monplus sets rem based + * on the singular form of the name, so "energy vortices" is parsed + * as "energy vortex" and rem is set to "es" */ + if (rem) { + while (*rem && *rem != ' ' && *rem != '\'') + ++rem; + while (*rem && (*rem == ' ' || *rem == '\'')) + ++rem; + if (*rem) + mndx = NON_PM; + } + if (mndx == NON_PM || (svm.mvitals[mndx].mvflags & G_GENOD)) { pline("Such creatures %s exist in this world.", (mndx == NON_PM) ? "do not" : "no longer"); From 877b7662c5ae6caa7f76890f3abf88cffbebcaee Mon Sep 17 00:00:00 2001 From: Nate Choe Date: Sat, 31 Jan 2026 07:53:35 -0600 Subject: [PATCH 2/2] remove fuzzy string matching for blessed scrolls of genocide --- include/extern.h | 1 + src/mondata.c | 30 +++++++++++++++++++++++------- src/read.c | 6 ++---- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/include/extern.h b/include/extern.h index 3d8261029a..b020f519bd 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1868,6 +1868,7 @@ extern boolean same_race(struct permonst *, struct permonst *) NONNULLARG12; extern int name_to_mon(const char *, int *) NONNULLARG1; extern int name_to_monplus(const char *, const char **, int *) NONNULLARG1; extern int name_to_monclass(const char *, int *); +extern int name_to_monclass_plus(const char *, int *, boolean); extern int gender(struct monst *) NONNULLARG1; extern int pronoun_gender(struct monst *, unsigned) NONNULLARG1; extern boolean levl_follower(struct monst *) NONNULLARG1; diff --git a/src/mondata.c b/src/mondata.c index d7ea802450..9a0dbf7d68 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -1087,7 +1087,18 @@ name_to_monplus( /* 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(const char *in_str, int * mndx_p) +name_to_monclass(const char *in_str, int *mndx_p) +{ + return name_to_monclass_plus(in_str, mndx_p, TRUE); +} + +int +name_to_monclass_plus( + const char *in_str, + int *mndx_p, + boolean allow_mname) /* if true, allows for a single monster to represent + its whole class. for example, "dwarf" for all "h". + */ { /* Single letters are matched against def_monsyms[].sym; words or phrases are first matched against def_monsyms[].explain @@ -1117,7 +1128,7 @@ name_to_monclass(const char *in_str, int * mndx_p) { 0, NON_PM, NEUTRAL} }; const char *p, *x; - int i, len; + int i, ret, len; if (mndx_p) *mndx_p = NON_PM; /* haven't [yet] matched a specific type */ @@ -1148,12 +1159,14 @@ name_to_monclass(const char *in_str, int * mndx_p) 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 */ + ret = truematch[i].pm_val; + if (ret < 0) + return -ret; /* class */ + if (!allow_mname) + continue; if (mndx_p) - *mndx_p = i; /* monster */ - return mons[i].mlet; + *mndx_p = ret; /* monster */ + return mons[ret].mlet; } /* check monster class descriptions */ len = (int) strlen(in_str); @@ -1164,6 +1177,9 @@ name_to_monclass(const char *in_str, int * mndx_p) && (p[len] == '\0' || p[len] == ' '))) return i; } + if (!allow_mname) { + return 0; + } /* check individual species names */ i = name_to_mon(in_str, (int *) 0); if (i != NON_PM) { diff --git a/src/read.c b/src/read.c index 849b44d3ae..47c9b01c37 100644 --- a/src/read.c +++ b/src/read.c @@ -2629,9 +2629,7 @@ do_class_genocide(void) continue; } - class = name_to_monclass(buf, (int *) 0); - if (class == 0 && (i = name_to_mon(buf, (int *) 0)) != NON_PM) - class = mons[i].mlet; + class = name_to_monclass_plus(buf, (int *) 0, FALSE); immunecnt = gonecnt = goodcnt = 0; for (i = LOW_PM; i < NUMMONS; i++) { if (mons[i].mlet == class) { @@ -2663,7 +2661,7 @@ do_class_genocide(void) pline("Eliminated %d monster%s.", gonecnt, plur(gonecnt)); return; } else - pline("That %s does not represent any monster.", + pline("That %s does not represent any monster class.", strlen(buf) == 1 ? "symbol" : "response"); continue; }