clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

chore/regenerate-vhs-gifs
puzzletea / app / update.go
update.go 6.3K
1package app23import (4 "time"56 "github.com/FelineStateMachine/puzzletea/game"7 "github.com/FelineStateMachine/puzzletea/store"8 "github.com/FelineStateMachine/puzzletea/theme"9 "github.com/FelineStateMachine/puzzletea/ui"1011 "charm.land/bubbles/v2/key"12 tea "charm.land/bubbletea/v2"13)1415func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {16 switch msg := msg.(type) {17 case spawnCompleteMsg:18 next, cmd := m.handleSpawnComplete(msg.jobID, msg.result)19 return next, cmd20 case game.SpawnCompleteMsg:21 next, cmd := m.handleSpawnComplete(m.session.spawnJobID, msg)22 return next, cmd23 case exportCompleteMsg:24 return m.handleExportComplete(msg)25 case tea.WindowSizeMsg:26 m = m.handleWindowSize(msg)27 if m.state != gameView {28 return m, nil29 }30 }3132 next, cmd, handled := m.handleGlobalKey(msg)33 if handled {34 return next, cmd35 }36 m = next3738 if m.state == gameView {39 gameCmd := newSessionController(&m).updateActiveGame(msg)40 return m, gameCmd41 }4243 screen := m.activeScreen()44 if screen == nil {45 return m, nil46 }4748 nextScreen, screenCmd, action := screen.Update(msg)49 m = nextScreen.Apply(m)50 if action == nil {51 return m, screenCmd52 }5354 next, actionCmd := m.handleScreenAction(action)55 return next, tea.Batch(screenCmd, actionCmd)56}5758func (m model) resizeActiveScreen() model {59 screen := m.activeScreen()60 if screen == nil {61 return m62 }63 return screen.Resize(m.width, m.height).Apply(m)64}6566func (m model) handleWindowSize(msg tea.WindowSizeMsg) model {67 m.width = msg.Width68 m.height = msg.Height69 if m.state == gameView {70 return m71 }72 return m.resizeActiveScreen()73}7475func (m model) handleGlobalKey(msg tea.Msg) (model, tea.Cmd, bool) {76 keyMsg, ok := msg.(tea.KeyPressMsg)77 if !ok {78 return m, nil, false79 }8081 if m.state == generatingView {82 switch {83 case key.Matches(keyMsg, rootKeys.Escape):84 returnState := m.activeSpawnReturnState()85 m.cancelActiveSpawn()86 m.state = returnState87 return m.resizeActiveScreen(), nil, true88 case key.Matches(keyMsg, rootKeys.Quit):89 return m, tea.Quit, true90 }91 return m, nil, true92 }9394 if m.state == exportRunningView {95 switch {96 case key.Matches(keyMsg, rootKeys.Escape):97 m.cancelActiveExport()98 m.state = exportView99 return m.resizeActiveScreen(), nil, true100 case key.Matches(keyMsg, rootKeys.Quit):101 return m, tea.Quit, true102 }103 return m, nil, true104 }105106 if m.state == gameView {107 switch {108 case key.Matches(keyMsg, rootKeys.Enter):109 next, cmd, handled := m.advanceSolvedWeekly()110 if handled {111 return next, cmd, true112 }113 case key.Matches(keyMsg, rootKeys.Escape):114 returnState := m.session.returnState115 m = saveCurrentGame(m, store.StatusInProgress)116 m.state = returnState117 if returnState == weeklyView {118 m = m.refreshWeeklyBrowser()119 }120 m.debug.enabled = false121 return m, nil, true122 case key.Matches(keyMsg, rootKeys.Quit):123 m = saveCurrentGame(m, store.StatusAbandoned)124 return m, tea.Quit, true125 case key.Matches(keyMsg, rootKeys.Debug):126 m.debug.enabled = !m.debug.enabled127 return m, nil, true128 case key.Matches(keyMsg, rootKeys.FullHelp):129 m.help.showFull = !m.help.showFull130 if m.session.game != nil {131 m.session.game, _ = m.session.game.Update(game.HelpToggleMsg{Show: m.help.showFull})132 }133 return m, nil, true134 case key.Matches(keyMsg, rootKeys.ResetGame):135 if m.session.game != nil {136 m.session.game = m.session.game.Reset()137 }138 return m, nil, true139 }140 return m, nil, false141 }142143 switch {144 case key.Matches(keyMsg, rootKeys.Quit):145 return m, tea.Quit, true146 case key.Matches(keyMsg, rootKeys.Debug):147 m.debug.enabled = !m.debug.enabled148 return m, nil, true149 case key.Matches(keyMsg, rootKeys.FullHelp):150 m.help.showFull = !m.help.showFull151 return m, nil, true152 default:153 return m, nil, false154 }155}156157func (m model) activeSpawnReturnState() viewState {158 if m.session.spawn == nil {159 return modeSelectView160 }161 return m.session.spawn.returnState162}163164func (m model) handleScreenAction(action screenAction) (model, tea.Cmd) {165 switch action := action.(type) {166 case openPlayMenuAction:167 m.nav.playMenu = ui.NewMainMenu(buildPlayMenuItems(time.Now(), m.currentWeeklyMenuIndex()))168 m.state = playMenuView169 m = m.clearNotice()170 return m.resizeActiveScreen(), nil171 case openStatsAction:172 return asModel(m.handleStatsEnter())173 case openOptionsMenuAction:174 m.nav.optionsMenu = ui.NewMainMenu(optionsMenuItems)175 m.state = optionsMenuView176 m = m.clearNotice()177 return m.resizeActiveScreen(), nil178 case quitAction:179 return m, tea.Quit180 case openGameSelectAction:181 m.state = gameSelectView182 m = m.updateCategoryDetailViewport()183 m = m.clearNotice()184 return m.resizeActiveScreen(), nil185 case openContinueAction:186 m.cont.table, m.cont.games = ui.InitContinueTable(m.store, m.height)187 m.state = continueView188 m = m.clearNotice()189 return m.resizeActiveScreen(), nil190 case openDailyAction:191 return asModel(m.handleDailyPuzzle())192 case openExportAction:193 return asModel(m.handleExportEnter())194 case openWeeklyAction:195 return asModel(m.enterWeeklyView())196 case openSeedInputAction:197 return m.enterSeedInputView()198 case backAction:199 if m.state == themeSelectView {200 _ = theme.Apply(m.theme.previous)201 }202 m.state = action.target203 return m.resizeActiveScreen(), nil204 case gameSelectEnterAction:205 return asModel(m.handleGameSelectEnter())206 case modeSelectEnterAction:207 return asModel(m.handleModeSelectEnter())208 case continueEnterAction:209 return asModel(m.handleContinueEnter())210 case weeklyShiftAction:211 m = m.moveWeeklyWeek(action.delta)212 return m, nil213 case weeklyEnterAction:214 return asModel(m.handleWeeklyEnter())215 case helpSelectEnterAction:216 return asModel(m.handleHelpSelectEnter())217 case openThemeSelectAction:218 return asModel(m.handleThemeEnter())219 case openHelpSelectAction:220 m.help.selectList = ui.InitList(gameCategoryItems, "How to Play")221 listWidth, listHeight := helpSelectListSize(m.width, m.height, m.help.selectList)222 m.help.selectList.SetSize(listWidth, listHeight)223 m.state = helpSelectView224 m = m.clearNotice()225 return m.resizeActiveScreen(), nil226 case previewThemeAction:227 _ = theme.Apply(action.name)228 ui.UpdateThemeListStyles(&m.theme.list)229 return m, nil230 case confirmThemeAction:231 return asModel(m.handleThemeConfirm())232 case seedConfirmAction:233 return asModel(m.handleSeedConfirm())234 case exportSubmitAction:235 return asModel(m.handleExportSubmit())236 default:237 return m, nil238 }239}240241func asModel(next tea.Model, cmd tea.Cmd) (model, tea.Cmd) {242 return next.(model), cmd243}