Monsters & threat filter
3.6 · 3.7 · 5.0
Every row here is read out of the monster tables in the NetHack source —
include/monsters.h for 3.7 and 5.0, src/monst.c for 3.6 —
by scripts/gen-monsters.py. The threat filter is a direct port of
rndmonst() in src/makemon.c: the same difficulty window, the
same G_NOGEN/G_UNIQ/Gehennom exclusions, and the same
generation weights, so the percentages below are the game's actual selection
probabilities and not an estimate.
level_difficulty() is normally just the depth of the level, so the
dungeon level field is that depth. It is not your depth if you are carrying
the Amulet (then it is the deepest level you ever reached) or in the endgame. The
level alignment shifts every monster's weight by align_shift(); ordinary
dungeon levels are unaligned, so leave it there unless you are somewhere special.
The exact rule, from rndmonst()
Every time the game wants a random monster it walks
mons[] from the first entry up to but not including
SPECIAL_PM (the long worm tail; everything from there on is quest
and endgame material that is never generated randomly) and keeps the entries
that pass all of:
difficulty >= level_difficulty / 6— the too-weak cutoff. This is why sewer rats stop showing up.difficulty <= (level_difficulty + experience_level) / 2— the ceiling. Note your experience level is half of it: levelling up is what lets nastier things appear, not just going deeper.- not
G_NOGENand notG_UNIQ. - outside Gehennom, not
G_HELL; inside Gehennom, notG_NOHELL, and only monsters whose alignment is 0 or negative.
Each survivor gets a weight of
(geno & G_FREQ) + align_shift(), plus 3 in 3.7/5.0 if the level
has a temperature the monster resists. A weight of zero means it is never picked.
One survivor is then chosen with probability weight ÷ total weight — 3.6 walks a
running total, 3.7 and 5.0 use weighted reservoir sampling, which gives the same
distribution.
What this page cannot know: whether you have
genocided or extinct-ed something (mvitals[]), the quest branch's own
monster list (qt_montype(), used 6 times out of 7 there), the Rogue
level's uppercase-only filter, the elemental planes, and
goodpos() rejecting a monster that cannot survive on the square that
was picked. Special levels and Sokoban also place monsters directly. Treat the
list as "what the random generator may hand you", not as everything you will meet.
Where difficulty comes from
Attack severity colours
The colour on an attack is keyed off its AD_*
damage type, which is a fact from the table — not a judgement about the monster.
red marks damage types that can end a game outright
or take something back permanently: petrification, sliming, disintegration, level
drain, digestion, and the Riders' touches.
orange marks damage that costs you equipment,
stats, items or control: disenchantment, corrosion and rust, seduction and theft,
paralysis, the stat drains, lycanthropy, teleport, polymorph.
yellow marks the merely inconvenient — stun,
confusion, blindness, sleep, sticking.