Skip to content

osrlib.core.monsters

Monster templates, instances, spawning, and the entity ID allocator.

The 138 SRD monster pages compile into monsters.json (packed-variant pages expand to one concrete entry per variant, because a frozen template must be spawnable) and load as frozen MonsterTemplate models via load_monsters. Play spawns mutable MonsterInstances from frozen templates with spawn_monster, so shared template data can never be damaged by play: load the catalog once, then spawn one instance per creature.

Ability bullets compile as structured tags plus the SRD prose (mirroring ClassAbility): the tags the kernel executes (regeneration, energy_drain, poison, paralysis, petrification, breath_weapon, gaze, disease, uses_fire) carry structured params the engine reads directly; everything else compiles with manual=True and stays prose the kernel doesn't execute. Defenses the damage pipeline checks at damage time compile into the structured Defenses shape while the bullets keep the prose.

Spawned hit points draw from the MONSTER_SPAWN_STREAM stream, a module-level constant, so a combat-rules change never shifts spawned hit points in a fixed scenario.

Part of the core kernel, alongside osrlib.core.combat, which the spawned instances feed as combatants.

MONSTER_SPAWN_STREAM module-attribute

MONSTER_SPAWN_STREAM = 'monster_spawn'

Stream key convention for monster spawning draws: hit point rolls.

AcAlternate

Bases: BaseModel

An alternate armour class with its printed condition (9 [10] in human form).

ac instance-attribute

ac: int

ac_ascending instance-attribute

ac_ascending: int

condition class-attribute instance-attribute

condition: str = ''

AlignmentSpec

Bases: BaseModel

A monster's alignment options — compound alignments compile to options.

Chaotic is one option; Lawful or Neutral is two; Any is all three, with usual carrying Any, usually Lawful.

options class-attribute instance-attribute

options: tuple[Alignment, ...] = Field(min_length=1)

usual class-attribute instance-attribute

usual: Alignment | None = None

AttackRoutine

Bases: BaseModel

One alternative attack routine — a monster acts with one routine per round.

attacks class-attribute instance-attribute

attacks: tuple[MonsterAttack, ...] = Field(min_length=1)

DamageKey

Bases: StrEnum

The damage-source keys a harmed_only_by gate or reduction can name.

holy is carried by holy water's combat facet: an undead target admits holy damage through any harmed_only_by gate — the specific rule ("holy water inflicts damage on undead monsters") overrides the general immunity, otherwise the wight's silver-or-magic gate would absorb the one weapon made for it.

SILVER class-attribute instance-attribute

SILVER = 'silver'

MAGIC class-attribute instance-attribute

MAGIC = 'magic'

FIRE class-attribute instance-attribute

FIRE = 'fire'

COLD class-attribute instance-attribute

COLD = 'cold'

HOLY class-attribute instance-attribute

HOLY = 'holy'

DamageReduction

Bases: BaseModel

A damage reduction applied after the roll: divide (floor, minimum 1).

Empty keys means the reduction applies to every source that passes the harmed_only_by gate (the mummy's "all damage reduced by half"); named keys restrict it (the wraith's half damage from silver weapons).

keys class-attribute instance-attribute

keys: tuple[DamageKey, ...] = ()

divisor class-attribute instance-attribute

divisor: int = Field(default=2, ge=2)

Defenses

Bases: BaseModel

The structured defense shape combat checks at damage time.

harmed_only_by is the weapon-material gate (empty means no gate): a source must carry at least one listed key or the hit is absorbed with no damage rolled. energy maps elements to defenses; condition_immunities names conditions the creature can never gain (the undead poison/mind immunity).

harmed_only_by class-attribute instance-attribute

harmed_only_by: tuple[DamageKey, ...] = ()

reductions class-attribute instance-attribute

reductions: tuple[DamageReduction, ...] = ()

energy class-attribute instance-attribute

energy: dict[Element, EnergyDefense] = {}

condition_immunities class-attribute instance-attribute

condition_immunities: tuple[Condition, ...] = ()

Element

Bases: StrEnum

Energy elements that appear in monster attacks, breath weapons, and defenses.

FIRE class-attribute instance-attribute

FIRE = 'fire'

COLD class-attribute instance-attribute

COLD = 'cold'

LIGHTNING class-attribute instance-attribute

LIGHTNING = 'lightning'

ACID class-attribute instance-attribute

ACID = 'acid'

GAS class-attribute instance-attribute

GAS = 'gas'

POISON class-attribute instance-attribute

POISON = 'poison'

STEAM class-attribute instance-attribute

