diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 0544ed75f..d9c52681e 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.142 $ $NHDT-Date: 1584872363 2020/03/22 10:19:23 $ +$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.144 $ $NHDT-Date: 1585361048 2020/03/28 02:04:08 $ General Fixes and Modified Features ----------------------------------- @@ -92,6 +92,8 @@ praying on an unaligned altar outside of Gehennom behaved like an ordinary Discworld typo: Moving Pictures passage 12 "or" -> "of" unicorn corpses and wraith corpses could be sacrificed even if "too old" hero polymorphed into a hider and hiding was not unhidden when teleporting +impose tighter restraints on 'summon nasties', both for spellcasting monsters + and post-Wizard harassment Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 015450055..18443b0a2 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1584405113 2020/03/17 00:31:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.814 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1585361043 2020/03/28 02:04:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.815 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1218,6 +1218,7 @@ E struct mextra *NDECL(newmextra); E void FDECL(copy_mextra, (struct monst *, struct monst *)); E void FDECL(dealloc_mextra, (struct monst *)); E struct monst *FDECL(makemon, (struct permonst *, int, int, int)); +E struct monst *FDECL(unmakemon, (struct monst *, int)); E boolean FDECL(create_critters, (int, struct permonst *, BOOLEAN_P)); E struct permonst *NDECL(rndmonst); E struct permonst *FDECL(mkclass, (CHAR_P, int)); @@ -1406,7 +1407,7 @@ E boolean FDECL(is_flammable, (struct obj *)); E boolean FDECL(is_rottable, (struct obj *)); E void FDECL(place_object, (struct obj *, int, int)); E void FDECL(remove_object, (struct obj *)); -E void FDECL(discard_minvent, (struct monst *)); +E void FDECL(discard_minvent, (struct monst *, BOOLEAN_P)); E void FDECL(obj_extract_self, (struct obj *)); E void FDECL(extract_nobj, (struct obj *, struct obj **)); E void FDECL(extract_nexthere, (struct obj *, struct obj **)); @@ -3037,7 +3038,7 @@ E int FDECL(tactics, (struct monst *)); E boolean FDECL(has_aggravatables, (struct monst *)); E void NDECL(aggravate); E void NDECL(clonewiz); -E int NDECL(pick_nasty); +E int FDECL(pick_nasty, (int)); E int FDECL(nasty, (struct monst *)); E void NDECL(resurrect); E void NDECL(intervene); diff --git a/src/makemon.c b/src/makemon.c index 959e54869..ef544ca2b 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 makemon.c $NHDT-Date: 1574722863 2019/11/25 23:01:03 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.142 $ */ +/* NetHack 3.6 makemon.c $NHDT-Date: 1585361050 2020/03/28 02:04:10 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.162 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -923,20 +923,20 @@ int mndx; boolean tally; boolean ghostly; { - boolean result; - uchar lim = mbirth_limit(mndx); - boolean gone = (g.mvitals[mndx].mvflags & G_GONE) != 0; /* geno'd|extinct */ + boolean gone, result; + int lim = mbirth_limit(mndx); - result = (((int) g.mvitals[mndx].born < lim) && !gone) ? TRUE : FALSE; + gone = (g.mvitals[mndx].mvflags & G_GONE) != 0; /* geno'd|extinct */ + result = ((int) g.mvitals[mndx].born < lim && !gone) ? TRUE : FALSE; /* if it's unique, don't ever make it again */ - if ((mons[mndx].geno & G_UNIQ) && mndx != PM_HIGH_PRIEST) + if ((mons[mndx].geno & G_UNIQ) != 0 && mndx != PM_HIGH_PRIEST) g.mvitals[mndx].mvflags |= G_EXTINCT; - if (g.mvitals[mndx].born < 255 && tally - && (!ghostly || (ghostly && result))) + if (g.mvitals[mndx].born < 255 && tally && (!ghostly || result)) g.mvitals[mndx].born++; - if ((int) g.mvitals[mndx].born >= lim && !(mons[mndx].geno & G_NOGEN) + if ((int) g.mvitals[mndx].born >= lim + && !(mons[mndx].geno & G_NOGEN) && !(g.mvitals[mndx].mvflags & G_EXTINCT)) { if (wizard) { debugpline1("Automatically extinguished %s.", @@ -1391,7 +1391,7 @@ int mmflags; } else { /* no initial inventory is allowed */ if (mtmp->minvent) - discard_minvent(mtmp); + discard_minvent(mtmp, TRUE); mtmp->minvent = (struct obj *) 0; /* caller expects this */ } if (ptr->mflags3 && !(mmflags & MM_NOWAIT)) { @@ -1412,6 +1412,34 @@ int mmflags; return mtmp; } +/* caller rejects makemon()'s result; always returns Null */ +struct monst * +unmakemon(mon, mmflags) +struct monst *mon; +int mmflags; +{ + boolean countbirth = ((mmflags & MM_NOCOUNTBIRTH) == 0); + int mndx = monsndx(mon->data); + + /* if count has reached the limit of 255, we don't know whether + that just happened when creating this monster or the threshold + had already been reached and further incrments were suppressed; + assume the latter */ + if (countbirth && g.mvitals[mndx].born > 0 && g.mvitals[mndx].born < 255) + g.mvitals[mndx].born -= 1; + if ((mon->data->geno & G_UNIQ) != 0) + g.mvitals[mndx].mvflags &= ~G_EXTINCT; + + mon->mhp = 0; /* let discard_minvent() know that mon isn't being kept */ + /* uncreate any artifact that the monster was provided with; unlike + mongone(), this doesn't protect special items like the Amulet + by dropping them so caller should handle them when applicable */ + discard_minvent(mon, TRUE); + + mongone(mon); + return (struct monst *) 0; +} + int mbirth_limit(mndx) int mndx; diff --git a/src/mkobj.c b/src/mkobj.c index 0248c873e..bb875ddb5 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mkobj.c $NHDT-Date: 1578895344 2020/01/13 06:02:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.174 $ */ +/* NetHack 3.6 mkobj.c $NHDT-Date: 1585361051 2020/03/28 02:04:11 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.176 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1935,11 +1935,12 @@ register struct obj *otmp; /* throw away all of a monster's inventory */ void -discard_minvent(mtmp) +discard_minvent(mtmp, uncreate_artifacts) struct monst *mtmp; +boolean uncreate_artifacts; { struct obj *otmp, *mwep = MON_WEP(mtmp); - boolean keeping_mon = (!DEADMONSTER(mtmp)); + boolean keeping_mon = !DEADMONSTER(mtmp); while ((otmp = mtmp->minvent) != 0) { /* this has now become very similar to m_useupall()... */ @@ -1953,6 +1954,8 @@ struct monst *mtmp; } otmp->owornmask = 0L; /* obfree() expects this */ } + if (uncreate_artifacts && otmp->oartifact) + artifact_exists(otmp, safe_oname(otmp), FALSE); obfree(otmp, (struct obj *) 0); /* dealloc_obj() isn't sufficient */ } } diff --git a/src/mon.c b/src/mon.c index 3fe0697b1..5567048b9 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1581886863 2020/02/16 21:01:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.324 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1585361052 2020/03/28 02:04:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.327 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2197,7 +2197,7 @@ struct monst *mdef; can't remove them from the game */ mdrop_special_objs(mdef); /* release rest of monster's inventory--it is removed from game */ - discard_minvent(mdef); + discard_minvent(mdef, FALSE); m_detach(mdef, mdef->data); } @@ -3534,11 +3534,11 @@ struct monst *mon; switch (mon->cham) { case PM_SANDESTIN: if (rn2(7)) - mndx = pick_nasty(); + mndx = pick_nasty(mons[PM_ARCHON].difficulty - 1); break; case PM_DOPPELGANGER: if (!rn2(7)) { - mndx = pick_nasty(); + mndx = pick_nasty(mons[PM_JABBERWOCK].difficulty - 1); } else if (rn2(3)) { /* role monsters */ mndx = rn1(PM_WIZARD - PM_ARCHEOLOGIST + 1, PM_ARCHEOLOGIST); } else if (!rn2(3)) { /* quest guardians */ diff --git a/src/monmove.c b/src/monmove.c index f9c168657..e88ab0464 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 monmove.c $NHDT-Date: 1580633722 2020/02/02 08:55:22 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.129 $ */ +/* NetHack 3.6 monmove.c $NHDT-Date: 1585361053 2020/03/28 02:04:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.133 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -874,16 +874,15 @@ register int after; if (hides_under(ptr) && OBJ_AT(mtmp->mx, mtmp->my) && rn2(10)) return 0; /* do not leave hiding place */ - set_apparxy(mtmp); - /* where does mtmp think you are? */ - /* Not necessary if m_move called from this file, but necessary in - * other calls of m_move (ex. leprechauns dodging) - */ + /* Where does 'mtmp' think you are? Not necessary if m_move() called + from this file, but needed for other calls of m_move(). */ + set_apparxy(mtmp); /* set mtmp->mux, mtmp->muy */ + if (!Is_rogue_level(&u.uz)) can_tunnel = tunnels(ptr); can_open = !(nohands(ptr) || verysmall(ptr)); - can_unlock = - ((can_open && monhaskey(mtmp, TRUE)) || mtmp->iswiz || is_rider(ptr)); + can_unlock = ((can_open && monhaskey(mtmp, TRUE)) + || mtmp->iswiz || is_rider(ptr)); doorbuster = is_giant(ptr); if (mtmp->wormno) goto not_special; @@ -1469,7 +1468,7 @@ register int after; add_damage(mtmp->mx, mtmp->my, 0L); } } else if (levl[mtmp->mx][mtmp->my].typ == IRONBARS) { - /* As of 3.6.2: was using may_dig() but it doesn't handle bars */ + /* 3.6.2: was using may_dig() but it doesn't handle bars */ if (!(levl[mtmp->mx][mtmp->my].wall_info & W_NONDIGGABLE) && (dmgtype(ptr, AD_RUST) || dmgtype(ptr, AD_CORR))) { if (canseemon(mtmp)) diff --git a/src/sp_lev.c b/src/sp_lev.c index a60a17c3d..f5078c7d4 100755 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1584655714 2020/03/19 22:08:34 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.181 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1585361055 2020/03/28 02:04:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.183 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -1981,7 +1981,7 @@ struct mkroom *croom; } if (m->has_invent) { - discard_minvent(mtmp); + discard_minvent(mtmp, TRUE); invent_carrying_monster = mtmp; } } diff --git a/src/wizard.c b/src/wizard.c index 0cf531ea8..e8dfa776a 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wizard.c $NHDT-Date: 1561336025 2019/06/24 00:27:05 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.56 $ */ +/* NetHack 3.6 wizard.c $NHDT-Date: 1585361057 2020/03/28 02:04:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.64 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -41,7 +41,7 @@ static NEARDATA const int nasties[] = { PM_SILVER_DRAGON, PM_ORANGE_DRAGON, PM_GREEN_DRAGON, PM_YELLOW_DRAGON, PM_GUARDIAN_NAGA, PM_FIRE_GIANT, PM_ALEAX, PM_COUATL, PM_HORNED_DEVIL, PM_BARBED_DEVIL, - /* (titans, ki-rin, and golden nagas are suitably nasty, but + /* (Archons, titans, ki-rin, and golden nagas are suitably nasty, but they're summoners so would aggravate excessive summoning) */ }; @@ -512,9 +512,10 @@ clonewiz() /* also used by newcham() */ int -pick_nasty() +pick_nasty(difcap) +int difcap; /* if non-zero, try to make difficulty be lower than this */ { - int res = nasties[rn2(SIZE(nasties))]; + int alt, res = nasties[rn2(SIZE(nasties))]; /* To do? Possibly should filter for appropriate forms when * in the elemental planes or surrounded by water or lava. @@ -526,6 +527,33 @@ pick_nasty() && !('A' <= mons[res].mlet && mons[res].mlet <= 'Z')) res = nasties[rn2(SIZE(nasties))]; + /* if genocided or too difficult or out of place, try a substitute + when a suitable one exists + arch-lich -> master lich, + master mind flayer -> mind flayer, + but the substitutes are likely to be genocided too */ + alt = res; + if ((g.mvitals[res].mvflags & G_GENOD) != 0 + || (difcap > 0 && mons[res].difficulty >= difcap) + /* note: nasty() -> makemon() ignores G_HELL|G_NOHELL; + arch-lich and master lich are both flagged as hell-only; + this filtering demotes arch-lich to master lich when + outside of Gehennom (unless the latter has been genocided) */ + || (mons[res].geno & (Inhell ? G_NOHELL : G_HELL)) != 0) + alt = big_to_little(res); + if (alt != res && (g.mvitals[alt].mvflags & G_GENOD) == 0) { + const char *mname = mons[alt].mname, + *lastspace = rindex(mname, ' '); + + /* only non-juveniles can become alternate choice */ + if (strncmp(mname, "baby ", 5) + && (!lastspace + || (strcmp(lastspace, " hatchling") + && strcmp(lastspace, " pup") + && strcmp(lastspace, " cub")))) + res = alt; + } + return res; } @@ -540,11 +568,10 @@ int nasty(summoner) struct monst *summoner; { - register struct monst *mtmp; - register int i, j; - int castalign = (summoner ? sgn(summoner->data->maligntyp) : 0); + struct monst *mtmp; coord bypos; - int count, census, tmp, makeindex, s_cls, m_cls; + int i, j, count, census, tmp, makeindex, + s_cls, m_cls, difcap, trylimit, castalign; #define MAXNASTIES 10 /* more than this can be created */ @@ -558,12 +585,14 @@ struct monst *summoner; } else { count = 0; s_cls = summoner ? summoner->data->mlet : 0; + difcap = summoner ? summoner->data->difficulty : 0; /* spellcasters */ + castalign = summoner ? sgn(summoner->data->maligntyp) : 0; tmp = (u.ulevel > 3) ? u.ulevel / 3 : 1; /* if we don't have a casting monster, nasties appear around hero, otherwise they'll appear around spot summoner thinks she's at */ bypos.x = u.ux; bypos.y = u.uy; - for (i = rnd(tmp); i > 0 && count < MAXNASTIES; --i) + for (i = rnd(tmp); i > 0 && count < MAXNASTIES; --i) { /* Of the 42 nasties[], 10 are lawful, 14 are chaotic, * and 18 are neutral. * @@ -585,15 +614,16 @@ struct monst *summoner; /* Don't create more spellcasters of the monsters' level or * higher--avoids chain summoners filling up the level. */ + trylimit = 10 + 1; /* 10 tries */ do { - makeindex = pick_nasty(); + if (!--trylimit) + goto nextj; /* break this loop, continue outer one */ + makeindex = pick_nasty(difcap); m_cls = mons[makeindex].mlet; - } while (summoner - && ((attacktype(&mons[makeindex], AT_MAGC) - && mons[makeindex].difficulty - >= mons[summoner->mnum].difficulty) - || (s_cls == S_DEMON && m_cls == S_ANGEL) - || (s_cls == S_ANGEL && m_cls == S_DEMON))); + } while ((difcap > 0 && mons[makeindex].difficulty >= difcap + && attacktype(&mons[makeindex], AT_MAGC)) + || (s_cls == S_DEMON && m_cls == S_ANGEL) + || (s_cls == S_ANGEL && m_cls == S_DEMON)); /* do this after picking the monster to place */ if (summoner && !enexto(&bypos, summoner->mux, summoner->muy, &mons[makeindex])) @@ -604,18 +634,46 @@ struct monst *summoner; NO_MM_FLAGS)) != 0) { mtmp->msleeping = mtmp->mpeaceful = mtmp->mtame = 0; set_malign(mtmp); - } else /* random monster to substitute for geno'd selection */ - mtmp = makemon((struct permonst *) 0, bypos.x, bypos.y, - NO_MM_FLAGS); + } else { + /* random monster to substitute for geno'd selection; + unlike direct choice, not forced to be hostile [why?]; + limit spellcasters to inhibit chain summoning */ + if ((mtmp = makemon((struct permonst *) 0, + bypos.x, bypos.y, + NO_MM_FLAGS)) != 0) { + m_cls = mtmp->data->mlet; + if ((difcap > 0 && mtmp->data->difficulty >= difcap + && attacktype(mtmp->data, AT_MAGC)) + || (s_cls == S_DEMON && m_cls == S_ANGEL) + || (s_cls == S_ANGEL && m_cls == S_DEMON)) + mtmp = unmakemon(mtmp, NO_MM_FLAGS); /* Null */ + } + } + if (mtmp) { + /* create at most one arch-lich or Archon regardless + of who is doing the summoning (note: Archon is + not in nasties[] but could be chosen as random + replacement for a genocided selection) */ + if (mtmp->data == &mons[PM_ARCH_LICH] + || mtmp->data == &mons[PM_ARCHON]) { + tmp = min(mons[PM_ARCHON].difficulty, /* A:26 */ + mons[PM_ARCH_LICH].difficulty); /* L:31 */ + if (!difcap || difcap > tmp) + difcap = tmp; /* rest must be lower difficulty */ + } /* delay first use of spell or breath attack */ mtmp->mspec_used = rnd(4); + if (++count >= MAXNASTIES || mtmp->data->maligntyp == 0 || sgn(mtmp->data->maligntyp) == castalign) break; } - } + nextj: + ; /* empty; label must be followed by a statement */ + } /* for j */ + } /* for i */ } if (count)