Custom enemies
Custom enemies are a clone-and-patch kind: you start from a vanilla enemy, change its stats/assets, and register it as a new definition. Experimental
Author one
The enemy kind is experimental, so the mod must opt in with
Mod(experimental=True) (lint enforces this for non-confirmed kinds).
from rsmm import sdk
with sdk.Mod("MyEnemies", experimental=True) as mod: mod.enemy( "Frost_Goblin", base="Goblins/Goblin_Warrior", # vanilla donor to clone name="Frost Goblin", # **fields override stat-block values on the clone )Equivalent declarative form:
[mod]id = "MyEnemies"experimental = true
[[content]]kind = "enemy"id = "Frost_Goblin"base = "Goblins/Goblin_Warrior"name = "Frost Goblin"Mod.boss(...) takes the same shape. Bosses are not a separate class — they
are ordinary enemies tagged into the boss flag-list and gated by the boss-timer
controller.
Run ./rsmm apply to cook and register the definition.
How enemies spawn
Spawning is selector-driven, not a direct “place enemy X” call. The chain:
flowchart LR
E["oCDtEnemyDefinition<br/>(one stat block)"] --> T["oCDtEnemyTribeDefinition<br/>(group / faction)"]
T --> S["Camp spawn selector<br/>(flag-list, tag-filtered)"]
C["oCDtEnemyCampTierDefinition<br/>(camp difficulty tier)"] --> S
S --> R["runtime spawn in a camp"]
An enemy reaches a run by being a member of a tribe, which a camp selector picks via tags/flags at the right difficulty tier. So registering a definition is necessary but not sufficient — something must select it.
Class map
The full camp/enemy machinery, for when you need the internals:
| Class | UID | Library | Purpose |
|---|---|---|---|
oCDtEnemyDefinition | 0x176debb7 | 0x1414118c0 | one enemy stat block |
oCDtEnemyTribeDefinition | — | 0x141411200 | tribe = group / faction |
oCDtEnemyCampTierDefinition | 0x176e18f8 | 0x141411560 | difficulty tier of one camp |
oCDtEnemyCampDifficultyDefinition | — | 0x141411710 | global difficulty curve |
oCDtEnemyCampEntitySelectorToSpawnEntityCpntSettings | 0x16b7d175 | — | per-camp spawn selector |
oCDtEnemyCampEntitySelectorToSpawnTribeEntrySettings | 0x16b81d80 | — | one tribe entry inside a camp |
oCDtEnemyFlagListEntitySelectorToSpawnEntityCpntSettings | 0x17019bf9 | — | tag-filtered enemy selector |
oCDtEnemyDefinition is fully registered (size 0x350, factory FUN_14030a190).
The runtime spawn primitive itself — the vftable[3] call that constructs an
entity at a position — is documented in Spawning.