diff --git a/game/session.go b/game/session.go index 84c219d..ffa611f 100644 --- a/game/session.go +++ b/game/session.go @@ -293,6 +293,13 @@ func (s *GameSession) BuyItem(playerID string, itemIdx int) bool { return false } +// SendChat appends a chat message to the combat log +func (s *GameSession) SendChat(playerName, message string) { + s.mu.Lock() + defer s.mu.Unlock() + s.addLog(fmt.Sprintf("[%s] %s", playerName, message)) +} + // LeaveShop exits the shop phase func (s *GameSession) LeaveShop() { s.mu.Lock() diff --git a/ui/game_view.go b/ui/game_view.go index 8c65ec5..7931ad7 100644 --- a/ui/game_view.go +++ b/ui/game_view.go @@ -11,11 +11,17 @@ import ( "github.com/tolelom/catacombs/game" ) -func renderGame(state game.GameState, width, height int, targetCursor int, moveCursor int) string { +func renderGame(state game.GameState, width, height int, targetCursor int, moveCursor int, chatting bool, chatInput string) string { mapView := renderMap(state.Floor) hudView := renderHUD(state, targetCursor, moveCursor) logView := renderCombatLog(state.CombatLog) + if chatting { + chatStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("117")) + chatView := chatStyle.Render(fmt.Sprintf("> %s_", chatInput)) + return lipgloss.JoinVertical(lipgloss.Left, mapView, hudView, logView, chatView) + } + return lipgloss.JoinVertical(lipgloss.Left, mapView, hudView, diff --git a/ui/model.go b/ui/model.go index cbb2816..2b6666c 100644 --- a/ui/model.go +++ b/ui/model.go @@ -47,6 +47,8 @@ type Model struct { inputBuffer string targetCursor int moveCursor int // selected neighbor index during exploration + chatting bool + chatInput string rankingSaved bool } @@ -117,7 +119,7 @@ func (m Model) View() string { case screenClassSelect: return renderClassSelect(m.classState, m.width, m.height) case screenGame: - return renderGame(m.gameState, m.width, m.height, m.targetCursor, m.moveCursor) + return renderGame(m.gameState, m.width, m.height, m.targetCursor, m.moveCursor, m.chatting, m.chatInput) case screenShop: return renderShop(m.gameState, m.width, m.height) case screenResult: @@ -328,6 +330,38 @@ func (m Model) updateGame(msg tea.Msg) (tea.Model, tea.Cmd) { } if key, ok := msg.(tea.KeyMsg); ok { + // Chat mode + if m.chatting { + if isEnter(key) && len(m.chatInput) > 0 { + if m.session != nil { + m.session.SendChat(m.playerName, m.chatInput) + m.gameState = m.session.GetState() + } + m.chatting = false + m.chatInput = "" + } else if isKey(key, "esc") || key.Type == tea.KeyEsc { + m.chatting = false + m.chatInput = "" + } else if key.Type == tea.KeyBackspace && len(m.chatInput) > 0 { + m.chatInput = m.chatInput[:len(m.chatInput)-1] + } else if len(key.Runes) == 1 && len(m.chatInput) < 40 { + m.chatInput += string(key.Runes) + } + if m.gameState.Phase == game.PhaseCombat { + return m, m.pollState() + } + return m, nil + } + + if isKey(key, "/") { + m.chatting = true + m.chatInput = "" + if m.gameState.Phase == game.PhaseCombat { + return m, m.pollState() + } + return m, nil + } + switch m.gameState.Phase { case game.PhaseExploring: neighbors := m.getNeighbors()