diff --git a/src/wizard.c b/src/wizard.c index d5237a6d9..2f7d2e7e4 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -1,4 +1,4 @@ -/* 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. */ @@ -20,21 +20,37 @@ STATIC_DCL boolean FDECL(you_have, (int)); 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 */ @@ -506,54 +522,85 @@ pick_nasty() } /* 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)