Modes Live Inside Modules
Modes Live Inside Modules
A mode always belongs to a module. Earlier versions accepted top-level game_mode and mode blocks (and a game_modes { } container); these forms are no longer parsed. Every mode must be declared inside a module { mode { } } block.
Why
A module owns the variables, event handlers, and scoring rules that belong to its mode. Top-level mode blocks let you declare a mode without a module to own it, which left the mode’s state and rules scattered across the config. Requiring module { mode { } } keeps ownership explicit.
Migration
Replace each top-level mode block with a module that wraps it.
Before (no longer valid):
game_mode "multiball" {
priority = 600
events {
on game.ball_drain { end_mode = "multiball" }
}
}
After:
module "multiball" {
mode {
priority = 600
events {
on game.ball_drain { end_mode = "multiball" }
}
}
}
A game_modes { } container holding multiple modes becomes one module per mode:
module "base" {
mode {
priority = 100
active_on_startup = true
}
}
module "attract" {
suppressed_by = ["base"]
mode {
priority = 0
active_on_startup = true
}
}
Automatic Module-Active Guard
When a module contains a mode block, every modifier, event handler, and accumulator declared at the module level is automatically guarded so it only runs while that mode is active.
| Your config | Effective condition after loading |
|---|---|
no condition | module.<name>.active |
condition = "var.x > 5" | module.<name>.active && (var.x > 5) |
Modules without a mode block (infrastructure modules) are not affected — their conditions are used as written.
Example
module "multiball" {
mode {
priority = 600
}
# Automatically guarded by `module.multiball.active`.
modifier "jackpot_modifier" {
target = "jackpot"
operation = "multiply"
value = "var.jackpot_level"
}
# Automatically becomes:
# module.multiball.active && (var.balls_locked >= 3)
event_handler "lock_check" {
when = "device.lock.activated"
condition = "var.balls_locked >= 3"
actions {
start_mode = "jackpot_round"
}
}
}
The auto-guard uses the fully resolved module name, so it remains correct after module composition. You do not need to write module.self.active anywhere.