STEAM = 'steam'

EnergyDefense

Bases: BaseModel

An elemental defense, checked by the damage pipeline.

The SRD's forms pin to two fields: immunity is "all" (a giant is "unharmed by fire", magical or not) or "nonmagical" (a red dragon is immune to its own breath and to flaming oil, but not to fire ball); auto_save_magical treats saving throws against magical forms of the element as automatically passed (the dragons' "automatically save versus similar attack forms").

immunity instance-attribute

immunity: Literal['all', 'nonmagical']

auto_save_magical class-attribute instance-attribute

auto_save_magical: bool = False

IdAllocator

Bases: BaseModel

A monotonic per-prefix entity ID counter (monster-0001, effect-0001).

A GameSession adopts one instance so every entity it creates — monsters, effects, NPCs, valuables — gets a unique id; standalone callers can create their own instead. Serializable — the counters are plain state.

counters class-attribute instance-attribute

counters: dict[str, int] = {}

allocate

allocate(prefix: str) -> str

Return the next id for prefix.

Parameters:

Name Type Description Default
prefix str

The entity kind, e.g. "monster" or "effect".

required

Returns:

Type Description
str

The allocated id, {prefix}-{n:04d} with n starting at 1.

MonsterAbility

Bases: BaseModel

A structured monster-ability tag plus the SRD prose it came from.

params carries the mechanizable values the compiler fixes (the troll's regeneration delay and rate, a breath weapon's shape and element); manual marks abilities the kernel doesn't execute — games and narrators present the prose.

tag class-attribute instance-attribute

tag: str = Field(min_length=1)

name class-attribute instance-attribute

name: str = Field(min_length=1)

prose instance-attribute

prose: str

manual class-attribute instance-attribute

manual: bool = False

params class-attribute instance-attribute

params: dict[str, int | str | bool | tuple[int | str, ...]] = {}

MonsterAttack

Bases: BaseModel

One attack within a routine: count × name (damage + effects).

