fix: remove debug, save rankings, block dead actions, save nickname

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-24 00:51:04 +09:00
parent 743b5b9058
commit 6809e49226

View File

@@ -2,6 +2,7 @@ package ui
import ( import (
"fmt" "fmt"
"time"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/tolelom/catacombs/entity" "github.com/tolelom/catacombs/entity"
@@ -46,6 +47,12 @@ type Model struct {
} }
func NewModel(width, height int, fingerprint string, lobby *game.Lobby, db *store.DB) Model { func NewModel(width, height int, fingerprint string, lobby *game.Lobby, db *store.DB) Model {
if width == 0 {
width = 80
}
if height == 0 {
height = 24
}
return Model{ return Model{
width: width, width: width,
height: height, height: height,
@@ -65,6 +72,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
m.width = msg.Width m.width = msg.Width
m.height = msg.Height m.height = msg.Height
if m.width == 0 {
m.width = 80
}
if m.height == 0 {
m.height = 24
}
return m, nil return m, nil
case StateUpdateMsg: case StateUpdateMsg:
m.gameState = msg.State m.gameState = msg.State
@@ -113,14 +126,42 @@ func (m Model) View() string {
return "" return ""
} }
func isKey(key tea.KeyMsg, names ...string) bool {
s := key.String()
for _, n := range names {
if s == n {
return true
}
}
return false
}
func isEnter(key tea.KeyMsg) bool {
return isKey(key, "enter") || key.Type == tea.KeyEnter
}
func isQuit(key tea.KeyMsg) bool {
return isKey(key, "q", "ctrl+c") || key.Type == tea.KeyCtrlC
}
func isUp(key tea.KeyMsg) bool {
return isKey(key, "up") || key.Type == tea.KeyUp
}
func isDown(key tea.KeyMsg) bool {
return isKey(key, "down") || key.Type == tea.KeyDown
}
func (m Model) updateTitle(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) updateTitle(msg tea.Msg) (tea.Model, tea.Cmd) {
if key, ok := msg.(tea.KeyMsg); ok { if key, ok := msg.(tea.KeyMsg); ok {
switch key.String() { if isEnter(key) {
case "enter":
if m.store != nil { if m.store != nil {
name, err := m.store.GetProfile(m.fingerprint) name, err := m.store.GetProfile(m.fingerprint)
if err != nil { if err != nil {
m.playerName = "Adventurer" m.playerName = "Adventurer"
if m.store != nil && m.fingerprint != "" {
m.store.SaveProfile(m.fingerprint, m.playerName)
}
} else { } else {
m.playerName = name m.playerName = name
} }
@@ -128,8 +169,8 @@ func (m Model) updateTitle(msg tea.Msg) (tea.Model, tea.Cmd) {
m.playerName = "Adventurer" m.playerName = "Adventurer"
} }
m.screen = screenLobby m.screen = screenLobby
m.refreshLobbyState() m = m.withRefreshedLobby()
case "q", "ctrl+c": } else if isQuit(key) {
return m, tea.Quit return m, tea.Quit
} }
} }
@@ -138,23 +179,22 @@ func (m Model) updateTitle(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m Model) updateLobby(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) updateLobby(msg tea.Msg) (tea.Model, tea.Cmd) {
if key, ok := msg.(tea.KeyMsg); ok { if key, ok := msg.(tea.KeyMsg); ok {
switch key.String() { if isKey(key, "c") {
case "c":
if m.lobby != nil { if m.lobby != nil {
code := m.lobby.CreateRoom(m.playerName + "'s Room") code := m.lobby.CreateRoom(m.playerName + "'s Room")
m.lobby.JoinRoom(code, m.playerName) m.lobby.JoinRoom(code, m.playerName)
m.roomCode = code m.roomCode = code
m.screen = screenClassSelect m.screen = screenClassSelect
} }
case "up": } else if isUp(key) {
if m.lobbyState.cursor > 0 { if m.lobbyState.cursor > 0 {
m.lobbyState.cursor-- m.lobbyState.cursor--
} }
case "down": } else if isDown(key) {
if m.lobbyState.cursor < len(m.lobbyState.rooms)-1 { if m.lobbyState.cursor < len(m.lobbyState.rooms)-1 {
m.lobbyState.cursor++ m.lobbyState.cursor++
} }
case "enter": } else if isEnter(key) {
if m.lobby != nil && len(m.lobbyState.rooms) > 0 { if m.lobby != nil && len(m.lobbyState.rooms) > 0 {
r := m.lobbyState.rooms[m.lobbyState.cursor] r := m.lobbyState.rooms[m.lobbyState.cursor]
if err := m.lobby.JoinRoom(r.Code, m.playerName); err == nil { if err := m.lobby.JoinRoom(r.Code, m.playerName); err == nil {
@@ -162,7 +202,7 @@ func (m Model) updateLobby(msg tea.Msg) (tea.Model, tea.Cmd) {
m.screen = screenClassSelect m.screen = screenClassSelect
} }
} }
case "q": } else if isKey(key, "q") {
m.screen = screenTitle m.screen = screenTitle
} }
} }
@@ -171,16 +211,15 @@ func (m Model) updateLobby(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m Model) updateClassSelect(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) updateClassSelect(msg tea.Msg) (tea.Model, tea.Cmd) {
if key, ok := msg.(tea.KeyMsg); ok { if key, ok := msg.(tea.KeyMsg); ok {
switch key.String() { if isUp(key) {
case "up":
if m.classState.cursor > 0 { if m.classState.cursor > 0 {
m.classState.cursor-- m.classState.cursor--
} }
case "down": } else if isDown(key) {
if m.classState.cursor < len(classOptions)-1 { if m.classState.cursor < len(classOptions)-1 {
m.classState.cursor++ m.classState.cursor++
} }
case "enter": } else if isEnter(key) {
if m.lobby != nil { if m.lobby != nil {
selectedClass := classOptions[m.classState.cursor].class selectedClass := classOptions[m.classState.cursor].class
room := m.lobby.GetRoom(m.roomCode) room := m.lobby.GetRoom(m.roomCode)
@@ -202,8 +241,29 @@ func (m Model) updateClassSelect(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
// pollState returns a Cmd that waits briefly then refreshes game state
func (m Model) pollState() tea.Cmd {
return tea.Tick(time.Millisecond*200, func(t time.Time) tea.Msg {
return tickMsg{}
})
}
type tickMsg struct{}
func (m Model) updateGame(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) updateGame(msg tea.Msg) (tea.Model, tea.Cmd) {
// Refresh state on every update
if m.session != nil {
m.gameState = m.session.GetState()
}
if m.gameState.GameOver { if m.gameState.GameOver {
if m.store != nil {
score := 0
for _, p := range m.gameState.Players {
score += p.Gold
}
m.store.SaveRun(m.playerName, m.gameState.FloorNum, score)
}
m.screen = screenResult m.screen = screenResult
return m, nil return m, nil
} }
@@ -212,6 +272,15 @@ func (m Model) updateGame(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
switch msg.(type) {
case tickMsg:
// State already refreshed above, just keep polling during combat
if m.gameState.Phase == game.PhaseCombat {
return m, m.pollState()
}
return m, nil
}
if key, ok := msg.(tea.KeyMsg); ok { if key, ok := msg.(tea.KeyMsg); ok {
switch m.gameState.Phase { switch m.gameState.Phase {
case game.PhaseExploring: case game.PhaseExploring:
@@ -220,9 +289,23 @@ func (m Model) updateGame(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.session != nil { if m.session != nil {
m.session.EnterRoom(idx) m.session.EnterRoom(idx)
m.gameState = m.session.GetState() m.gameState = m.session.GetState()
// If combat started, begin polling
if m.gameState.Phase == game.PhaseCombat {
return m, m.pollState()
}
} }
} }
case game.PhaseCombat: case game.PhaseCombat:
isPlayerDead := false
for _, p := range m.gameState.Players {
if p.Name == m.playerName && p.IsDead() {
isPlayerDead = true
break
}
}
if isPlayerDead {
return m, m.pollState()
}
if m.session != nil { if m.session != nil {
switch key.String() { switch key.String() {
case "1": case "1":
@@ -236,6 +319,8 @@ func (m Model) updateGame(msg tea.Msg) (tea.Model, tea.Cmd) {
case "5": case "5":
m.session.SubmitAction(m.playerName, game.PlayerAction{Type: game.ActionWait}) m.session.SubmitAction(m.playerName, game.PlayerAction{Type: game.ActionWait})
} }
// After submitting, poll for turn resolution
return m, m.pollState()
} }
} }
} }
@@ -264,20 +349,19 @@ func (m Model) updateShop(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m Model) updateResult(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) updateResult(msg tea.Msg) (tea.Model, tea.Cmd) {
if key, ok := msg.(tea.KeyMsg); ok { if key, ok := msg.(tea.KeyMsg); ok {
switch key.String() { if isEnter(key) {
case "enter":
m.screen = screenLobby m.screen = screenLobby
m.refreshLobbyState() m = m.withRefreshedLobby()
case "q", "ctrl+c": } else if isQuit(key) {
return m, tea.Quit return m, tea.Quit
} }
} }
return m, nil return m, nil
} }
func (m *Model) refreshLobbyState() { func (m Model) withRefreshedLobby() Model {
if m.lobby == nil { if m.lobby == nil {
return return m
} }
rooms := m.lobby.ListRooms() rooms := m.lobby.ListRooms()
m.lobbyState.rooms = make([]roomInfo, len(rooms)) m.lobbyState.rooms = make([]roomInfo, len(rooms))
@@ -294,4 +378,5 @@ func (m *Model) refreshLobbyState() {
} }
} }
m.lobbyState.cursor = 0 m.lobbyState.cursor = 0
return m
} }