Skip to content

Activities

Nat20 resolves combat against a typed activity corpus, not hard-coded rule branches. Weapons, spells, monster actions, and class features each carry a list of activities — Foundry-aligned, Pydantic-typed descriptions of what happens when the thing is used (an attack roll, a save-for-damage, an applied effect, and so on). The dataset lives in the companion dnd5e-srd-data package and is loaded through BundledAssetLoader.

How an intent becomes activities

When you call submit_player_intent(handle, actor_id, intent), the engine reads the PlayerIntent's intent_type to pick the relevant asset reference: "attack" consumes weapon_id, "cast_spell" consumes spell_id (and optional slot_level), "use_item" consumes item_id, and "use_feature" consumes feature_id. It fetches that typed entity from the loader and walks its activities through the per-kind resolvers, emitting the resulting CombatEvent stream.

Why typed activities

This is the core design choice: the engine is edition-agnostic and consumes whatever typed content it is handed. Adding a new weapon, spell, or monster is a data change to the SRD corpus, not an engine change. Because the shapes mirror the Foundry VTT dnd5e schema, content can be sourced and validated against an existing, battle-tested model rather than a bespoke Nat20-only one.

The activity layer is where attack rolls, damage expressions, area-of-effect targeting, and effect application are uniformly described — so combat resolution stays a single code path regardless of which game object triggered it.