-$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
-----------------------------------
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
-/* 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. */
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));
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 **));
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);
-/* 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. */
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.",
} 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)) {
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;
-/* 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. */
/* 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()... */
}
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 */
}
}
-/* 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. */
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);
}
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 */
-/* 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. */
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;
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))
-/* 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. */
}
if (m->has_invent) {
- discard_minvent(mtmp);
+ discard_minvent(mtmp, TRUE);
invent_carrying_monster = mtmp;
}
}
-/* 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. */
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) */
};
/* 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.
&& !('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;
}
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 */
} 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.
*
/* 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]))
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)