diff --git a/doc/fixes34.3 b/doc/fixes34.3 index e2738cfe2..3f0ecfec5 100644 --- a/doc/fixes34.3 +++ b/doc/fixes34.3 @@ -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 diff --git a/include/extern.h b/include/extern.h index be7c0b21e..741087c7f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 ### */ diff --git a/include/hack.h b/include/hack.h index 69f25ad4e..469664e16 100644 --- a/include/hack.h +++ b/include/hack.h @@ -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 */ diff --git a/src/bones.c b/src/bones.c index 2fe77948c..54c585b25 100644 --- a/src/bones.c +++ b/src/bones.c @@ -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); diff --git a/src/makemon.c b/src/makemon.c index 7b7dda6a5..1657582f3 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -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); } diff --git a/src/restore.c b/src/restore.c index 2ad6617d6..16a58e9ce 100644 --- a/src/restore.c +++ b/src/restore.c @@ -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);