Upgrade MyGo to v0.5.5

Elements are now pass-scoped value handles: tests that read bounds
after a frame bind a ui.Handle. pixel tells frames apart by the build
pass's root, since the Tester clock no longer moves on its own.

Fix the regressions the upgrade brought:
- Clipboard reads in input handlers go through c.Services().
- MyGo notes an overlay's opener after building it, so AutoFocus
  inside lost it; pixel.AutoFocus focuses through a Handle, and
  archtest bans the raw call. Dialogs hand the focus back again.
- Elements around a press stay hovered: Tooltip tells a press-given
  focus by focus arriving under the pointer.
- The web host moves the Tester clock with real time, at most 500 ms
  a call, and gives components the real clock.

The gallery's source index also accepts CRLF checkouts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-17 01:31:08 -04:00
1 parent 1f3d38110e
commit 03e1f91706
532 files changed
+828 -735

No files matched your search

+1 -1
View File
@@ -25,7 +25,7 @@ type AchievementsOptions struct {
}
type AchievementsResult struct {
Element *ui.Element
Element ui.Element
Changed bool
}
+1 -1
View File
@@ -15,7 +15,7 @@ type ATBGaugeOptions struct {
}
// ATBGauge fills as a turn nears; full, it flashes READY.
func ATBGauge(c *ui.Context, text string, value float64, o ATBGaugeOptions) *ui.Element {
func ATBGauge(c *ui.Context, text string, value float64, o ATBGaugeOptions) ui.Element {
k := pixel.K(c)
if !(value >= 0 && value <= 1) {
panic(fmt.Sprintf("game: ATBGauge value %v outside 0–1", value))
+1 -1
View File
@@ -18,7 +18,7 @@ type BattleLogOptions struct {
}
// BattleLog keeps the newest lines last; old ones roll off.
func BattleLog(c *ui.Context, text string, lines []Line, o BattleLogOptions) *ui.Element {
func BattleLog(c *ui.Context, text string, lines []Line, o BattleLogOptions) ui.Element {
k := pixel.K(c)
w, h := pixel.Or("BattleLog Width", o.Width, 180), pixel.Or("BattleLog Height", o.Height, 68)
n := (h - 4) / pixel.LineHeight
+1 -1
View File
@@ -17,7 +17,7 @@ type BossBarOptions struct {
}
// BossBar is a wide bar under the name, phases marked.
func BossBar(c *ui.Context, name string, hp, full int, o BossBarOptions) *ui.Element {
func BossBar(c *ui.Context, name string, hp, full int, o BossBarOptions) ui.Element {
k := pixel.K(c)
amount("BossBar hp", hp, full)
for _, f := range o.Phases {
+1 -1
View File
@@ -28,7 +28,7 @@ type Character struct {
type CharacterSheetOptions struct{}
// CharacterSheet shows a portrait, name, class, level and stats.
func CharacterSheet(c *ui.Context, h Character, o CharacterSheetOptions) *ui.Element {
func CharacterSheet(c *ui.Context, h Character, o CharacterSheetOptions) ui.Element {
k := pixel.K(c)
if h.Portrait == nil || h.Level < 1 {
panic(fmt.Sprintf("game: character %q needs a portrait and a level (%d)", h.Name, h.Level))
+1 -1
View File
@@ -16,7 +16,7 @@ type CoinCounterOptions struct {
}
// CoinCounter spins a coin beside a count that ticks up.
func CoinCounter(c *ui.Context, text string, coins int, o CoinCounterOptions) *ui.Element {
func CoinCounter(c *ui.Context, text string, coins int, o CoinCounterOptions) ui.Element {
k := pixel.K(c)
if coins < 0 {
panic(fmt.Sprintf("game: CoinCounter %d", coins))
+1 -1
View File
@@ -23,7 +23,7 @@ type CompassOptions struct {
}
// Compass is a heading strip with letters and markers.
func Compass(c *ui.Context, text string, heading float64, marks []Bearing, o CompassOptions) *ui.Element {
func Compass(c *ui.Context, text string, heading float64, marks []Bearing, o CompassOptions) ui.Element {
k := pixel.K(c)
for _, v := range append([]float64{heading}, bearings(marks)...) {
if math.IsNaN(v) || math.IsInf(v, 0) {
+1 -1
View File
@@ -22,7 +22,7 @@ type CraftingOptions struct {
}
type CraftingResult struct {
Element *ui.Element
Element ui.Element
Changed bool
// Picked is the cell chosen this frame, or -1.
Picked int
+1 -1
View File
@@ -22,7 +22,7 @@ type CreditsOptions struct {
}
type CreditsResult struct {
Element *ui.Element
Element ui.Element
// Done is true in the frame the roll ends.
Done bool
}
+1 -1
View File
@@ -28,7 +28,7 @@ type DamageNumbersOptions struct {
const life = 800 * time.Millisecond
// DamageNumbers floats hits up, dissolves, then drops them.
func DamageNumbers(c *ui.Context, text string, hits *[]Hit, o DamageNumbersOptions) *ui.Element {
func DamageNumbers(c *ui.Context, text string, hits *[]Hit, o DamageNumbersOptions) ui.Element {
k := pixel.K(c)
w, h := pixel.Or("DamageNumbers Width", o.Width, 160), pixel.Or("DamageNumbers Height", o.Height, 80)
// S5: hits rise in whole frames, fifteen a second.
+1 -1
View File
@@ -16,7 +16,7 @@ type EndingCardOptions struct {
}
// EndingCard dithers THE END in, step by step.
func EndingCard(c *ui.Context, o EndingCardOptions) *ui.Element {
func EndingCard(c *ui.Context, o EndingCardOptions) ui.Element {
k := pixel.K(c)
w, h := pixel.Or("EndingCard Width", o.Width, 200), pixel.Or("EndingCard Height", o.Height, 80)
s := k.Msg("the_end")
+2 -2
View File
@@ -38,7 +38,7 @@ type EquipmentOptions struct {
}
type EquipmentResult struct {
Element *ui.Element
Element ui.Element
Changed bool
// Picked is the slot opened this frame, or -1.
Picked int
@@ -61,7 +61,7 @@ func Equipment(c *ui.Context, text string, gear []data.Item, selected *int, o Eq
}
w = max(w, pixel.Measure(captions[i]))
}
shell, base, st := pixel.Shell(c, func() *ui.Element {
shell, base, st := pixel.Shell(c, func() ui.Element {
return ui.Box(c).Focusable().Role(ui.RoleGroup).Label(text).Disabled(o.Disabled)
})
arrows(base, spots, selected)
+1 -1
View File
@@ -23,7 +23,7 @@ type rise struct {
}
// ExpBar shows xp toward next and the level; ups flash.
func ExpBar(c *ui.Context, text string, level, xp, next int, o ExpBarOptions) *ui.Element {
func ExpBar(c *ui.Context, text string, level, xp, next int, o ExpBarOptions) ui.Element {
k := pixel.K(c)
if level < 1 {
panic(fmt.Sprintf("game: ExpBar level %d", level))
+1 -1
View File
@@ -14,7 +14,7 @@ import (
type GachaReelOptions struct{}
type GachaReelResult struct {
Element *ui.Element
Element ui.Element
// Done is true in the frame the last reel stops.
Done bool
}
+2 -2
View File
@@ -88,7 +88,7 @@ func nearest(spots []Spot, from, dx, dy int) int {
}
// arrows moves cur among spots by arrow keys.
func arrows(base *ui.Element, spots []Spot, cur *int) {
func arrows(base ui.Element, spots []Spot, cur *int) {
for _, m := range []struct {
key ui.Key
dx, dy int
@@ -115,7 +115,7 @@ type begun struct {
}
// start is e's first showing, or its last cue change.
func start(k pixel.Kit, e *ui.Element, cue int) begun {
func start(k pixel.Kit, e ui.Element, cue int) begun {
s := ui.Local(e, startKey{}, func() begun { return begun{cue: cue, at: k.Now()} })
if s.cue != cue {
s.cue, s.at, s.run = cue, k.Now(), s.run+1
+1 -1
View File
@@ -14,7 +14,7 @@ type GameOverOptions struct {
}
type GameOverResult struct {
Element *ui.Element
Element ui.Element
Continue, Quit bool
}
+1 -1
View File
@@ -26,7 +26,7 @@ type trail struct {
const hold = 300 * time.Millisecond
// HealthBar shows hp with a lagging trail; low HP blinks.
func HealthBar(c *ui.Context, text string, hp, full int, o HealthBarOptions) *ui.Element {
func HealthBar(c *ui.Context, text string, hp, full int, o HealthBarOptions) ui.Element {
k := pixel.K(c)
amount("HealthBar hp", hp, full)
w := pixel.Or("HealthBar Width", o.Width, 96)
+1 -1
View File
@@ -17,7 +17,7 @@ type HeartsOptions struct {
}
// Hearts shows hp of full hearts, in quarters.
func Hearts(c *ui.Context, text string, hp, full float64, o HeartsOptions) *ui.Element {
func Hearts(c *ui.Context, text string, hp, full float64, o HeartsOptions) ui.Element {
k := pixel.K(c)
if full < 1 || full != math.Trunc(full) || !(hp >= 0 && hp <= full) || hp*4 != math.Trunc(hp*4) {
panic(fmt.Sprintf("game: Hearts %v of %v; hearts are whole, health in quarters", hp, full))
+1 -1
View File
@@ -16,7 +16,7 @@ type HotbarOptions struct {
}
type HotbarResult struct {
Element *ui.Element
Element ui.Element
Changed bool
// Used is the slot used this frame, or -1.
Used int
+2 -2
View File
@@ -15,7 +15,7 @@ type InteractPromptOptions struct {
}
// InteractPrompt bobs a Ⓐ TALK bubble above target.
func InteractPrompt(c *ui.Context, target *ui.Element, action string, o InteractPromptOptions) *ui.Element {
func InteractPrompt(c *ui.Context, target ui.Element, action string, o InteractPromptOptions) ui.Element {
k := pixel.K(c)
key := o.Key
if key == "" {
@@ -33,7 +33,7 @@ func InteractPrompt(c *ui.Context, target *ui.Element, action string, o Interact
if !k.Reduce() {
bob = k.Tick(c, 4) / 2 % 2
}
var e *ui.Element
var e ui.Element
ui.Overlay(c, func() {
e = ui.Box(c).Absolute().Left(x).Bottom(wh - b.Y + k.D(1+bob)).PassThrough().Role(ui.RoleText).Label(key + " " + action)
// Bounds lag a layout; if the target moved, build again.
+2 -2
View File
@@ -13,10 +13,10 @@ type LetterboxOptions struct {
}
// Letterbox slides cutscene bars over content in four steps.
func Letterbox(c *ui.Context, on bool, o LetterboxOptions, content func()) *ui.Element {
func Letterbox(c *ui.Context, on bool, o LetterboxOptions, content func()) ui.Element {
k := pixel.K(c)
bar := pixel.Or("Letterbox Bar", o.Bar, 16)
var cover *ui.Element
var cover ui.Element
e := layout.Layers(c, layout.LayersOptions{}, content, func() { cover = ui.Box(c).Fill().PassThrough() })
h := bar * k.Toward(c, e, on, 8, 4) / 4
cover.Draw(func(p *ui.Painter, r ui.Rect) {
+1 -1
View File
@@ -26,7 +26,7 @@ type LevelUpScreenOptions struct {
}
type LevelUpScreenResult struct {
Element *ui.Element
Element ui.Element
Changed bool
Done bool
}
+1 -1
View File
@@ -15,7 +15,7 @@ type LivesOptions struct {
}
// Lives shows a life icon and ×count.
func Lives(c *ui.Context, text string, n int, o LivesOptions) *ui.Element {
func Lives(c *ui.Context, text string, n int, o LivesOptions) ui.Element {
k := pixel.K(c)
if n < 0 {
panic(fmt.Sprintf("game: Lives %d", n))
+1 -1
View File
@@ -18,7 +18,7 @@ type LoadingTipsOptions struct {
const tipTurn = 4 * time.Second
// LoadingTips turns tips every 4 s under a progress bar.
func LoadingTips(c *ui.Context, tips []string, value float64, o LoadingTipsOptions) *ui.Element {
func LoadingTips(c *ui.Context, tips []string, value float64, o LoadingTipsOptions) ui.Element {
k := pixel.K(c)
w := pixel.Or("LoadingTips Width", o.Width, 200)
if len(tips) == 0 {
+1 -1
View File
@@ -15,7 +15,7 @@ type LootPopupOptions struct {
}
type LootPopupResult struct {
Element *ui.Element
Element ui.Element
// Closed is OK, Escape or a click outside.
Closed bool
}
+1 -1
View File
@@ -14,7 +14,7 @@ type ManaBarOptions struct {
}
// ManaBar shows mp of full, ticked every few points.
func ManaBar(c *ui.Context, text string, mp, full int, o ManaBarOptions) *ui.Element {
func ManaBar(c *ui.Context, text string, mp, full int, o ManaBarOptions) ui.Element {
k := pixel.K(c)
amount("ManaBar mp", mp, full)
w := pixel.Or("ManaBar Width", o.Width, 96)
+1 -1
View File
@@ -262,7 +262,7 @@ func TestLoadingTipsTurnEveryFourSeconds(t *testing.T) {
func TestInteractPromptBobsOnceASecond(t *testing.T) {
wd := &world{clk: newClock()}
tt := view(300, 200, wd, func(c *ui.Context) {
var target *ui.Element
var target ui.Element
ui.Column(c).Padding(60).Children(func() { target = ui.Box(c).Size(20, 20) })
game.InteractPrompt(c, target, "OPEN", game.InteractPromptOptions{})
})
+1 -1
View File
@@ -27,7 +27,7 @@ type MinimapOptions struct {
var tiles = map[rune]pixel.Slot{'#': pixel.Edge, '.': pixel.Well, '~': pixel.Info, '"': pixel.Good, '^': pixel.Faint, ' ': pixel.Back}
// Minimap draws tiles, fog where unseen, player and marks.
func Minimap(c *ui.Context, text string, rows []string, seen [][]bool, player Spot, marks []Marker, o MinimapOptions) *ui.Element {
func Minimap(c *ui.Context, text string, rows []string, seen [][]bool, player Spot, marks []Marker, o MinimapOptions) ui.Element {
k := pixel.K(c)
cell := pixel.Or("Minimap Cell", o.Cell, 3)
if len(rows) == 0 {
+1 -1
View File
@@ -14,7 +14,7 @@ type MissionBriefOptions struct {
}
type MissionBriefResult struct {
Element *ui.Element
Element ui.Element
Start bool
}
+2 -2
View File
@@ -39,7 +39,7 @@ type OptionsMenuOptions struct {
}
type OptionsMenuResult struct {
Element *ui.Element
Element ui.Element
Changed bool
}
@@ -63,7 +63,7 @@ func OptionsMenu(c *ui.Context, text string, settings []Setting, row *int, o Opt
value = max(value, pixel.Measure(ch))
}
}
shell, base, st := pixel.Shell(c, func() *ui.Element {
shell, base, st := pixel.Shell(c, func() ui.Element {
return ui.Box(c).Focusable().Role(ui.RoleList).Label(text).Disabled(o.Disabled)
})
res, was := OptionsMenuResult{Element: shell}, *row
+1 -1
View File
@@ -18,7 +18,7 @@ type Member struct {
type PartyFramesOptions struct{}
// PartyFrames stacks member cards; a fallen member greys out.
func PartyFrames(c *ui.Context, text string, members []Member, o PartyFramesOptions) *ui.Element {
func PartyFrames(c *ui.Context, text string, members []Member, o PartyFramesOptions) ui.Element {
k := pixel.K(c)
return ui.Column(c).Gap(k.D(3)).AlignItems(ui.Start).Role(ui.RoleGroup).Label(text).Children(func() {
for _, m := range members {
+2 -2
View File
@@ -13,7 +13,7 @@ type PressStartOptions struct {
}
type PressStartResult struct {
Element *ui.Element
Element ui.Element
Pressed bool
}
@@ -21,7 +21,7 @@ type PressStartResult struct {
func PressStart(c *ui.Context, o PressStartOptions) PressStartResult {
k := pixel.K(c)
s := k.Msg("press_start")
shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(s).Disabled(o.Disabled) })
shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(s).Disabled(o.Disabled) })
res := PressStartResult{Element: shell, Pressed: base.Clicked()}
if res.Pressed {
slog.Debug("8bit: press start")
+1 -1
View File
@@ -16,7 +16,7 @@ type QuestLogOptions struct {
}
type QuestLogResult struct {
Element *ui.Element
Element ui.Element
Changed bool
}
+1 -1
View File
@@ -12,7 +12,7 @@ import (
type QuestTrackerOptions struct{}
// QuestTracker lists a quest's objectives with counts, met ones ticked.
func QuestTracker(c *ui.Context, q Quest, o QuestTrackerOptions) *ui.Element {
func QuestTracker(c *ui.Context, q Quest, o QuestTrackerOptions) ui.Element {
k := pixel.K(c)
q.check()
w := icon.Star.W + 2 + pixel.Measure(q.Title)
+1 -1
View File
@@ -12,7 +12,7 @@ import (
type ResultScreenOptions struct{}
// ResultScreen counts time and score up, then shows the rank.
func ResultScreen(c *ui.Context, text string, took time.Duration, score int, rank string, o ResultScreenOptions) *ui.Element {
func ResultScreen(c *ui.Context, text string, took time.Duration, score int, rank string, o ResultScreenOptions) ui.Element {
k := pixel.K(c)
if took < 0 || score < 0 || rank == "" {
panic(fmt.Sprintf("game: ResultScreen %v, %d, rank %q", took, score, rank))
+1 -1
View File
@@ -262,7 +262,7 @@ func TestGachaReelRefusesAFaceWithoutSprite(t *testing.T) {
}
func TestInteractPromptSitsAboveItsTarget(t *testing.T) {
var target *ui.Element
var target ui.Element
pad := 40
tt := view(400, 300, &world{clk: newClock(), reduce: true}, func(c *ui.Context) {
ui.Column(c).Padding(pixel.K(c).D(pad)).Children(func() {
+1 -1
View File
@@ -22,7 +22,7 @@ type SaveSlotsOptions struct {
}
type SaveSlotsResult struct {
Element *ui.Element
Element ui.Element
Changed bool
// Chosen is the slot opened this frame, or -1.
Chosen int
+1 -1
View File
@@ -14,7 +14,7 @@ type ScoreBoardOptions struct {
}
// ScoreBoard shows SCORE and HI-SCORE in big digits.
func ScoreBoard(c *ui.Context, score, hi int, o ScoreBoardOptions) *ui.Element {
func ScoreBoard(c *ui.Context, score, hi int, o ScoreBoardOptions) ui.Element {
k := pixel.K(c)
if score < 0 || hi < 0 {
panic(fmt.Sprintf("game: ScoreBoard %d and %d", score, hi))
+1 -1
View File
@@ -24,7 +24,7 @@ type ShopOptions struct {
}
type ShopResult struct {
Element *ui.Element
Element ui.Element
// Bought and Sold are this frame's trades, or -1.
Bought, Sold int
}
+2 -2
View File
@@ -16,7 +16,7 @@ type SkillButtonOptions struct {
}
type SkillButtonResult struct {
Element *ui.Element
Element ui.Element
// Clicked is a press while the skill is ready.
Clicked bool
}
@@ -27,7 +27,7 @@ func SkillButton(c *ui.Context, text string, sprite *pixel.Sprite, key string, c
if sprite == nil || !(cooldown >= 0 && cooldown <= 1) || !pixel.MicroOK(key) {
panic(fmt.Sprintf("game: SkillButton %q needs a sprite, a micro key %q, a cooldown in 0–1 (%v)", text, key, cooldown))
}
shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(text).Disabled(o.Disabled) })
shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(text).Disabled(o.Disabled) })
res := SkillButtonResult{Element: shell, Clicked: base.Clicked() && cooldown == 0}
if res.Clicked {
slog.Debug("8bit: skill", "label", text)
+2 -2
View File
@@ -25,7 +25,7 @@ type SkillTreeOptions struct {
}
type SkillTreeResult struct {
Element *ui.Element
Element ui.Element
Changed bool
// Learned is the skill learned this frame, or -1.
Learned int
@@ -69,7 +69,7 @@ func SkillTree(c *ui.Context, text string, skills []Skill, cursor, points *int,
return !skills[i].Learned
}
old := *cursor
shell, base, st := pixel.Shell(c, func() *ui.Element {
shell, base, st := pixel.Shell(c, func() ui.Element {
return ui.Box(c).Focusable().Role(ui.RoleGroup).Label(text).Disabled(o.Disabled)
})
arrows(base, spots, cursor)
+1 -1
View File
@@ -15,7 +15,7 @@ type StaminaBarOptions struct {
}
// StaminaBar blocks stamina out; the part-full block dithers.
func StaminaBar(c *ui.Context, text string, value float64, o StaminaBarOptions) *ui.Element {
func StaminaBar(c *ui.Context, text string, value float64, o StaminaBarOptions) ui.Element {
k := pixel.K(c)
if !(value >= 0 && value <= 1) {
panic(fmt.Sprintf("game: StaminaBar value %v outside 0–1", value))
+1 -1
View File
@@ -22,7 +22,7 @@ type Effect struct {
type StatusEffectsOptions struct{}
// StatusEffects rows effect icons: buffs edged Good, debuffs Bad.
func StatusEffects(c *ui.Context, text string, effects []Effect, o StatusEffectsOptions) *ui.Element {
func StatusEffects(c *ui.Context, text string, effects []Effect, o StatusEffectsOptions) ui.Element {
k := pixel.K(c)
parts := make([]string, len(effects))
for i, ef := range effects {
+1 -1
View File
@@ -21,7 +21,7 @@ type TargetSelectorOptions struct {
}
type TargetSelectorResult struct {
Element *ui.Element
Element ui.Element
Changed bool
// Chosen is the target confirmed this frame, or -1.
Chosen int
+1 -1
View File
@@ -13,7 +13,7 @@ import (
type TimerHUDOptions struct{}
// TimerHUD shows TIME in seconds; under 30 it blinks red.
func TimerHUD(c *ui.Context, left time.Duration, o TimerHUDOptions) *ui.Element {
func TimerHUD(c *ui.Context, left time.Duration, o TimerHUDOptions) ui.Element {
k := pixel.K(c)
if left < 0 {
panic(fmt.Sprintf("game: TimerHUD %v left", left))
+2 -2
View File
@@ -16,7 +16,7 @@ type TitleScreenOptions struct {
}
type TitleScreenResult struct {
Element *ui.Element
Element ui.Element
// Chosen is 0 NEW GAME, 1 CONTINUE, 2 OPTIONS.
Chosen int
}
@@ -50,7 +50,7 @@ func TitleScreen(c *ui.Context, title string, started *bool, o TitleScreenOption
}
// logo writes the title big, gold over red, shadowed.
func logo(c *ui.Context, title string) *ui.Element {
func logo(c *ui.Context, title string) ui.Element {
k := pixel.K(c)
w := 3*pixel.Measure(title) + 2
return ui.Box(c).Role(ui.RoleText).Label(title).Size(k.D(w), k.D(3*pixel.LineHeight+2)).Draw(func(p *ui.Painter, r ui.Rect) {
+2 -2
View File
@@ -16,7 +16,7 @@ type TreasureChestOptions struct {
}
type TreasureChestResult struct {
Element *ui.Element
Element ui.Element
// Opened is true in the frame a press opens it.
Opened bool
}
@@ -35,7 +35,7 @@ func TreasureChest(c *ui.Context, text string, it data.Item, open *bool, o Treas
if it.Sprite == nil {
panic(fmt.Sprintf("game: TreasureChest item %q has no sprite", it.Name))
}
shell, base, st := pixel.Shell(c, func() *ui.Element { return ui.ButtonBase(c).Label(text).Disabled(o.Disabled) })
shell, base, st := pixel.Shell(c, func() ui.Element { return ui.ButtonBase(c).Label(text).Disabled(o.Disabled) })
res := TreasureChestResult{Element: shell}
if base.Clicked() && !*open {
*open, res.Opened = true, true
+1 -1
View File
@@ -19,7 +19,7 @@ type Fighter struct {
type TurnOrderOptions struct{}
// TurnOrder rows portraits in order, the active raised.
func TurnOrder(c *ui.Context, text string, order []Fighter, active int, o TurnOrderOptions) *ui.Element {
func TurnOrder(c *ui.Context, text string, order []Fighter, active int, o TurnOrderOptions) ui.Element {
k := pixel.K(c)
if active < 0 || active >= len(order) {
panic(fmt.Sprintf("game: TurnOrder active %d of %d", active, len(order)))