]> granicus.if.org Git - nethack/commitdiff
monster birth limits exceeded by bones load
authornethack.allison <nethack.allison>
Fri, 5 Sep 2003 20:39:35 +0000 (20:39 +0000)
committernethack.allison <nethack.allison>
Fri, 5 Sep 2003 20:39:35 +0000 (20:39 +0000)
Bones loading was only checking to see if a
monster was marked extinct, it wasn't adding
up the born count of a species in the current
game with the number of that species on the
bones level being loaded. That made it possible
to exceed the correct number of nazgul and
erinys via bones.

This adds a common routine called propagate()
that makemon() and restmonchn(ghostly) share,
for incrementing the born count and checking for
extinction, etc.

When a bones level is loaded, restmonchn()
will flag an illegal monster (duplicated unique,
or too many of a species) by setting the
individual monster's mhpmax to the cookie
value DEFUNCT_MONSTER. Before getbones() finishes
loading the bones level, it will purge those
monsters from the chain.

doc/fixes34.3
include/extern.h
include/hack.h
src/bones.c
src/makemon.c
src/restore.c

index e2738cfe2895d1f1480e7a08ca35c85620777fdd..3f0ecfec5726eda6084af8ab4e7b586271ed7a95 100644 (file)
@@ -7,6 +7,7 @@ polymorphing to a flaming sphere should cure slime like other flaming monsters
 grammar, spelling and other typos
 student statues were converted to valkyries, not archeologists
 fix typo in bustling town down stairs declaration
+you could exceed the limits on nazgul and erinys counts via bones files
 
 
 Platform- and/or Interface-Specific Fixes
index be7c0b21e8ba1020b5098eddee29ead2c073e9c5..741087c7fd757d9a9cf696df5f8baed835821216 100644 (file)
@@ -938,6 +938,7 @@ E void FDECL(mimic_hit_msg, (struct monst *, SHORT_P));
 E void FDECL(mkmonmoney, (struct monst *, long));
 #endif
 E void FDECL(bagotricks, (struct obj *));
+E boolean FDECL(propagate, (int, BOOLEAN_P));
 
 /* ### mapglyph.c ### */
 
index 69f25ad4ef72671c64f24899f58d367faa709b73..469664e16c6d3d295846409269c3c98411c81198 100644 (file)
@@ -142,6 +142,9 @@ NEARDATA extern coord bhitpos;      /* place where throw or zap hits or stops */
 #define MM_IGNOREWATER   0x80  /* ignore water when positioning */
 #define MM_ADJACENTOK    0x100 /* it is acceptable to use adjacent coordinates */
 
+/* special mhpmax value when loading bones monster to flag as extinct or genocided */
+#define DEFUNCT_MONSTER        (-100)
+
 /* flags for special ggetobj status returns */
 #define ALL_FINISHED     0x01  /* called routine already finished the job */
 
index 2fe77948ca0ebf1b055035c14fa31c9f6d7ced9e..54c585b25d0ffccf6f1c67a8e607015d4ee41a96 100644 (file)
@@ -422,21 +422,21 @@ getbones()
                        trickery(errbuf);
                } else {
                        register struct monst *mtmp;
-                       int mndx;
 
                        getlev(fd, 0, 0, TRUE);
 
-                       /* to correctly reset named artifacts on the level and
-                          to keep tabs on unique monsters like demon lords */
+                       /* Note that getlev() now keeps tabs on unique
+                        * monsters such as demon lords, and tracks the
+                        * birth counts of all species just as makemon()
+                        * does.  If a bones monster is extinct or has been
+                        * subject to genocide, their mhpmax will be
+                        * set to the magic DEFUNCT_MONSTER cookie value.
+                        */
                        for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
-                           mndx = monsndx(mtmp->data);
-                           if (mvitals[mndx].mvflags & G_EXTINCT) {
-                               mongone(mtmp);
-                           } else {
-                               if (mons[mndx].geno & G_UNIQ)
-                                   mvitals[mndx].mvflags |= G_EXTINCT;
+                           if (mtmp->mhpmax == DEFUNCT_MONSTER) mongone(mtmp);
+                           else
+                               /* to correctly reset named artifacts on the level */
                                resetobjs(mtmp->minvent,TRUE);
-                           }
                        }
                        resetobjs(fobj,TRUE);
                        resetobjs(level.buriedobjlist,TRUE);
index 7b7dda6a5967fb00605337b719ee55108f1fb861..1657582f3874d64dba1d5d78d121b3a91367fb2b 100644 (file)
@@ -736,6 +736,44 @@ struct monst *mon;
        return m2;
 }
 
