From 735428f0667f67c12fdf3caca872d7dc65e2ce26 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Wed, 30 Sep 2026 03:55:37 -0400 Subject: feat: restyle roulette presentation --- internal/game/render_roulette.go | 53 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) (limited to 'internal/game/render_roulette.go') diff --git a/internal/game/render_roulette.go b/internal/game/render_roulette.go index ce541c1..d69b5bf 100644 --- a/internal/game/render_roulette.go +++ b/internal/game/render_roulette.go @@ -22,7 +22,9 @@ func renderRouletteSnapshot(s *casino.RouletteSnapshot, mode string, unicode boo case "result": return "\n" + renderRouletteResult(s.Pockets, mode, unicode, size) + "\n" default: - return renderRouletteTracker(s, mode, unicode) + "\n" + renderRouletteLayout(s.DoubleZero, s.Racetrack, mode, unicode) + // A blank line above the tracker separates it from the prose or menu + // that precedes the board. + return "\n" + renderRouletteTracker(s, mode, unicode) + "\n" + renderRouletteLayout(s.DoubleZero, s.Racetrack, mode, unicode) } } @@ -174,6 +176,18 @@ func renderRouletteRacetrack(mode string, unicode bool) string { ink(58, 5, b.v) ink(58, 6, bl+tr) ink(59, 7, bl+tr) + // Labels are centered on the middle row; a label too wide to leave a blank + // cell against its dividers is wrapped onto the rows above and below it. + // Only the glyphs are written: the centering padding must not be painted + // over the step and curve dividers that share these rows. + labelX := func(left, right int, text string) int { + width := right - left - 1 + pad := max(0, width-color.VisibleLen(text)) + return left + 1 + pad/2 + } + inkCenter := func(y, left, right int, text string) { + ink(labelX(left, right, text), y, text) + } for _, label := range []struct { left, right int text string @@ -181,7 +195,17 @@ func renderRouletteRacetrack(mode string, unicode bool) string { {4, 22, "Tiers du Cylindre"}, {22, 40, "Orphelins"}, {40, 58, "Voisins du Zero"}, {58, 68, "Jeu Zero"}, } { - ink(label.left+1, 5, rouletteCenter(label.text, label.right-label.left-1)) + lines := rouletteWrapLabel(label.text, label.right-label.left-3) + if len(lines) == 1 { + inkCenter(5, label.left, label.right, lines[0]) + continue + } + // Wrapped lines share the first line's left edge so the two words read + // as one label instead of drifting apart under independent centering. + x := labelX(label.left, label.right, lines[0]) + for i, line := range lines { + ink(x, 4+2*i, line) + } } lines := make([]string, height) for y, row := range grid { @@ -190,6 +214,31 @@ func renderRouletteRacetrack(mode string, unicode bool) string { return strings.Join(lines, "\n") } +// rouletteWrapLabel breaks a call-bet label on spaces so each line leaves a +// blank cell against the track dividers. The caller knows the labels are short +// enough to wrap onto at most two lines; a single over-long word is left whole +// rather than hyphenated. +func rouletteWrapLabel(text string, limit int) []string { + words := strings.Fields(text) + var lines []string + cur := "" + for _, word := range words { + switch { + case cur == "": + cur = word + case len(cur)+1+len(word) <= limit: + cur += " " + word + default: + lines = append(lines, cur) + cur = word + } + } + if cur != "" { + lines = append(lines, cur) + } + return lines +} + // renderRouletteLayout draws the betting board. Green is reserved for the // 0/00 lane: every other cell is red, black, or plain. The odds reminder is // not part of the board (it is shown by the "table" command), so the board -- cgit v1.2.3