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 configEffective condition after loading
no conditionmodule.<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.