diff --git a/cmd/signup/main.go b/cmd/signup/main.go index f39daf2..3341fe7 100644 --- a/cmd/signup/main.go +++ b/cmd/signup/main.go @@ -3,6 +3,7 @@ package main import ( "bytes" "fmt" + "io" "log" "os" "path" @@ -15,9 +16,6 @@ import ( "github.com/rivo/tview" ) -// TODO remove sidebar -// TODO add /help - const ( maxInputLength = 10000 logDir = "/town/var/signups/log" @@ -27,7 +25,59 @@ type scene struct { Name string Description string Host *character - Write func([]byte) + SorryMsg string + Input *bytes.Buffer + OnAdvance func(*scene) +} + +func newScene(name, desc, sorryMsg string, host *character, onAdvance func(*scene)) *scene { + return &scene{ + Name: name, + Description: desc, + SorryMsg: sorryMsg, + Host: host, + Input: bytes.NewBuffer([]byte{}), + OnAdvance: onAdvance, + } +} + +type sceneManager struct { + scenes []*scene + Current *scene + index int + output io.Writer + Save func() +} + +func (m *sceneManager) Advance() bool { + if m.Current.Name == "done" { + return false + } + if m.Current.Input.Len() == 0 { + fmt.Fprintln(m.output, m.Current.Host.Say(m.Current.SorryMsg)) + return false + } + m.Current.OnAdvance(m.Current) + m.index++ + m.Current = m.scenes[m.index] + if m.Current.Name == "done" { + m.Save() + } + fmt.Fprintln(m.output, heredoc.Doc(` + + [purple]----------[-:-:-] + + `)) + fmt.Fprintln(m.output, m.Current.Description) + return true +} + +func newSceneManager(output io.Writer, scenes []*scene) *sceneManager { + return &sceneManager{ + output: output, + scenes: scenes, + Current: scenes[0], + } } type character struct { @@ -47,7 +97,9 @@ func (c *character) Say(msg string) string { if c.Name == "you" { verb = "say" } - return fmt.Sprintf("[-:-:b]%s[-:-:-] %s: '%s'", + return fmt.Sprintf(`[-:-:b]%s %s:[-:-:-] + %s +`, c.Name, verb, strings.TrimSpace(msg)) @@ -80,10 +132,9 @@ func _main(l *log.Logger, db *signup.DB) error { l.Println("starting a session") pages := tview.NewPages() mainFlex := tview.NewFlex() - innerFlex := tview.NewFlex() input := tview.NewTextArea() input.SetBorder(true).SetBorderColor(tcell.ColorPaleTurquoise) - input.SetTitle("press ctrl+d to send") + input.SetTitle("press enter to send") input.SetMaxLength(2000) title := tview.NewTextView() @@ -91,37 +142,19 @@ func _main(l *log.Logger, db *signup.DB) error { title.SetTextAlign(tview.AlignCenter) title.SetText("[purple]the tilde town sign up portal[-]") + footer := tview.NewTextView() + footer.SetText("type /help and press enter to get help") + msgScroll := tview.NewTextView() msgScroll.SetDynamicColors(true) msgScroll.SetBackgroundColor(tcell.ColorBlack) msgScroll.SetTextColor(tcell.ColorWhite) - sidebar := tview.NewTextView() - sidebar.SetBorder(true) - sidebar.SetDynamicColors(true) - sidebar.SetBackgroundColor(tcell.ColorBlack) - sidebar.SetTextColor(tcell.ColorGray) - sidebar.SetText(heredoc.Doc(` - [-:-:b]hey here are some hints[-:-:-] - - quit by pressing [-:-:b]ctrl-c[-:-:-] - (your responses won't be saved) - - type stuff. send it with [-:-:b]ctrl+d[-:-:-] - - try a [-:-:b]verb[-:-:-] using [-:-:b]/[-:-:-] like: - - [-:-:b]/nod[-:-:-] - `)) - - innerFlex.SetDirection(tview.FlexColumn) - innerFlex.AddItem(msgScroll, 0, 3, false) - innerFlex.AddItem(sidebar, 0, 1, false) - mainFlex.SetDirection(tview.FlexRow) mainFlex.AddItem(title, 1, -1, false) - mainFlex.AddItem(innerFlex, 0, 1, false) - mainFlex.AddItem(input, 5, -1, true) + mainFlex.AddItem(msgScroll, 0, 1, false) + mainFlex.AddItem(input, 4, -1, true) + mainFlex.AddItem(footer, 1, -1, false) pages.AddPage("main", mainFlex, true, true) @@ -144,9 +177,7 @@ func _main(l *log.Logger, db *signup.DB) error { } scenes := []*scene{ - { - Name: "start", - Description: heredoc.Doc(` + newScene("start", heredoc.Doc(` You open your eyes. You're in some kind of workshop. @@ -159,18 +190,15 @@ func _main(l *log.Logger, db *signup.DB) error { [-:-:b]wire guy says:[-:-:-] hello, welcome to the application for membership in tilde town. first, please let me know what a good [-:-:b]email address[-:-:-] is for you? + just say it out loud. as many times as you need. to get it right. - when you're ready to move on, [-:-:b]/nod[-:-:-] + once you've told me your email you can [-:-:b]/nod[-:-:-] to move on. `), - Host: newCharacter("wire guy", "a lil homonculus made of discarded computer cables"), - Write: func(b []byte) { - su.Email = string(b) - }, - }, - { - Name: "nodded", - Description: heredoc.Doc(` + "i'm sorry, before going further could you share an email with me?", + newCharacter("wire guy", "a lil homonculus made of discarded computer cables"), + func(s *scene) { su.Email = string(s.Input.Bytes()) }), + newScene("how", heredoc.Doc(` The workshop fades away. You hear the sound of a dial up modem in the distance. @@ -185,17 +213,13 @@ func _main(l *log.Logger, db *signup.DB) error { [-:-:b]the shrike says:[-:-:-] phweeturpff. how did you find out about the town? did anyone refer you? - just say your answer out loud. when you've said what you want, [-:-:b]/lean[-:-:-] - against a tree. + just say your answer out loud. when you've said what you want, [-:-:b]/nod[-:-:-] + to continue. `), - Write: func(b []byte) { - su.How = string(b) - }, - Host: newCharacter("the shrike", "a little grey bird. it has a pretty song."), - }, - { - Name: "leaned", - Description: heredoc.Doc(` + "phweeturpff", + newCharacter("the shrike", "a little grey bird. it has a pretty song."), + func(s *scene) { su.How = string(s.Input.Bytes()) }), + newScene("what", heredoc.Doc(` You sink backwards into the forest. You find yourself floating in darkness. At the far reaches of your vision you can make out a faint neon grid. Around you @@ -209,14 +233,10 @@ func _main(l *log.Logger, db *signup.DB) error { as usual, just say your answer. when you're satisfied, please [-:-:b]/spin[-:-:-] around in this void. `), - Write: func(b []byte) { - su.Why = string(b) - }, - Host: newCharacter("the vcr", "a black and grey VCR from 1991"), - }, - { - Name: "spun", - Description: heredoc.Doc(` + "hmm did you say something?", + newCharacter("the vcr", "a black and grey VCR from 1991"), + func(s *scene) { su.Why = string(s.Input.Bytes()) }), + newScene("link", heredoc.Doc(` You realize your eyes have been shut. You open them and, in an instant, the neon grid and polygons are gone. You're in a convenience store. Outside it's dark besides a single pool of light coming from a street lamp. it's illuminating @@ -235,14 +255,10 @@ func _main(l *log.Logger, db *signup.DB) error { when you're happy you can submit this whole experience by leaving the store. just [-:-:b]/open[-:-:-] the door. `), - Write: func(b []byte) { - su.Links = string(b) - }, - Host: newCharacter("the mop", "a greying mop with a wooden handle."), - }, - { - Name: "done", - Description: heredoc.Doc(` + "just the one last thing please", + newCharacter("the mop", "a greying mop with a wooden handle."), + func(s *scene) { su.Links = string(s.Input.Bytes()) }), + newScene("done", heredoc.Doc(` thank you for applying to tilde.town! please be on the look out for an email from [-:-:b]root@tilde.town[-:-:-] @@ -251,38 +267,13 @@ func _main(l *log.Logger, db *signup.DB) error { ok bye have a good one~ `), - Write: func(b []byte) { - su.Extra = string(b) - }, - }, + "", + nil, + func(s *scene) { su.Extra = string(s.Input.Bytes()) }), } - sceneIx := 0 - currentScene := scenes[sceneIx] - - inputWriter := bytes.NewBuffer([]byte{}) - - advanceScene := func(fromScene, sorryMsg string) { - if currentScene.Name != fromScene { - return - } - if inputWriter.Len() == 0 { - fmt.Fprintln(msgScroll, currentScene.Host.Say(sorryMsg)) - return - } - l.Println("advancing scene") - - currentScene.Write(inputWriter.Bytes()) - inputWriter = bytes.NewBuffer([]byte{}) - sceneIx++ - currentScene = scenes[sceneIx] - fmt.Fprintln(msgScroll, heredoc.Doc(` - - [purple]----------[-:-:-] - - `)) - fmt.Fprintln(msgScroll, currentScene.Description) - } + sm := newSceneManager(msgScroll, scenes) + sm.Save = save handleInput := func(msg string) { msg = strings.TrimSpace(msg) @@ -295,37 +286,42 @@ func _main(l *log.Logger, db *signup.DB) error { msg = split[0] } switch strings.TrimPrefix(msg, "/") { + case "help": + fmt.Fprintln(msgScroll, sm.Current.Host.Say(`some artificial beings will guide you through applying to tilde.town. + type things, then press enter. to take an action, put a "/" before a word. + for example typing: + /nod + and pressing enter will cause you to nod. some other verbs: /quit /look`)) case "quit": l.Println("got /quit") app.Stop() case "look": fmt.Fprintln(msgScroll, "") - fmt.Fprintln(msgScroll, currentScene.Description) + fmt.Fprintln(msgScroll, sm.Current.Description) case "nod": - advanceScene("start", - "i'm sorry, before going further could you share an email with me?") - case "lean": - advanceScene("nodded", "phweeturpff") - case "spin": - advanceScene("leaned", "hmm did you say something?") - case "open": - advanceScene("spun", "just the one last thing please") - save() + if !sm.Advance() { + fmt.Fprintln(msgScroll, "you nod, but nothing happens.") + fmt.Fprintln(msgScroll) + } else { + l.Println("advancing scene") + } } return } - if inputWriter.Len() > maxInputLength { + if sm.Current.Input.Len() > maxInputLength { fmt.Fprintln(msgScroll, - currentScene.Host.Say("sorry I've heard more than I can remember :( maybe it's time to move on")) + sm.Current.Host.Say( + "sorry I've heard more than I can remember :( maybe it's time to move on")) return } fmt.Fprintln(msgScroll, player.Say(msg)) - fmt.Fprintln(inputWriter, msg) + fmt.Fprintln(sm.Current.Input, msg) } defer func() { - if currentScene.Name == "done" { - currentScene.Write(inputWriter.Bytes()) + l.Println("exiting") + if sm.Current.Name == "done" { + sm.Current.OnAdvance(sm.Current) db.UpdateSignup(su) } db.Close() @@ -333,7 +329,7 @@ func _main(l *log.Logger, db *signup.DB) error { app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { switch event.Key() { - case tcell.KeyCtrlD: + case tcell.KeyEnter: handleInput(input.GetText()) input.SetText("", false) return nil @@ -343,7 +339,7 @@ func _main(l *log.Logger, db *signup.DB) error { }) app.SetAfterDrawFunc(func(_ tcell.Screen) { - fmt.Fprintln(msgScroll, currentScene.Description) + fmt.Fprintln(msgScroll, sm.Current.Description) app.SetAfterDrawFunc(nil) })