-/* NetHack 3.6 makemon.c $NHDT-Date: 1542798623 2018/11/21 11:10:23 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.128 $ */
+/* NetHack 3.6 makemon.c $NHDT-Date: 1544998885 2018/12/16 22:21:25 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.131 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
}
/* Make one of the multiple types of a given monster class.
- * The second parameter specifies a special casing bit mask
- * to allow the normal genesis masks to be deactivated.
- * Returns Null if no monsters in that class can be made.
- */
+ The second parameter specifies a special casing bit mask
+ to allow the normal genesis masks to be deactivated.
+ Returns Null if no monsters in that class can be made. */
struct permonst *
mkclass(class, spc)
char class;
int spc;
+{
+ return mkclass_aligned(class, spc, A_NONE);
+}
+
+/* mkclass() with alignment restrictions; used by ndemon() */
+struct permonst *
+mkclass_aligned(class, spc, atyp)
+char class;
+int spc;
+aligntyp atyp;
{
register int first, last, num = 0;
+ int k, nums[SPECIAL_PM + 1]; /* +1: insurance for final return value */
int maxmlev, mask = (G_NOGEN | G_UNIQ) & ~spc;
+ (void) memset((genericptr_t) nums, 0, sizeof nums);
maxmlev = level_difficulty() >> 1;
if (class < 1 || class >= MAXMCLASSES) {
impossible("mkclass called with bad class!");
return (struct permonst *) 0;
}
/* Assumption #1: monsters of a given class are contiguous in the
- * mons[] array.
+ * mons[] array. Player monsters and quest denizens
+ * are an exception; mkclass() won't pick them.
+ * SPECIAL_PM is long worm tail and separates the
+ * regular monsters from the exceptions.
*/
for (first = LOW_PM; first < SPECIAL_PM; first++)
if (mons[first].mlet == class)
break;
- if (first == SPECIAL_PM)
+ if (first == SPECIAL_PM) {
+ impossible("mkclass found no class %d monsters", class);
return (struct permonst *) 0;
+ }
- for (last = first; last < SPECIAL_PM && mons[last].mlet == class; last++)
+ /* Assumption #2: monsters of a given class are presented in ascending
+ * order of strength.
+ */
+ for (last = first; last < SPECIAL_PM && mons[last].mlet == class; last++) {
+ if (atyp != A_NONE && sgn(mons[last].maligntyp) != sgn(atyp))
+ continue;
if (mk_gen_ok(last, G_GONE, mask)) {
- /* consider it */
+ /* 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
+ 'num' implies last > first so mons[last-1] is safe);
+ sometimes accept it even if high difficulty */
if (num && toostrong(last, maxmlev)
- && mons[last].difficulty != mons[last - 1].difficulty && rn2(2))
+ && mons[last].difficulty > mons[last - 1].difficulty
+ && rn2(2))
break;
- num += mons[last].geno & G_FREQ;
+ if ((k = (mons[last].geno & G_FREQ)) > 0) {
+ /* skew towards lower value monsters at lower exp. levels
+ (this used to be done in the next loop, but that didn't
+ work well when multiple species had the same level and
+ were followed by one that was past the bias threshold;
+ cited example was sucubus and incubus, where the bias
+ against picking the next demon resulted in incubus
+ being picked nearly twice as often as sucubus);
+ we need the '+1' in case the entire set is too high
+ level (really low level hero) */
+ nums[last] = k + 1 - (adj_lev(&mons[last]) > (u.ulevel * 2));
+ num += nums[last];
+ }
}
+ }
if (!num)
return (struct permonst *) 0;
- /* Assumption #2: monsters of a given class are presented in ascending
- * order of strength.
- */
- for (num = rnd(num); num > 0; first++)
- if (mk_gen_ok(first, G_GONE, mask)) {
- /* skew towards lower value monsters at lower exp. levels */
- num -= mons[first].geno & G_FREQ;
- if (num && adj_lev(&mons[first]) > (u.ulevel * 2)) {
- /* but not when multiple monsters are same level */
- if (mons[first].mlevel != mons[first + 1].mlevel)
- num--;
- }
- }
- first--; /* correct an off-by-one error */
+ /* the hard work has already been done; 'num' should hit 0 before
+ first reaches last (which is actually one past our last candidate) */
+ for (num = rnd(num); first < last; first++)
+ if ((num -= nums[first]) <= 0)
+ break;
- return &mons[first];
+ return nums[first] ? &mons[first] : (struct permonst *) 0;
}
/* like mkclass(), but excludes difficulty considerations; used when
-/* NetHack 3.6 minion.c $NHDT-Date: 1432512773 2015/05/25 00:12:53 $ $NHDT-Branch: master $:$NHDT-Revision: 1.33 $ */
+/* NetHack 3.6 minion.c $NHDT-Date: 1544998886 2018/12/16 22:21:26 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.40 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2008. */
/* NetHack may be freely redistributed. See license for details. */
flags.female ? "Sister" : "Brother");
if (!tele_restrict(mtmp))
(void) rloc(mtmp, TRUE);
- return (1);
+ return 1;
}
cash = money_cnt(invent);
demand =
}
}
mongone(mtmp);
- return (1);
+ return 1;
}
long
}
(void) money2mon(mtmp, offer);
context.botl = 1;
- return (offer);
+ return offer;
}
int
pm = rn1(PM_DEMOGORGON + 1 - PM_ORCUS, PM_ORCUS);
if (!(mvitals[pm].mvflags & G_GONE)
&& (atyp == A_NONE || sgn(mons[pm].maligntyp) == sgn(atyp)))
- return (pm);
+ return pm;
}
- return (dlord(atyp)); /* approximate */
+ return dlord(atyp); /* approximate */
}
int
pm = rn1(PM_YEENOGHU + 1 - PM_JUIBLEX, PM_JUIBLEX);
if (!(mvitals[pm].mvflags & G_GONE)
&& (atyp == A_NONE || sgn(mons[pm].maligntyp) == sgn(atyp)))
- return (pm);
+ return pm;
}
- return (ndemon(atyp)); /* approximate */
+ return ndemon(atyp); /* approximate */
}
/* create lawful (good) lord */
llord()
{
if (!(mvitals[PM_ARCHON].mvflags & G_GONE))
- return (PM_ARCHON);
+ return PM_ARCHON;
- return (lminion()); /* approximate */
+ return lminion(); /* approximate */
}
int
for (tryct = 0; tryct < 20; tryct++) {
ptr = mkclass(S_ANGEL, 0);
if (ptr && !is_lord(ptr))
- return (monsndx(ptr));
+ return monsndx(ptr);
}
return NON_PM;
int
ndemon(atyp)
-aligntyp atyp;
+aligntyp atyp; /* A_NONE is used for 'any alignment' */
{
- int tryct;
struct permonst *ptr;
- for (tryct = 0; tryct < 20; tryct++) {
- ptr = mkclass(S_DEMON, 0);
- if (ptr && is_ndemon(ptr)
- && (atyp == A_NONE || sgn(ptr->maligntyp) == sgn(atyp)))
- return (monsndx(ptr));
- }
-
- return NON_PM;
+ /*
+ * 3.6.2: [fix #H2204, 22-Dec-2010, eight years later...]
+ * pick a correctly aligned demon in one try. This used to
+ * use mkclass() to choose a random demon type and keep trying
+ * (up to 20 times) until it got one with the desired alignment.
+ * mkclass_aligned() skips wrongly aligned potential candidates.
+ * [The only neutral demons are djinni and mail daemon and
+ * mkclass() won't pick them, but call it anyway in case either
+ * aspect of that changes someday.]
+ */
+#if 0
+ if (atyp == A_NEUTRAL)
+ return NON_PM;
+#endif
+ ptr = mkclass_aligned(S_DEMON, 0, atyp);
+ return (ptr && is_ndemon(ptr)) ? monsndx(ptr) : NON_PM;
}
/* guardian angel has been affected by conflict so is abandoning hero */