Skip to content

Definitions

Definitions are reusable YAML structures that can be inherited using the $extends directive at any nesting level. Definitions can be parameterized with $with bindings and validated with $params declarations.

Definitions are declared in a top-level definitions section:

spec:
version: "1.0"
definitions:
baseCondition:
awaken: false
masterpiece: false
params:
evolutionProb: 1.0
requiredMoney: 10000
rareCondition:
$extends: baseCondition
params:
evolutionProb: 0.8
requiredMoney: 50000

Definition names must match pattern [A-Za-z_][A-Za-z0-9_]*.

The $extends directive inherits from a definition:

evolutions:
create:
- targetTemplateId: 10121
conditions:
- $extends: baseCondition
targetEnchantStep: 9
result:
resultTemplateId: 12265

$extends works at any mapping level - top-level entities, nested blocks, deeply nested objects.

Definitions can be combined with ID List Expansion to apply templates to multiple IDs at once.

When extending a definition, values are merged using deep merge rules:

ParentChildResult
{a: 1}{b: 2}{a: 1, b: 2}
{a: 1}{a: 2}{a: 2} (child wins)
{a: {x: 1}}{a: {y: 2}}{a: {x: 1, y: 2}} (recursive)
[1, 2][3, 4][3, 4] (replace)
"foo""bar""bar" (replace)

Key rules:

  • Objects merge recursively
  • Lists replace entirely (no element merging)
  • Scalars replace
  • Child values always win over parent values

The $remove directive removes inherited keys after merge:

definitions:
fullItem:
name: "Base Item"
tradable: true
internalId: 12345
debugFlag: true
items:
create:
- $extends: fullItem
$remove:
- internalId
- debugFlag
name: "Player Item"

Result: {name: "Player Item", tradable: true}

Rules:

  • $remove must be a list of key names
  • Processed after deep merge
  • Removes top-level keys only
  • Removing non-existent keys is allowed

The $with directive adds scoped value bindings to $extends, making definitions reusable templates. A definition declares placeholder references ($NAME) in its body; the call site provides concrete values via $with:

definitions:
Greeting:
message: hello
target: $WHO
result:
upsert:
- $extends: Greeting
$with: { WHO: world }

Result: {message: hello, target: world}

$with values are resolved against the current variable scope before injection. Values can reference file-scoped variables:

variables:
SOURCE_ID: 12265
TARGET_ID: 12273
definitions:
Item:
source: $SRC
target: $TGT
items:
create:
- $extends: Item
$with: { SRC: $SOURCE_ID, TGT: $TARGET_ID }

Result: {source: 12265, target: 12273}

$with bindings shadow file-scoped variables of the same name:

variables:
HP: 100
definitions:
Stats:
hp: $HP
result:
create:
- $extends: Stats
$with: { HP: 999 }

Result: hp: 999 (not 100).

$with bindings propagate into nested $extends chains through scope inheritance. Each $extends block forms a scope boundary — nested blocks receive the outer scope:

definitions:
Inner:
value: $X
Outer:
nested:
$extends: Inner
$with: { X: $Y }
result:
create:
- $extends: Outer
$with: { Y: 42 }

Result: {nested: {value: 42}}

The outer $with: { Y: 42 } provides Y to the scope. The nested $with: { X: $Y } resolves $Y to 42 from the inherited scope, then binds X: 42 for Inner.

$with can pass definition names for dynamic $extends resolution inside definition bodies:

definitions:
BasePath:
cost: 500
Item:
path:
$extends: $PATH_DEF
extra: true
result:
create:
- $extends: Item
$with: { PATH_DEF: BasePath }

Result: {path: {cost: 500, extra: true}}

The $extends: $PATH_DEF inside Item resolves $PATH_DEF to BasePath via the inherited scope. The resolved value must be a valid definition name (E501 if not).

$with and $remove can be combined. $remove is processed before substitution, so removed fields with unresolved placeholders do not trigger E520:

definitions:
Base:
a: 1
b: 2
c: $VAL
result:
create:
- $extends: Base
$with: { VAL: 3 }
$remove: [b]

Result: {a: 1, c: 3}

  • $with requires $extends as a sibling (E540)
  • $with value must be a YAML mapping (E541)
  • Binding names must match [A-Za-z_][A-Za-z0-9_]* (E542)
  • Binding values must be scalars or scalar lists — mappings and sequences containing non-scalar elements are not allowed (E543)
  • Substitution is whole-value only: $NAME must be the entire scalar value. Substrings like "prefix_$NAME" remain as literal strings
  • $with is stripped from output

