during devel make it easy to review mon difficulty
authornhmall <nhmall@nethack.org>
Fri, 7 Oct 2022 14:26:40 +0000 (10:26 -0400)
committernhmall <nhmall@nethack.org>
Fri, 7 Oct 2022 14:26:40 +0000 (10:26 -0400)
include/extern.h
src/cmd.c
src/mon.c

index 9f6a426c6e21ce581aa100635c04a977fc8d47b7..51f13b319555a4e6c59b63d99de9db1fa878df16 100644 (file)
@@ -1604,6 +1604,9 @@ extern void pacify_guards(void);
 extern void decide_to_shapeshift(struct monst *, int);
 extern boolean vamp_stone(struct monst *);
 extern void check_gear_next_turn(struct monst *);
+#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG)
+extern int mstrength(struct permonst *ptr);
+#endif
 
 /* ### mondata.c ### */
 
index 804c0f0174933aa41d622c9bf6fe47970d80a8c7..beb6011bca09cc2b1b80b81e7629463b94a45e6d 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -17,6 +17,7 @@
 #endif
 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG)
 static int wiz_display_macros(void);
+static int wiz_check_mdifficulty(void);
 #endif
 
 #ifdef DUMB /* stuff commented out in extern.h, but needed here */
@@ -2756,6 +2757,8 @@ struct ext_func_tab extcmdlist[] = {
     { C('e'), "wizdetect", "reveal hidden things within a small radius",
               wiz_detect, IFBURIED | WIZMODECMD, NULL },
 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG)
+    { '\0',   "wizcheckmdifficulty", "validate the difficulty levels of monsters",
+              wiz_check_mdifficulty, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL },
     { '\0',   "wizdispmacros", "validate the display macro ranges",
               wiz_display_macros, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL },
 #endif
@@ -4088,6 +4091,39 @@ wiz_display_macros(void)
 }
 #endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */
 
+#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG)
+/* the #wizcheckmdifficulty command */
+static int
+wiz_check_mdifficulty(void)
+{
+    char buf[BUFSZ];
+    winid win;
+    int mhardcoded = 0, mcalculated = 0, trouble = 0, cnt = 0, mdiff = 0;
+    struct permonst *ptr;
+    static const char *const display_issues = "Review of monster difficulties:";
+
+    win = create_nhwindow(NHW_TEXT);
+    for (ptr = &mons[0]; ptr->mlet; ptr++, cnt++) {
+        mcalculated = mstrength(ptr);
+        mhardcoded = (int) ptr->difficulty;
+        mdiff = mhardcoded - mcalculated;
+        if (mdiff) {
+            trouble++;
+            Snprintf(buf, sizeof buf,
+                     "%-18s [%4d]: calculated: %2d, hardcoded: %2d (%+d)",
+                     ptr->pmnames[NEUTRAL], cnt, mcalculated, mhardcoded,
+                     mdiff);
+            putstr(win, 0, buf);
+       }
+    }
+    if (!trouble)
+        putstr(win, 0, "No monster difficulty discrepencies were detected");
+    display_nhwindow(win, FALSE);
+    destroy_nhwindow(win);
+    return ECMD_OK;
+}
+#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */
+
 RESTORE_WARNING_FORMAT_NONLITERAL
 
 static void
index cc18b0c46d7dfcc4f9550987f285187e92625c28..6e5f359e612abc571d97ad34d29a89bee9adafb4 100644 (file)
--- a/src/mon.c
+++ b/src/mon.c
@@ -30,6 +30,10 @@ static struct permonst *accept_newcham_form(struct monst *, int);
 static void kill_eggs(struct obj *);
 static void pacify_guard(struct monst *);
 
+#ifdef DEBUG
+int mstrength(struct permonst *ptr);
+#endif
+
 #define LEVEL_SPECIFIC_NOCORPSE(mdat) \
     (Is_rogue_level(&u.uz)            \
      || (g.level.flags.graveyard && is_undead(mdat) && rn2(3)))
@@ -5177,4 +5181,63 @@ check_gear_next_turn(struct monst *mon)
     mon->misc_worn_check |= I_SPECIAL;
 }
 
+#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG)
+/* This routine is designed to return an integer value which represents
+ * an approximation of monster strength.  It uses a similar method of
+ * determination as "experience()" to arrive at the strength.
+ */
+int
+mstrength(struct permonst *ptr)
+{
+    int i, tmp2, n, tmp = ptr->mlevel;
+
+    if(tmp > 49)        /* special fixed hp monster */
+        tmp = 2*(tmp - 6) / 4;
+
+/*  For creation in groups */
+    n = (!!(ptr->geno & G_SGROUP));
+    n += (!!(ptr->geno & G_LGROUP)) << 1;
+
+/*  For ranged attacks */
+    if (ranged_attk(ptr)) n++;
+
+/*  For higher ac values */
+    n += (ptr->ac < 4);
+    n += (ptr->ac < 0);
+
+/*  For very fast monsters */
+    n += (ptr->mmove >= 18);
+
+/*  For each attack and "special" attack */
+    for(i = 0; i < NATTK; i++) {
+
+        tmp2 = ptr->mattk[i].aatyp;
+        n += (tmp2 > 0);
+        n += (tmp2 == AT_MAGC);
+        n += (tmp2 == AT_WEAP && (ptr->mflags2 & M2_STRONG));
+    }
+
+/*  For each "special" damage type */
+    for(i = 0; i < NATTK; i++) {
+
+        tmp2 = ptr->mattk[i].adtyp;
+        if ((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || (tmp2 == AD_DRST)
+        || (tmp2 == AD_DRDX) || (tmp2 == AD_DRCO) || (tmp2 == AD_WERE))
+            n += 2;
+        else if (strcmp(ptr->pmnames[NEUTRAL], "grid bug")) n += (tmp2 != AD_PHYS);
+        n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23);
+    }
+
+/*  Leprechauns are special cases.  They have many hit dice so they
+    can hit and are hard to kill, but they don't really do much damage. */
+    if (!strcmp(ptr->pmnames[NEUTRAL], "leprechaun")) n -= 2;
+
+/*  Finally, adjust the monster level  0 <= n <= 24 (approx.) */
+    if(n == 0) tmp--;
+    else if(n >= 6) tmp += ( n / 2 );
+    else tmp += ( n / 3 + 1);
+
+    return((tmp >= 0) ? tmp : 0);
+}
+#endif  /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || DEBUG */
 /*mon.c*/