+/*
+ * Propagate a species
+ *
+ * Once a certain number of monsters are created, don't create any more
+ * at random (i.e. make them extinct).  The previous (3.2) behavior was
+ * to do this when a certain number had _died_, which didn't make
+ * much sense.
+ *
+ * Returns FALSE propagation unsuccessful
+ *         TRUE  propagation successful
+ */
+boolean
+propagate(mndx, tally)
+int mndx;
+boolean tally;
+{
+       uchar lim = mbirth_limit(mndx);
+       boolean not_gone_yet = (!(mons[mndx].geno & G_NOGEN) &&
+                               !(mvitals[mndx].mvflags & G_EXTINCT));
+       if ((int) mvitals[mndx].born < lim && not_gone_yet) {
+               if (tally) mvitals[mndx].born++;
+               /* if it's unique, or we've reached the limit
+                * don't ever make it again.
+                */
+               if ((mvitals[mndx].born >= lim) || (mons[mndx].geno & G_UNIQ)) {
+#if defined(WIZARD) && defined(DEBUG)
+                       if (wizard)
+                               pline("Automatically extinguished %s.",
+                                       makeplural(mons[mndx].mname));
+#endif
+                       mvitals[mndx].mvflags |= G_EXTINCT;
+                       reset_rndmonst(mndx);
+               }
+               return TRUE;
+       }
+       return FALSE;
+}
+
 /*
  * called with [x,y] = coordinates;
  *     [0,0] means anyplace
@@ -756,7 +794,6 @@ register int        mmflags;
        boolean allow_minvent = ((mmflags & NO_MINVENT) == 0);
        boolean countbirth = ((mmflags & MM_NOCOUNTBIRTH) == 0);
        unsigned gpflags = (mmflags & MM_IGNOREWATER) ? MM_IGNOREWATER : 0;
-       uchar lim;
 
        /* if caller wants random location, do it here */
        if(x == 0 && y == 0) {
@@ -797,7 +834,7 @@ register int        mmflags;
                /* if you are to make a specific monster and it has
                   already been genocided, return */
                if (mvitals[mndx].mvflags & G_GENOD) return((struct monst *) 0);
-#ifdef DEBUG
+#if defined(WIZARD) && defined(DEBUG)
                if (wizard && (mvitals[mndx].mvflags & G_EXTINCT))
                    pline("Explicitly creating extinct monster %s.",
                        mons[mndx].mname);
@@ -821,32 +858,7 @@ register int       mmflags;
                } while(!goodpos(x, y, &fakemon, gpflags) && tryct++ < 50);
                mndx = monsndx(ptr);
        }
-       /* if it's unique, don't ever make it again */
-       if (ptr->geno & G_UNIQ) mvitals[mndx].mvflags |= G_EXTINCT;
-
-       /* Once a certain number of monsters are created, don't create any more
-        * at random (i.e. make them extinct).  The previous (3.2) behavior was
-        * to do this when a certain number had _died_, which didn't make
-        * much sense.
-        * This version makes a little more sense but still requires that
-        * the caller manually decrement mvitals if the monster is created
-        * under circumstances where one would not logically expect the
-        * creation to reduce the supply of wild monsters.  Monster cloning
-        * might be one case that requires that in order to reduce the
-        * possibility of abuse, but currently doesn't.
-        */
-       if (mvitals[mndx].born < 255 && countbirth) mvitals[mndx].born++;
-       lim = mbirth_limit(mndx);
-       if ((int) mvitals[mndx].born >= lim && !(mons[mndx].geno & G_NOGEN) &&
-               !(mvitals[mndx].mvflags & G_EXTINCT)) {
-#ifdef DEBUG
-               pline("Automatically extinguished %s.",
-                                       makeplural(mons[mndx].mname));
-#endif
-               mvitals[mndx].mvflags |= G_EXTINCT;
-               reset_rndmonst(mndx);
-       }
-
+       propagate(mndx, countbirth);
        xlth = ptr->pxlth;
        if (mmflags & MM_EDOG) xlth += sizeof(struct edog);
        else if (mmflags & MM_EMIN) xlth += sizeof(struct emin);
@@ -1050,6 +1062,7 @@ int
 mbirth_limit(mndx)
 int mndx;
 {
+       /* assert(MAXMONNO < 255); */
        return (mndx == PM_NAZGUL ? 9 : mndx == PM_ERINYS ? 3 : MAXMONNO); 
 }
 
index 2ad6617d60b145c60b43d0ad7ebe10ae03189a9f..16a58e9ce37e77c5ed9029638b87772859b35945 100644 (file)
@@ -266,6 +266,13 @@ boolean ghostly;
                }
                offset = mtmp->mnum;
                mtmp->data = &mons[offset];
+               if (ghostly) {
+                       int mndx = monsndx(mtmp->data);
+                       if (propagate(mndx, TRUE) == 0) {
+                               /* cookie to trigger purge in getbones() */
+                               mtmp->mhpmax = DEFUNCT_MONSTER; 
+                       }
+               }
                if(mtmp->minvent) {
                        struct obj *obj;
                        mtmp->minvent = restobjchn(fd, ghostly, FALSE);