Table Design Basics
Table Design Basics
Almost every table is built from two kinds of pieces: scoring elements that award points when something happens, and modes that change the rules for a stretch of play. This guide builds one of each from scratch, so you come away knowing the shape of both.
It assumes you can already load and run a .cade file. If not, walk through Getting Started first — it covers installing Cade, validating a table, and launching the console.
Scoring Elements
A scoring element is a score "event" block. When the event you name fires, Cade evaluates an expression and adds the result to the player’s score. The two attributes you always provide are when (the event to listen for) and points (how much to award):
# A variable holds the point value so you can tune it in one place.
variable "int" "bumper_value" {
initial = 1000
scope = "player"
}
score "event" "bumper_hit" {
when = device.bumper.activated # the event that fires this rule
points = var.bumper_value # how many points to award
}That is a complete, working scoring rule: every time the bumper fires its activated event, the player gets bumper_value points.
The attributes you’ll reach for
| Attribute | Required | What it does |
|---|---|---|
when | yes | The event that fires the rule, for example "device.ramp.cleared" |
points | yes | An expression for the points to award |
condition | no | A boolean gate — the rule scores only when it is true |
update | no | Variable changes applied after scoring |
emit | no | An event to emit once scoring completes |
priority | no | Execution order when several rules match (lower runs first) |
A richer rule that gates on game state and feeds back into a variable — here a ramp that scores more each time it’s hit during the ball:
variable "int" "ramp_value" {
initial = 5000
scope = "player"
}
variable "int" "combo_multiplier" {
initial = 100 # 100 means 1.0x — see "integer-first arithmetic"
scope = "ball"
}
score "event" "ramp_shot" {
when = device.ramp.cleared
condition = var.combo_multiplier > 0
points = var.ramp_value * var.combo_multiplier / 100
update {
combo_multiplier = var.combo_multiplier + 10 # +0.1x each ramp
}
}The points expression and the condition gate are written in Cade’s expression language. For how var.… references, the / 100 basis-points multiplier convention, and integer-first arithmetic work, see Writing Expressions. For every scoring attribute and the modifier and accumulator block types, see the Score reference.
Modes
A mode is a stretch of play with its own rules — a timed frenzy, multiball, a wizard mode. While the mode is active, the scoring rules and variables you put inside it switch on; when it ends, they switch off automatically.
You author a mode as a module with a mode { } block inside it. Here is a complete timed mode: for 20 seconds, bumpers are worth double.
module "bumper_frenzy" {
description = "Timed mode: bumpers score double for 20 seconds"
mode {
priority = 300
stop_events = ["frenzy_timeout"]
on_start {
display_text "BUMPER FRENZY!" 3000
}
timers {
frenzy_timer {
duration = 20000 # milliseconds
on_expire { emit "frenzy_timeout" }
}
}
on_end {
display_text "FRENZY OVER" 2000
}
}
# Active only while this mode is running — Cade adds that guard for you.
score "modifier" "frenzy_boost" {
apply {
bumper_value = var.bumper_value * 2
}
}
}The pieces:
module "…" { mode { } }— the mode lives in amodeblock inside a module. (The older top-levelmode "x"andgame_mode "x"blocks are no longer valid; everything is a module now.)priority— when more than one mode is active at once, higher priority runs first.on_start/on_end— actions that run as the mode begins and ends: post a display message, play a sound, set a variable.timers— a named timer with adurationin milliseconds; when it expires,on_expireruns — here it emits the event that stops the mode.stop_events— the events that end the mode.- the
score "modifier"block — anything you put inside the module (scoring rules, modifiers, variables) is active only while the mode is running. You don’t write a condition for it; Cade guards it with the mode’s active state for you.
Starting and ending a mode
A mode sits dormant until something starts it. Start it from an event handler with the start_mode action — for example, when a particular target is hit:
module "base" {
mode {
priority = 100
events {
on device.frenzy_target.hit { start_mode "bumper_frenzy" }
}
}
}The mode ends when one of its stop_events fires — in this case the frenzy_timeout event the timer emits after 20 seconds. You can also end a mode explicitly with the end_mode action inside any of its handlers, which is how modes like multiball wind down when the last ball drains.
Mode modules vs. infrastructure modules
A module with a mode { } block is a mode: it switches on and off, and its contents are active only while it runs. A module without a mode { } block is an infrastructure module — it is always on from the start of the game, useful for ball tracking, tilt handling, or the base ruleset that starts other modes. Same container, two lifecycles.
Where to Go Next
- Run what you wrote — Getting Started covers validating and launching a table and exploring it in the console.
- Write the expressions — Writing Expressions explains the language used in
points,condition, andupdate. - See every option — the Score reference and Variable reference document the full set of scoring and variable attributes, and the table configuration reference covers devices, events, and platforms.