-/* NetHack 3.6 wizard.c $NHDT-Date: 1455934524 2016/02/20 02:15:24 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.46 $ */
+/* NetHack 3.6 wizard.c $NHDT-Date: 1456185030 2016/02/22 23:50:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.47 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
STATIC_DCL unsigned long FDECL(target_on, (int, struct monst *));
STATIC_DCL unsigned long FDECL(strategy, (struct monst *));
+/* adding more neutral creatures will tend to reduce the number of monsters
+ summoned by nasty(); adding more lawful creatures will reduce the number
+ of monsters summoned by lawfuls; adding more chaotic creatures will reduce
+ the number of monsters summoned by chaotics; prior to 3.6.1, there were
+ only four lawful candidates, so lawful summoners tended to summon more
+ (trying to get lawful or neutral but obtaining chaotic instead) than
+ their chaotic counterparts */
static NEARDATA const int nasties[] = {
- PM_COCKATRICE, PM_ETTIN, PM_STALKER, PM_MINOTAUR, PM_RED_DRAGON,
- PM_BLACK_DRAGON, PM_GREEN_DRAGON, PM_OWLBEAR, PM_PURPLE_WORM,
- PM_ROCK_TROLL, PM_XAN, PM_GREMLIN, PM_UMBER_HULK, PM_VAMPIRE_LORD,
- PM_XORN, PM_ZRUTY, PM_ELF_LORD, PM_ELVENKING, PM_YELLOW_DRAGON,
- PM_LEOCROTTA, PM_BALUCHITHERIUM, PM_CARNIVOROUS_APE, PM_FIRE_GIANT,
- PM_COUATL, PM_CAPTAIN, PM_WINGED_GARGOYLE, PM_MASTER_MIND_FLAYER,
- PM_FIRE_ELEMENTAL, PM_JABBERWOCK, PM_ARCH_LICH, PM_OGRE_KING, PM_OLOG_HAI,
- PM_IRON_GOLEM, PM_OCHRE_JELLY, PM_GREEN_SLIME, PM_DISENCHANTER
+ /* neutral */
+ PM_COCKATRICE, PM_ETTIN, PM_STALKER, PM_MINOTAUR,
+ PM_OWLBEAR, PM_PURPLE_WORM, PM_XAN, PM_UMBER_HULK,
+ PM_XORN, PM_ZRUTY, PM_LEOCROTTA, PM_BALUCHITHERIUM,
+ PM_CARNIVOROUS_APE, PM_FIRE_ELEMENTAL, PM_JABBERWOCK,
+ PM_IRON_GOLEM, PM_OCHRE_JELLY, PM_GREEN_SLIME,
+ /* chaotic */
+ PM_BLACK_DRAGON, PM_RED_DRAGON, PM_ARCH_LICH, PM_VAMPIRE_LORD,
+ PM_MASTER_MIND_FLAYER, PM_DISENCHANTER, PM_WINGED_GARGOYLE,
+ PM_STORM_GIANT, PM_OLOG_HAI, PM_ELF_LORD, PM_ELVENKING,
+ PM_OGRE_KING, PM_CAPTAIN, PM_GREMLIN,
+ /* lawful */
+ 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
+ they're summoners so would aggravate excessive summoning) */
};
static NEARDATA const unsigned wizapp[] = {
PM_HUMAN, PM_WATER_DEMON, PM_VAMPIRE, PM_RED_DRAGON,
PM_TROLL, PM_UMBER_HULK, PM_XORN, PM_XAN,
- PM_COCKATRICE, PM_FLOATING_EYE, PM_GUARDIAN_NAGA, PM_TRAPPER
+ PM_COCKATRICE, PM_FLOATING_EYE, PM_GUARDIAN_NAGA, PM_TRAPPER,
};
/* If you've found the Amulet, make the Wizard appear after some time */
}
/* create some nasty monsters, aligned with the caster or neutral; chaotic
- and unaligned are treated as equivalent; default caster is chaotic */
+ and unaligned are treated as equivalent; if summoner is Null, this is
+ for late-game harassment (after the Wizard has been killed at least once
+ or the invocation ritual has been performed), in which case we treat
+ 'summoner' as neutral, since that will produce the greatest number of
+ creatures on average (in 3.6.0 and earlier, Null was treated as chaotic);
+ returns the number of monsters created */
int
-nasty(mcast)
-struct monst *mcast;
+nasty(summoner)
+struct monst *summoner;
{
register struct monst *mtmp;
register int i, j;
- int castalign = (mcast ? sgn(mcast->data->maligntyp) : -1);
+ int castalign = (summoner ? sgn(summoner->data->maligntyp) : 0);
coord bypos;
- int count, census, tmp;
+ int count, census, tmp, makeindex, s_cls, m_cls;
-#define MAXNASTIES 8 /* more than this can be created if any come in groups */
+#define MAXNASTIES 10 /* more than this can be created */
/* some candidates may be created in groups, so simple count
of non-null makemon() return is inadequate */
census = monster_census(FALSE);
if (!rn2(10) && Inhell) {
+ /* this might summon a demon prince or lord */
count = msummon((struct monst *) 0); /* summons like WoY */
} else {
count = 0;
- tmp = (u.ulevel > 3) ? u.ulevel / 3 : 1; /* just in case -- rph */
- /* if we don't have a casting monster, nasties appear around you */
+ s_cls = summoner ? summoner->data->mlet : 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; --i)
+ for (i = rnd(tmp); i > 0 && count < MAXNASTIES; --i)
+ /* Of the 42 nasties[], 10 are lawful, 14 are chaotic,
+ * and 18 are neutral.
+ *
+ * Neutral caster, used for late-game harrassment,
+ * has 18/42 chance to stop the inner loop on each
+ * critter, 24/42 chance for another iteration.
+ * Lawful caster has 28/42 chance to stop unless the
+ * summoner is an angel or demon, in which case the
+ * chance is 26/42.
+ * Chaotic or unaligned caster has 32/42 chance to
+ * stop, so will summon fewer creatures on average.
+ *
+ * The outer loop potentially gives chaotic/unaligned
+ * a chance to even things up since others will hit
+ * MAXNASTIES sooner, but its number of iterations is
+ * randomized so it won't always do so.
+ */
for (j = 0; j < 20; j++) {
- int makeindex;
-
/* Don't create more spellcasters of the monsters' level or
* higher--avoids chain summoners filling up the level.
*/
do {
makeindex = pick_nasty();
- } while (mcast && attacktype(&mons[makeindex], AT_MAGC)
- && monstr[makeindex] >= monstr[mcast->mnum]);
+ m_cls = mons[makeindex].mlet;
+ } while (summoner
+ && ((attacktype(&mons[makeindex], AT_MAGC)
+ && monstr[makeindex] >= monstr[summoner->mnum])
+ || (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 (mcast && !enexto(&bypos, mcast->mux, mcast->muy,
- &mons[makeindex]))
+ if (summoner && !enexto(&bypos, summoner->mux, summoner->muy,
+ &mons[makeindex]))
continue;
+ /* this honors genocide but overrides extinction; it ignores
+ inside-hell-only (G_HELL) & outside-hell-only (G_NOHELL) */
if ((mtmp = makemon(&mons[makeindex], bypos.x, bypos.y,
NO_MM_FLAGS)) != 0) {
mtmp->msleeping = mtmp->mpeaceful = mtmp->mtame = 0;
set_malign(mtmp);
- } else /* GENOD? */
+ } else /* random monster to substitute for geno'd selection */
mtmp = makemon((struct permonst *) 0, bypos.x, bypos.y,
NO_MM_FLAGS);
if (mtmp) {
+ /* 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)