$params declares required parameter names on a definition. It is validated at expansion time — if required parameters are not provided via $with bindings or file-scoped variables, E544 fires with an actionable message.

definitions:
Item:
$params: [SOURCE, TARGET]
source: $SOURCE
target: $TARGET
result:
create:
- $extends: Item
$with: { SOURCE: 1, TARGET: 2 }
ScenarioBehavior
$with provides all required paramsNo error
$with provides extra params beyond $params listAllowed
File-scoped variables satisfy all required params (no $with)No error
Required param missing from both $with and scopeE544
$params is not a sequence of stringsE545
Duplicate names in $paramsE545
$params used outside definitions:E507

$params is definition metadata — it is extracted and stripped at registration time, never appearing in output.

Definitions can extend other definitions:

definitions:
baseWeapon:
combatItemType: EquipWeapon
maxStack: 1
tradable: true
lancerWeapon:
$extends: baseWeapon
requiredClass: ["Lancer"]
attackRange: 3.0
lancerWeaponTier2:
$extends: lancerWeapon
requiredLevel: 30
rareGrade: Uncommon
items:
create:
- id: 10001
$extends: lancerWeaponTier2
name: steel_lance

Maximum inheritance depth is 10 levels.

Export definitions for use in other modules:

packages/core/index.yml
spec:
version: "1.0"
exports:
definitions:
- baseCondition
- rareCondition
definitions:
baseCondition:
awaken: false
masterpiece: false
rareCondition:
$extends: baseCondition
params:
evolutionProb: 0.8

Import a package and reference definitions with namespace qualification:

specs/evolutions.yml
spec:
version: "1.0"
imports:
- from: core
evolutions:
create:
- targetTemplateId: 10121
conditions:
- $extends: core.baseCondition
targetEnchantStep: 9
- $extends: core.rareCondition
targetEnchantStep: 12

Packages can split definitions across multiple files. All files within a package share the same namespace:

packages/weapons/
index.yml # imports ./bases.yml, exports baseSword
bases.yml # defines baseSword → registered as weapons.baseSword
  1. Unqualified names (baseCondition) resolve to local definitions first
  2. Qualified names (core.baseCondition) resolve to the specified package
  3. If an unqualified name matches multiple packages, an error is raised (use qualified name)

Module-level variables can be used inside definition bodies. Variable substitution runs after $extends merge, so inherited fields containing $VAR references resolve correctly:

variables:
BASE_HP: 1000
definitions:
baseStats:
hp: $BASE_HP
mp: 500
warriors:
create:
- $extends: baseStats
name: fighter

After resolution: hp: 1000, mp: 500.

When $with bindings are present, they shadow file-scoped variables of the same name within the definition body. See $with shadowing above.

Variables can also be imported from other modules and used in local definitions. See Variables for declaration syntax, types, and cross-module export/import.

CodeMeaning
E501Unknown definition reference
E502Circular definition inheritance
E503Inheritance depth exceeds limit (10)
E504Ambiguous definition (multiple matches)
E506Unsupported YAML feature (anchors/aliases)
E507Unknown directive
E509Duplicate definition name
E510Invalid definition name
E511Invalid $remove directive
E512Exported definition not found
E540$with without $extends
E541Invalid $with value (not a mapping)
E542Invalid $with binding name
E543Invalid $with binding value (mapping or non-scalar sequence element)
E544Missing required parameter ($params unsatisfied)
E545Invalid $params declaration
items:
create:
- $extends: nonexistent # E501

Also fires when $extends: $PARAM resolves via $with to a name that is not a known definition.

definitions:
a:
$extends: b
b:
$extends: a # E502: Circular

YAML anchors (&), aliases (*), and merge keys (<<) are not supported:

definitions:
base: &base # E506: Anchors not allowed
level: 10
items:
create:
- <<: *base # E506: Merge keys not allowed

Use $extends instead.

result:
create:
- id: 1
$with: { X: 2 } # E540: $with requires $extends
result:
create:
- $extends: Template
$with: not_a_mapping # E541: Expected mapping
result:
create:
- $extends: Template
$with:
X:
nested: value # E543: Mappings not allowed

Binding values follow the same type restrictions as variables: scalars and scalar lists only.

definitions:
Item:
$params: [SOURCE, TARGET]
source: $SOURCE
target: $TARGET
result:
create:
- $extends: Item
$with: { SOURCE: 1 } # E544: Missing TARGET
definitions:
Item:
$params: not_a_list # E545: Expected sequence of strings
id: 1

Also fires for duplicate parameter names ($params: [ID, ID]).