damage is a dice-grammar expression; fixed_damage covers flat forms (1hp); fixed_damage_options covers printed alternatives (the insect swarm's 2 or 4hp, armour-dependent per its prose, which stays manual). by_weapon marks or by weapon forms, with the printed modifier. effects are the effect keywords from the damage parens (poison, paralysis, energy_drain, charm, ...) compiled to tags on the attack.

count class-attribute instance-attribute

count: int = Field(default=1, ge=1)

name class-attribute instance-attribute

name: str = Field(min_length=1)

damage class-attribute instance-attribute

damage: str | None = None

fixed_damage class-attribute instance-attribute

fixed_damage: int | None = None

fixed_damage_options class-attribute instance-attribute

fixed_damage_options: tuple[int, ...] = ()

by_weapon class-attribute instance-attribute

by_weapon: bool = False

by_weapon_modifier class-attribute instance-attribute

by_weapon_modifier: int = 0

effects class-attribute instance-attribute

effects: tuple[str, ...] = ()

MonsterCatalog

Bases: BaseModel

The loaded monster list, with id lookup.

monsters instance-attribute

monsters: tuple[MonsterTemplate, ...]

get

get(monster_id: str) -> MonsterTemplate

Return the monster template for monster_id.

Parameters:

Name Type Description Default
monster_id str

A monster id from load_monsters — see the monster id index, e.g. "troll" or "red_dragon".

required

Returns:

Type Description
MonsterTemplate

The monster template.

Raises:

Type Description
ValueError

If no monster has that id.

MonsterHitDice

Bases: BaseModel

A monster's Hit Dice, exactly as the stat block prints them.

die is 8 unless fractional (½ compiles as 1d4); modifier is signed (1-1 is −1). asterisks is the special-ability count — XP data, not noise. fixed_hp forms (1hp, the hydra's 8 hp per HD) roll nothing; count is 0 for pure fixed-hp forms. The modifier drives the attack-matrix "1 HD higher" rule and the negative-modifier XP-band mapping (see osrlib.core.tables).

count class-attribute instance-attribute

count: int = Field(default=0, ge=0)

die class-attribute instance-attribute

die: int = 8

modifier class-attribute instance-attribute

modifier: int = 0

asterisks class-attribute instance-attribute

asterisks: int = Field(default=0, ge=0)

average_hp class-attribute instance-attribute

average_hp: int | None = None

fixed_hp class-attribute instance-attribute

fixed_hp: int | None = None

MonsterInstance

Bases: BaseModel

A mutable monster spawned from a frozen template.

Exposes the same combatant surface as Character (THAC0, attack bonus, AC both ways, saves, conditions, stat modifiers), so combat functions take either. nonregen_damage is the troll's non-regenerable damage ledger (fire and acid accrue here; regeneration never heals it, and the troll is permanently dead only when this ledger alone reaches max HP). last_damaged_round feeds regeneration's damage delay; breath_uses_today tracks the dragons' three-per-day limit. alignment is the operative alignment resolved at spawn (a multi-option AlignmentSpec alone can't answer protection from evil's ward gate); None means unresolved, which the ward treats as differing.

id instance-attribute

id: str

template instance-attribute

template: MonsterTemplate

max_hp class-attribute instance-attribute

max_hp: int = Field(ge=1)

current_hp class-attribute instance-attribute

current_hp: int = Field(ge=0)

conditions class-attribute instance-attribute

conditions: tuple[ActiveCondition, ...] = ()

stat_modifiers class-attribute instance-attribute

stat_modifiers: tuple[ActiveModifier, ...] = ()

alignment class-attribute instance-attribute

alignment: Alignment | None = None

nonregen_damage class-attribute instance-attribute

nonregen_damage: int = Field(default=0, ge=0)

last_damaged_round class-attribute instance-attribute

last_damaged_round: int | None = None

breath_uses_today class-attribute instance-attribute

breath_uses_today: int = Field(default=0, ge=0)

drained_hd class-attribute instance-attribute

drained_hd: int = Field(default=0, ge=0)

name property

name: str

The template's name.

hit_dice_count property

hit_dice_count: int

The instance's current Hit Dice count: the template's minus any drained.

thac0 property

thac0: int

The printed THAC0 (already reflecting the bonus-hit-points 1-HD-higher rule).

A drained instance re-derives from its reduced Hit Dice via the attack matrix rows.

attack_bonus property

attack_bonus: int

The printed ascending-AC attack bonus; drained instances re-derive.

armour_class property

armour_class: int | None

Descending AC; None when no hit roll is required.

armour_class_ascending property

armour_class_ascending: int | None

Ascending AC; None when no hit roll is required.

saves property

saves: SavingThrows

The stat block's saving throw values; drained instances re-derive.

A drained instance reads the monster saving-throw band for its reduced Hit Dice.

melee_modifier property

melee_modifier: int

Monsters' attack and damage rolls are not modified by STR (RAW).

missile_modifier property

missile_modifier: int

Monsters' attack rolls are not modified by DEX (RAW).

initiative_modifier property

initiative_modifier: int

Monsters take a caller-supplied initiative modifier; the intrinsic one is 0.

MonsterSaves

Bases: BaseModel

A monster's saving throws: the five values plus the printed save-as note.

save_as keeps the stat block's parenthetical ("2", "NH", "Cleric 1", "F1 to F3") for provenance and validation against the monster save bands.

values instance-attribute

values: SavingThrows

save_as instance-attribute

save_as: str

MonsterTemplate

Bases: BaseModel

A monster stat block, compiled from its SRD page.

Frozen SRD data: play never mutates a template. page is the source-page grouping (variants of one page stay associable); attack_roll_required is False for the No hit roll required AC sentinel (attacks auto-hit). xp is the printed value, cross-validated against the XP-awards table at compile time.

id instance-attribute

id: str

name instance-attribute

name: str

page instance-attribute

page: str

intro class-attribute instance-attribute

intro: str = ''

ac class-attribute instance-attribute

ac: int | None = None

ac_ascending class-attribute instance-attribute

ac_ascending: int | None = None

ac_alternates class-attribute instance-attribute

ac_alternates: tuple[AcAlternate, ...] = ()

attack_roll_required class-attribute instance-attribute

attack_roll_required: bool = True

hit_dice instance-attribute

hit_dice: MonsterHitDice

attacks class-attribute instance-attribute

attacks: tuple[AttackRoutine, ...] = ()

thac0 class-attribute instance-attribute

thac0: int = Field(ge=2, le=20)

attack_bonus class-attribute instance-attribute

attack_bonus: int = Field(ge=-1)

movement class-attribute instance-attribute

movement: tuple[MovementMode, ...] = Field(min_length=1)

saves instance-attribute

saves: MonsterSaves

morale class-attribute instance-attribute

morale: int | None = Field(default=None, ge=2, le=12)

morale_alternates class-attribute instance-attribute

morale_alternates: tuple[MoraleAlternate, ...] = ()

alignment instance-attribute

alignment: AlignmentSpec

xp class-attribute instance-attribute

xp: int = Field(ge=0)

xp_notes class-attribute instance-attribute

xp_notes: tuple[XpNote, ...] = ()

number_appearing instance-attribute

number_appearing: NumberAppearing

treasure class-attribute instance-attribute

treasure: TreasureRef = TreasureRef()

abilities class-attribute instance-attribute

abilities: tuple[MonsterAbility, ...] = ()

defenses class-attribute instance-attribute

defenses: Defenses = Defenses()

categories class-attribute instance-attribute

categories: tuple[str, ...] = ()

overrides_applied class-attribute instance-attribute

overrides_applied: tuple[str, ...] = ()

ability

ability(tag: str) -> MonsterAbility | None

Return the first ability with tag, or None.

Parameters:

Name Type Description Default
tag str

The ability tag, e.g. "regeneration".

required

Returns:

Type Description
MonsterAbility | None

The ability, or None when the monster doesn't have it.

MoraleAlternate

Bases: BaseModel

A conditional morale score: 10 (8 fear of fire) keeps score 8 + the prose.

score class-attribute instance-attribute

score: int = Field(ge=2, le=12)

condition class-attribute instance-attribute

condition: str = Field(min_length=1)

MovementMode

Bases: BaseModel

One movement mode: rate per turn, encounter rate per round, and a descriptor.

descriptor is None for plain ground movement, else the SRD's word (flying, swimming, gliding, in human form, in webs, ...).

rate_feet class-attribute instance-attribute

rate_feet: int = Field(ge=0)

encounter_rate_feet class-attribute instance-attribute

encounter_rate_feet: int = Field(ge=0)

descriptor class-attribute instance-attribute

descriptor: str | None = None

NumberAppearing

Bases: BaseModel

The stat block's two number-appearing values: dungeon, then lair/wilderness.

dungeon instance-attribute

lair instance-attribute

NumberAppearingValue

Bases: BaseModel

One number-appearing value: dice, a fixed count, and see below semantics.

dice class-attribute instance-attribute

dice: str | None = None

fixed class-attribute instance-attribute

fixed: int | None = None

see_below class-attribute instance-attribute

see_below: bool = False

TreasureRef

Bases: BaseModel

A faithful reference to the stat block's treasure type, as printed.

letters are the primary treasure-type letters (R + S is two); parenthetical keeps bracketed letters (P (B)); special keeps labels that are not treasure types (Tusks, Honey).

letters class-attribute instance-attribute

letters: tuple[str, ...] = ()

parenthetical class-attribute instance-attribute

parenthetical: tuple[str, ...] = ()

extra_gp class-attribute instance-attribute

extra_gp: int = Field(default=0, ge=0)

multiplier class-attribute instance-attribute

multiplier: int = Field(default=1, ge=1)

special class-attribute instance-attribute

special: tuple[str, ...] = ()

see_below class-attribute instance-attribute

see_below: bool = False

XpNote

Bases: BaseModel

A structured XP note for a leader/chieftain/guard variant (stats stay prose).

role class-attribute instance-attribute

role: str = Field(min_length=1)

xp class-attribute instance-attribute

xp: int = Field(ge=0)

spawn_monster

spawn_monster(
    template: MonsterTemplate, *, id: str, stream: RngStream, alignment: Alignment | None = None
) -> MonsterInstance

Spawn a mutable instance from a frozen template, rolling hit points from its Hit Dice.

template is immutable stat-block data: load the catalog once with load_monsters and call spawn_monster once per creature that enters play. Every instance gets its own hit points, conditions, and stat modifiers, so damage and status changes on one instance never touch the shared template or any other instance spawned from it.

Hit points are the sum of count rolls of the hit die (d8, or d4 for ½ HD) plus the signed modifier, minimum 1; fixed-hp forms (1hp, the hydra's 8 hp per HD) roll nothing and are exact. The operative alignment resolves at spawn: the caller's choice wins, else the template's usual, else its sole option; a multi-option template with no usual and no caller choice stays unresolved, which alignment-gated wards treat as differing (erring protective).

Parameters:

Name Type Description Default
template MonsterTemplate

The frozen template to spawn from — get one from load_monsters().get(monster_id); see the monster id index for valid ids.

required
id str

The instance's entity id, conventionally from an IdAllocator.

required
stream RngStream

The RNG stream for the hit point rolls, conventionally MONSTER_SPAWN_STREAM.

required
alignment Alignment | None

The encounter's or script's alignment choice; must be one of the template's options.

None

Returns:

Type Description
MonsterInstance

The spawned instance at full hit points.

Raises:

Type Description
ValueError

If alignment is not among the template's options.