Files
games/ui/snakelobby.go
Ryan Hamamura e239e948ae feat: add configurable speed and expanded grid presets for snake
- Add per-game speed setting with presets (Slow/Normal/Fast/Insane)
- Add speed selector UI in snake lobby
- Expand grid presets with Tiny (15x15) and XL (50x30)
- Auto-calculate cell size based on grid dimensions
- Preserve speed setting in rematch games
2026-02-04 10:02:40 -10:00

125 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package ui
import (
"fmt"
"github.com/ryanhamamura/c4/snake"
"github.com/ryanhamamura/via/h"
)
func SnakeLobbyTab(nicknameBind h.H, soloClicks, multiClicks []h.H, activeGames []*snake.SnakeGame, selectedSpeedIndex int, speedSelectClicks []h.H) h.H {
// Solo play buttons
var soloButtons []h.H
for i, preset := range snake.GridPresets {
var click h.H
if i < len(soloClicks) {
click = soloClicks[i]
}
soloButtons = append(soloButtons,
h.Button(
h.Class("btn btn-secondary"),
h.Type("button"),
h.Text(fmt.Sprintf("%s (%d×%d)", preset.Name, preset.Width, preset.Height)),
click,
),
)
}
// Multiplayer buttons
var multiButtons []h.H
for i, preset := range snake.GridPresets {
var click h.H
if i < len(multiClicks) {
click = multiClicks[i]
}
multiButtons = append(multiButtons,
h.Button(
h.Class("btn btn-primary"),
h.Type("button"),
h.Text(fmt.Sprintf("%s (%d×%d)", preset.Name, preset.Width, preset.Height)),
click,
),
)
}
nicknameField := h.Div(h.Class("mb-4"),
h.FieldSet(h.Class("fieldset"),
h.Label(h.Class("label"), h.Text("Your Nickname"), h.Attr("for", "snake-nickname")),
h.Input(
h.Class("input input-bordered w-full"),
h.ID("snake-nickname"),
h.Type("text"),
h.Placeholder("Enter your nickname"),
nicknameBind,
h.Attr("required"),
),
),
)
// Speed selector
var speedButtons []h.H
for i, preset := range snake.SpeedPresets {
btnClass := "btn btn-sm"
if i == selectedSpeedIndex {
btnClass += " btn-active"
}
var click h.H
if i < len(speedSelectClicks) {
click = speedSelectClicks[i]
}
speedButtons = append(speedButtons, h.Button(
h.Class(btnClass),
h.Type("button"),
h.Text(preset.Name),
click,
))
}
speedSelector := h.Div(h.Class("mb-4"),
h.Label(h.Class("label"), h.Text("Speed")),
h.Div(append([]h.H{h.Class("btn-group")}, speedButtons...)...),
)
soloSection := h.Div(h.Class("mb-6"),
h.H3(h.Class("text-lg font-bold mb-2"), h.Text("Play Solo")),
h.Div(append([]h.H{h.Class("flex gap-2 justify-center")}, soloButtons...)...),
)
multiSection := h.Div(h.Class("mb-6"),
h.H3(h.Class("text-lg font-bold mb-2"), h.Text("Create Multiplayer Game")),
h.Div(append([]h.H{h.Class("flex gap-2 justify-center")}, multiButtons...)...),
)
var gameListEl h.H
if len(activeGames) > 0 {
var items []h.H
for _, g := range activeGames {
playerCount := g.PlayerCount()
sizeLabel := fmt.Sprintf("%d×%d", g.State.Width, g.State.Height)
statusLabel := "Waiting"
if g.Status == snake.StatusCountdown {
statusLabel = "Starting soon"
}
items = append(items, h.A(
h.Href("/snake/"+g.ID),
h.Class("flex justify-between items-center p-3 bg-base-200 rounded-lg hover:bg-base-300 no-underline text-base-content"),
h.Span(h.Text(fmt.Sprintf("%s — %d/8 players", sizeLabel, playerCount))),
h.Span(h.Class("text-sm opacity-60"), h.Text(statusLabel)),
))
}
listAttrs := []h.H{h.Class("flex flex-col gap-2")}
listAttrs = append(listAttrs, items...)
gameListEl = h.Div(h.Class("mt-6"),
h.H3(h.Class("text-lg font-bold mb-2 text-center"), h.Text("Join a Game")),
h.Div(listAttrs...),
)
}
return h.Div(
nicknameField,
speedSelector,
soloSection,
multiSection,
gameListEl,
)
}