package cardsim import ( "fmt" "os" "strconv" "strings" ) func RunSimpleTerminalUI[C StatsCollection](p *Player[C]) error { for { err := p.StartNextTurn() if p.DebugLevel < 1 && IsSeriousError(err) { return err } p.ReportError(err) for p.CanAct() { isCard, cardIdx, choiceIdx := pickNextAction(p) var msg Message if isCard { msg, err = p.EnactCard(cardIdx, choiceIdx) } else { msg, err = p.EnactPermanentAction(cardIdx, choiceIdx) } p.ReportError(err) if p.DebugLevel < 1 && IsSeriousError(err) { return err } display(msg) wait() } review(p) err = p.Simulate() if p.DebugLevel < 1 && IsSeriousError(err) { return err } if p.DebugLevel < 1 && p.State.Over() { return nil } } return nil } func display(m Message) { if m == nil { return } fmt.Println(m.String()) } func wait() { fmt.Println() fmt.Println("") fmt.Scanln() } func pickNextAction[C StatsCollection](p *Player[C]) (isCard bool, cardIdx int, choiceIdx int) { for { cls() needsDivider := displayMessageSection(p) if needsDivider { divider() } displayOnePanel(p, p.Prompt) divider() actionsOffset := displayStatsMenu(p) if actionsOffset > 0 { divider() } handOffset := displayPermanentActionsMenu(p, actionsOffset) if handOffset > actionsOffset { fmt.Println() } max := displayHandMenu(p, handOffset) divider() var input string fmt.Printf("Show just (M)essages, (S)tats, (A)ctions, make a choice (1-%d), or (Q)uit? >", max+1) fmt.Scanln(&input) input = strings.TrimSpace(input) input = strings.ToLower(input) switch input { // Special cases case "m": cls() displayMessageSection(p) wait() case "s": statsMode(p) case "a": actionsMode(p) case "q": confirmQuit() default: i, err := strconv.Atoi(input) if err != nil { fmt.Println("Sorry, I don't understand.") wait() return pickNextAction(p) } if i > max { fmt.Println("That's not a valid action.") wait() return pickNextAction(p) } i -= 1 if i < actionsOffset { cls() displayOnePanel(p, p.InfoPanels[i]) wait() } else if i < handOffset { cls() i -= actionsOffset option, ok := promptPermanentAction(p, i) if ok { return false, i, option } } else { cls() i -= handOffset option, ok := promptCard(p, i) if ok { return true, i, option } } } } } func cls() { fmt.Println("\033[H\033[2J") } func divider() { fmt.Println() fmt.Println(SectionBreak.String()) fmt.Println() } func confirmQuit() { divider() fmt.Println("Are you sure you want to quit? (Y/N) ") var s string fmt.Scanln(&s) s = strings.TrimSpace(s) s = strings.ToLower(s) if strings.HasPrefix(s, "y") { fmt.Println("Bye!") os.Exit(0) } } func displayOnePanel[C StatsCollection](p *Player[C], panel InfoPanel[C]) error { ts := panel.Title(p).String() if len(ts) > 0 { fmt.Println(ts) fmt.Println(strings.Repeat("-", len(ts))) fmt.Println() } m, err := panel.Info(p) if IsSeriousError(err) { return err } display(MultiMessage(m)) return err } func displayMessageSection[C StatsCollection](p *Player[C]) bool { if len(p.TemporaryMessages) == 0 { return false } display(MultiMessage(p.TemporaryMessages)) return true } func displayStatsMenu[C StatsCollection](p *Player[C]) int { if len(p.InfoPanels) == 0 { return 0 } fmt.Println("Info Panels") fmt.Println("-----------") for i, s := range p.InfoPanels { fmt.Printf("[%2d]: %s\n", i+1, s.Title(p).String()) } return len(p.InfoPanels) } func displayPermanentActionsMenu[C StatsCollection](p *Player[C], offset int) int { if len(p.PermanentActions) == 0 { return offset } fmt.Println("Always Available") fmt.Println("----------------") for i, s := range p.PermanentActions { fmt.Printf("[%2d]: %s\n", i+offset+1, s.Title(p)) } return offset + len(p.PermanentActions) } func displayHandMenu[C StatsCollection](p *Player[C], offset int) int { if len(p.Hand) == 0 { return offset } fmt.Println("Hand") fmt.Println("----") for i, s := range p.Hand { fmt.Printf("[%2d]: %s\n", i+offset+1, s.Title(p)) } return offset + len(p.Hand) }