From 8c1120261a1b11a7915068fbb21e195a7edab8e7 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 30 May 2020 16:00:29 -0700 Subject: [PATCH] fix github issue #352 - geno'd monsters in quest The recent change to mkclass() was letting genocided monsters be created when role-specific monsters were chosen for quest levels. makemon(Null) -> rndmonst() -> qt_montype() -> mkclass() -> mk_gen_ok() was accepting members of the quest-specified class even when they should have been rejected. I'm still not sure why the revised bit manipulation didn't work as intended; the re-revised code does. G_IGNORE was a bug waiting to happen since it gets passed to mkclass() as a mons[].geno flag but is used to control the use of mvitals[].mvflags values. It's still being misused but at least it doesn't conflict with any of the other flags now. Fixes #352 --- doc/fixes37.0 | 3 ++- include/monflag.h | 14 ++++++++------ src/makemon.c | 32 ++++++++++++++++++++------------ 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 3aa10f764..9867cd7d6 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.226 $ $NHDT-Date: 1590870784 2020/05/30 20:33:04 $ +$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.227 $ $NHDT-Date: 1590879610 2020/05/30 23:00:10 $ General Fixes and Modified Features ----------------------------------- @@ -251,6 +251,7 @@ fix new "objects[0] class #1 not in order!" panic if plain 'char' is unsigned only generate shop items on solid floor squares avoid gcc 10 warning by removing duplicate definition of 'head_engr' if a monster removed a corpse from an ice box, the corpse would never rot away +monster creation on quest levels could make genocided creatures tty: redraw unexplored locations as S_unexplored rather than after map has been partially overwritten by popup menu or text display diff --git a/include/monflag.h b/include/monflag.h index 6f9f591ca..b343791b0 100644 --- a/include/monflag.h +++ b/include/monflag.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 monflag.h $NHDT-Date: 1585356420 2020/03/28 00:47:00 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.19 $ */ +/* NetHack 3.6 monflag.h $NHDT-Date: 1590879610 2020/05/30 23:00:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.20 $ */ /* Copyright (c) 1989 Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ @@ -194,14 +194,16 @@ enum ms_sounds { #define G_GENO 0x0020 /* can be genocided */ #define G_NOCORPSE 0x0010 /* no corpse left ever */ #define G_FREQ 0x0007 /* creation frequency mask */ +/* note: G_IGNORE controls handling of mvitals[].mvflags bits but is + passed to mkclass() as if it dealt with mons[].geno bits */ +#define G_IGNORE 0x8000 /* for mkclass(), ignore G_GENOD|G_EXTINCT */ /* for g.mvitals[].mvflags (variant during game), along with G_NOCORPSE */ -#define G_KNOWN 0x0004 /* have been encountered */ -#define G_GENOD 0x0002 /* have been genocided */ -#define G_EXTINCT 0x0001 /* population control; create no more */ +#define G_KNOWN 0x04 /* have been encountered */ +#define G_GENOD 0x02 /* have been genocided */ +#define G_EXTINCT 0x01 /* population control; create no more */ #define G_GONE (G_GENOD | G_EXTINCT) -#define MV_KNOWS_EGG 0x0008 /* player recognizes egg of this monster type */ -#define G_IGNORE 0x1000 /* for mkclass(), ignore G_GENOD|G_EXTINCT */ +#define MV_KNOWS_EGG 0x08 /* player recognizes egg of this monster type */ /* *INDENT-ON* */ /* clang-format on */ diff --git a/src/makemon.c b/src/makemon.c index 4278d5a46..7ac6924c8 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 makemon.c $NHDT-Date: 1590621476 2020/05/27 23:17:56 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.171 $ */ +/* NetHack 3.6 makemon.c $NHDT-Date: 1590879611 2020/05/30 23:00:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.172 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -16,7 +16,7 @@ static boolean FDECL(uncommon, (int)); static int FDECL(align_shift, (struct permonst *)); -static boolean FDECL(mk_gen_ok, (int, int, int)); +static boolean FDECL(mk_gen_ok, (int, unsigned, unsigned)); static boolean FDECL(wrong_elem_type, (struct permonst *)); static void FDECL(m_initgrp, (struct monst *, int, int, int, int)); static void FDECL(m_initthrow, (struct monst *, int, int)); @@ -1624,11 +1624,12 @@ rndmonst() /* decide whether it's ok to generate a candidate monster by mkclass() */ static boolean mk_gen_ok(mndx, mvflagsmask, genomask) -int mndx, mvflagsmask, genomask; +int mndx; +unsigned mvflagsmask, genomask; { struct permonst *ptr = &mons[mndx]; - if ((g.mvitals[mndx].mvflags & mvflagsmask) && !(genomask & G_IGNORE)) + if (g.mvitals[mndx].mvflags & mvflagsmask) return FALSE; if (ptr->geno & genomask) return FALSE; @@ -1658,12 +1659,13 @@ int spc; struct permonst * mkclass_aligned(class, spc, atyp) char class; -int spc; +int spc; /* special mons[].geno handling */ aligntyp atyp; { register int first, last, num = 0; int k, nums[SPECIAL_PM + 1]; /* +1: insurance for final return value */ - int maxmlev, gmask, gehennom = Inhell != 0; + int maxmlev, gehennom = Inhell != 0; + unsigned mv_mask, gn_mask; (void) memset((genericptr_t) nums, 0, sizeof nums); maxmlev = level_difficulty() >> 1; @@ -1685,6 +1687,13 @@ aligntyp atyp; return (struct permonst *) 0; } + mv_mask = G_GONE; /* G_GENOD | G_EXTINCT */ + if ((spc & G_IGNORE) != 0) { + mv_mask = 0; /* mv_mask &= ~G_GONE; */ + /* G_IGNORE is not a mons[].geno mask so get rid of it now */ + spc &= ~G_IGNORE; + } + /* Assumption #2: monsters of a given class are presented in ascending * order of strength. */ @@ -1696,13 +1705,12 @@ aligntyp atyp; the majority of major demons aren't constrained to Gehennom; arch- and master liches are always so constrained (for creation; lesser liches might grow up into them elsewhere) */ - gmask = (G_NOGEN | G_UNIQ); + gn_mask = (G_NOGEN | G_UNIQ); if (rn2(9) || class == S_LICH) - gmask |= (gehennom ? G_NOHELL : G_HELL); - gmask &= ~spc; - gmask |= (spc & G_IGNORE); + gn_mask |= (gehennom ? G_NOHELL : G_HELL); + gn_mask &= ~spc; - if (mk_gen_ok(last, G_GONE, gmask)) { + if (mk_gen_ok(last, mv_mask, gn_mask)) { /* consider it; don't reject a toostrong() monster if we don't have anything yet (num==0) or if it is the same (or lower) difficulty as preceding candidate (non-zero @@ -1748,7 +1756,7 @@ mkclass_poly(class) int class; { register int first, last, num = 0; - int gmask; + unsigned gmask; for (first = LOW_PM; first < SPECIAL_PM; first++) if (mons[first].mlet == class)