chart
local Chart = require("game.chart") local c = Chart.build("okehazama") -- deterministic; same id, same chart
game.chart — the chart format, and the three charts
local Chart = require("game.chart")
local c = Chart.build("okehazama") -- deterministic; same id, same chart
A chart is authored in beats. Nothing in this module knows a tempo or a
second: it knows bars, beats, and which section of the track a bar falls in.
The conductor converts a beat to a song time with ST.clock.beatToTime(),
once, at duel start, after the clock has been bound to the deck that is
actually playing. There is one tempo in the system and the chart never
carries a copy of it, so a chart cannot silently disagree with its track.
The record
| field | meaning |
|---|---|
id | 1..n, in beat order |
lane | 1..4 — D F J K |
beat | position, in beats from the top of the track |
kind | "tap" or "hold" |
beats | length of a hold, in beats; 0 for a tap |
and the chart around them:
| field | meaning |
|---|---|
notes | sorted by beat, then lane |
totalBeats / barCount | the track, in the chart's own units |
holds | how many of the notes are holds |
damageScale / selfScale | the two gains — see below |
The shape follows the music
Each bar is written by the section it falls in, read out of the stage's
structure in ST.STAGES:
| section | what the hand is asked to do |
|---|---|
| intro | one note a bar, walking the lanes in order. This is where the player learns which key is which. |
| groove | the kick on every beat, and the rolling offbeat on alternating bars. The steady body. |
| build | thins out — down to two notes a bar, then one long hold, then a bar of nothing at all. The drop lands into an empty bar. |
| drop | eighths all the way through, sixteenth runs, jacks, and two-lane chords on the bar line. |
| outro | downbeats, then one last hold to the end. |
Lanes are not drawn at random: stepLane walks to a neighbour, sometimes
crosses, occasionally jumps. That is what makes a generated chart feel
played rather than sprayed.
Two rules are then applied to whatever the writers produced, which is what
turns a dense chart into a playable one: no lane is asked for twice inside
MIN_LANE_GAP (0.24 beats), and no more than MAX_CHORD (2) lanes are
ever asked for at once.
The two gains
ST.RULES.damage fixes what the judgements are worth relative to each
other and resolveMax fixes the rival's will at 100. Those two numbers
alone would make the longest chart the easiest break, because a longer
chart simply has more notes to deposit damage with — backwards from a
difficulty curve.
So a chart normalises the overall gain, and only the gain:
damageScale— a flawless run breaks the rival afterBREAK_ATof the chart has gone by: 62% / 72% / 82% for the three stages.selfScale— Nobunaga falls after missingFALL_ATof the chart outright: 30% / 24% / 18%.
A KIWAMI is still worth 1.6 SHIN and 3.6 KA everywhere in the game.
As built
| stage | notes | holds | bars | drop share |
|---|---|---|---|---|
| okehazama (142, d1) | 256 | 4 | 56 | 136 |
| nagashino (146, d2) | 417 | 4 | 65 | 267 |
| honnoji (150, d3) | 524 | 4 | 75 | 311 |
Chart.census(stageId) reports that breakdown live.
Scoped to this part · feeds back into the world's score.