Tuning
Required
tuning.json holds the shared numbers of your game: costs, speeds, sizes, timings. The builder reads them from here, and your prose points at them by name. The file has three parts. values is required; ranges and rules are optional.
tuning.json, the numbers file
{
"values": {
"dash.speed_tiles_per_second": 12,
"dash.duration_seconds": 0.15,
"dash.cooldown_seconds": 0.6,
"gap.width_tiles": 1,
"level.width_tiles": 24,
"room.width_tiles": 8
},
"ranges": {
"dash.speed_tiles_per_second": [8, 20],
"dash.duration_seconds": [0.1, 0.3],
"dash.cooldown_seconds": [0.3, 1.5]
},
"rules": {
"no-dash-while-dashing": "dash.cooldown_seconds >= dash.duration_seconds",
"dash-covers-the-gap": "dash.speed_tiles_per_second * dash.duration_seconds >= gap.width_tiles"
}
}
values gives each number one name and one current value. A name has two or more parts joined by dots. A part uses letters, digits, underscores or hyphens, and may not begin or end with a hyphen; lowercase with underscores is the usual choice.
ranges says how far a value may move: by you when balancing, or by the builder while balancing the build. Both end numbers are allowed values. A value without a range stays as written.
rules are one-line checks between values that must stay true. Each rule has a name, in lowercase words joined by hyphens, and one comparison. no-dash-while-dashing keeps the cooldown at least as long as the dash, so one dash ends before the next can start. dash-covers-the-gap makes sure a dash always crosses the gap. The validator (the checking tool) checks every rule and reports the ones that are false. A rule never changes a number.
Suppose a balance pass sets the speed to 8 and the duration to 0.1. Both are inside their ranges, but dash-covers-the-gap is now false:
rule "dash-covers-the-gap" does not hold
dash.speed_tiles_per_second * dash.duration_seconds >= gap.width_tiles
8 * 0.1 = 0.8, which is not >= gap.width_tiles = 1
The report names the rule, shows it as you wrote it, and shows the numbers that broke it. A false rule is an error: the package does not validate until the rule holds.
A rule is one comparison, using ==, !=, <, <=, > or >=, between two calculations. A calculation uses plain numbers and keys from values, joined by +, -, * and /, with parentheses and the functions min, max and floor. floor rounds down, which is useful for a whole-number count:
tuning.json, a whole-number rule using values declared above
{
"two-rooms-fit-across": "floor((level.width_tiles - 2) / room.width_tiles) >= 2"
}
Multiplication and division happen before addition and subtraction. The parentheses above subtract the two wall tiles first. Use parentheses when unsure. A rule reads keys from values only. A number that belongs to one item in a collection, or a runtime value that changes during play, cannot appear in a rule.
Rules are about the numbers written in the design. "The player never has more than three dashes stored" is about what happens during play, so it is an acceptance test, not a rule.
A shared gameplay number belongs here. A number that belongs to one item in a list stays with that item. A number used only by one test stays with the test.
Do not start a key with a word OpenGDD already uses, such as runtime, clocks, palette or collections, and do not name any part of a key json or md. The validator reports a reserved word if you use one. The specification lists them all.
Full rules: OpenGDD specification, §4.