|
@@ -0,0 +1,413 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "database/sql"
|
|
|
+ "fmt"
|
|
|
+ "math/rand"
|
|
|
+ "os"
|
|
|
+ "path/filepath"
|
|
|
+ "red-green/door"
|
|
|
+ "regexp"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+ "unicode"
|
|
|
+
|
|
|
+ _ "github.com/mattn/go-sqlite3"
|
|
|
+ "github.com/seehuhn/mt19937"
|
|
|
+)
|
|
|
+
|
|
|
+func pctUpdate(pct *int64) func() int64 {
|
|
|
+ return func() int64 {
|
|
|
+ return *pct
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Can I add a function to Door?
|
|
|
+// NO: cannot define new methods on non-local type door.Door
|
|
|
+
|
|
|
+/*
|
|
|
+func (d *door.Door) PressAKey() {
|
|
|
+ d.Write(door.Reset + door.CRNL + "Press a key to continue...")
|
|
|
+ d.Key()
|
|
|
+ d.Write(door.CRNL)
|
|
|
+}
|
|
|
+*/
|
|
|
+
|
|
|
+func find_words(text string) [][]int {
|
|
|
+ word, _ := regexp.Compile("([A-Za-z]+)")
|
|
|
+ return word.FindAllStringIndex(text, -1)
|
|
|
+}
|
|
|
+
|
|
|
+func press_a_key(d *door.Door) int {
|
|
|
+ green := door.ColorText("BOLD GREEN")
|
|
|
+ blue := door.ColorText("BOLD BLUE")
|
|
|
+ yellow := door.ColorText("BOLD YELLOW")
|
|
|
+ any_caps := green
|
|
|
+ any_lower := yellow
|
|
|
+
|
|
|
+ any_color := func(text string) string {
|
|
|
+ var output string
|
|
|
+ var lastColor string
|
|
|
+
|
|
|
+ for _, letter := range text {
|
|
|
+ if unicode.IsUpper(letter) {
|
|
|
+ if lastColor != any_caps {
|
|
|
+ lastColor = any_caps
|
|
|
+ output += any_caps
|
|
|
+ }
|
|
|
+ output += string(letter)
|
|
|
+ } else {
|
|
|
+ if lastColor != any_lower {
|
|
|
+ lastColor = any_lower
|
|
|
+ output += any_lower
|
|
|
+ }
|
|
|
+ output += string(letter)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return output
|
|
|
+ }
|
|
|
+
|
|
|
+ text := "Press a key to continue..."
|
|
|
+ work_text := []rune(text)
|
|
|
+
|
|
|
+ d.Write(door.Reset + any_color(text))
|
|
|
+
|
|
|
+ var r int = -1
|
|
|
+ var t int
|
|
|
+ sleep_ms := 250
|
|
|
+ ms_sleep := 0
|
|
|
+
|
|
|
+ words := find_words(text)
|
|
|
+ var word int = 0
|
|
|
+ var wpos int = 0
|
|
|
+ current_word := text[words[word][0]:words[word][1]]
|
|
|
+ t = 0
|
|
|
+
|
|
|
+ r = d.WaitKey(0, int64(sleep_ms)*1000)
|
|
|
+
|
|
|
+ for r == -1 {
|
|
|
+ ms_sleep += sleep_ms
|
|
|
+
|
|
|
+ if ms_sleep > 1000 {
|
|
|
+ ms_sleep -= 1000
|
|
|
+ t++
|
|
|
+ if t >= int(door.Inactivity) {
|
|
|
+ d.Write(strings.Repeat("\b", len(text)))
|
|
|
+ d.Write(any_color(text) + door.CRNL)
|
|
|
+ return -1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ wpos++
|
|
|
+
|
|
|
+ if wpos == len(current_word) {
|
|
|
+ word++
|
|
|
+ wpos = 0
|
|
|
+ work_text = []rune(text)
|
|
|
+
|
|
|
+ if word == len(words) {
|
|
|
+ word = 0
|
|
|
+ if any_caps == green {
|
|
|
+ any_caps = blue
|
|
|
+ } else {
|
|
|
+ any_caps = green
|
|
|
+ }
|
|
|
+ d.Write(strings.Repeat("\b", len(text)))
|
|
|
+ d.Write(any_color(string(work_text)))
|
|
|
+ current_word = text[words[word][0]:words[word][1]]
|
|
|
+ goto READKEY
|
|
|
+ }
|
|
|
+
|
|
|
+ current_word = text[words[word][0]:words[word][1]]
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ c := rune(current_word[wpos])
|
|
|
+
|
|
|
+ for !unicode.IsLower(c) {
|
|
|
+ wpos++
|
|
|
+ if wpos == len(current_word) {
|
|
|
+ wpos = 0
|
|
|
+ word++
|
|
|
+ work_text = []rune(text)
|
|
|
+ if word == len(words) {
|
|
|
+ word = 0
|
|
|
+ }
|
|
|
+ current_word = text[words[word][0]:words[word][1]]
|
|
|
+ }
|
|
|
+ c = rune(current_word[wpos])
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ok, we found something that's lower case
|
|
|
+ work_text[words[word][0]+wpos] = unicode.ToUpper(c)
|
|
|
+ }
|
|
|
+ d.Write(strings.Repeat("\b", len(text)))
|
|
|
+ d.Write(any_color(string(work_text)))
|
|
|
+
|
|
|
+ READKEY:
|
|
|
+ r = d.WaitKey(0, int64(sleep_ms)*1000)
|
|
|
+ }
|
|
|
+
|
|
|
+ d.Write(strings.Repeat("\b", len(text)))
|
|
|
+ d.Write(any_color(text))
|
|
|
+ return r
|
|
|
+}
|
|
|
+
|
|
|
+func MainMenu() door.Menu {
|
|
|
+ // Make the main menu
|
|
|
+ m := door.Menu{Panel: door.Panel{Width: 45,
|
|
|
+ X: 5,
|
|
|
+ Y: 5,
|
|
|
+ Style: door.DOUBLE,
|
|
|
+ Title: "[ Main Menu: ]",
|
|
|
+ TitleOffset: 3,
|
|
|
+ BorderColor: door.ColorText("BRI CYAN ON BLA")}}
|
|
|
+ m.SelectedR = door.MakeMenuRender(door.ColorText("BOLD CYAN"),
|
|
|
+ door.ColorText("BOLD BLUE"),
|
|
|
+ door.ColorText("BOLD CYAN"),
|
|
|
+ door.ColorText("BOLD BLUE"))
|
|
|
+ m.UnselectedR = door.MakeMenuRender(door.ColorText("BOLD YEL ON BLUE"),
|
|
|
+ door.ColorText("BOLD WHI ON BLUE"),
|
|
|
+ door.ColorText("BOLD YEL ON BLUE"),
|
|
|
+ door.ColorText("BOLD CYAN ON BLUE"))
|
|
|
+
|
|
|
+ m.AddSelection("A", "ANSI Display")
|
|
|
+ m.AddSelection("D", "Display Information (dropfile, screen)")
|
|
|
+ m.AddSelection("I", "Input Prompt Demo")
|
|
|
+ m.AddSelection("P", "Progress Bars Demo")
|
|
|
+ m.AddSelection("S", "Show Panel")
|
|
|
+
|
|
|
+ m.AddSelection("Q", "Quit")
|
|
|
+ return m
|
|
|
+}
|
|
|
+
|
|
|
+func display_information(d *door.Door) {
|
|
|
+ d.Write(door.Clrscr)
|
|
|
+
|
|
|
+ keyColor := door.ColorText("BRI GREEN")
|
|
|
+ sepColor := door.ColorText("BRI YEL")
|
|
|
+ valColor := door.ColorText("CYAN")
|
|
|
+ nice_format := func(key string, value string) string {
|
|
|
+ return fmt.Sprintf("%s%-20s %s: %s%s", keyColor, key, sepColor, valColor, value) + door.CRNL
|
|
|
+ }
|
|
|
+
|
|
|
+ d.Write(nice_format("BBS Software", d.Config.BBSID))
|
|
|
+ d.Write(nice_format("Real Name", d.Config.Real_name))
|
|
|
+ d.Write(nice_format("Handle", d.Config.Handle))
|
|
|
+ d.Write(nice_format("User #", strconv.Itoa(d.Config.User_number)))
|
|
|
+ d.Write(nice_format("Security Level", strconv.Itoa(d.Config.Security_level)))
|
|
|
+ d.Write(nice_format("Node #", strconv.Itoa(d.Config.Node)))
|
|
|
+ d.Write(nice_format("Unicode", strconv.FormatBool(door.Unicode)))
|
|
|
+ d.Write(nice_format("CP437", strconv.FormatBool(door.CP437)))
|
|
|
+ d.Write(nice_format("Screen Size", fmt.Sprintf("%d X %d", door.Width, door.Height)))
|
|
|
+}
|
|
|
+
|
|
|
+func display_ansi(d *door.Door) {
|
|
|
+ space := SPACE()
|
|
|
+ d.Write(door.Clrscr)
|
|
|
+ for _, line := range space {
|
|
|
+ if door.Unicode {
|
|
|
+ d.Write(door.CP437_to_Unicode(line) + door.CRNL)
|
|
|
+ } else {
|
|
|
+ d.Write(line + door.CRNL)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func input_demo(d *door.Door) {
|
|
|
+ inputColor := door.ColorText("BRI WHI ON BLUE")
|
|
|
+ inputColor2 := door.ColorText("BRI WHI ON GREEN")
|
|
|
+ prompt := door.Line{Text: "What is YOUR Name: "}
|
|
|
+ prompt.RenderF = door.RenderBlueYellow
|
|
|
+ d.Write(prompt.Output() + inputColor)
|
|
|
+ name := d.Input(25)
|
|
|
+ d.Write(door.Reset + door.CRNL)
|
|
|
+ prompt.Text = "What is Your Quest: "
|
|
|
+ d.Write(prompt.Output() + inputColor2)
|
|
|
+ quest := d.Input(35)
|
|
|
+ d.Write(door.Reset + door.CRNL)
|
|
|
+ prompt.Text = "What is your Favorite CoLoR: "
|
|
|
+ d.Write(prompt.Output() + inputColor2)
|
|
|
+ color := d.Input(15)
|
|
|
+ d.Write(door.Reset + door.CRNL)
|
|
|
+ d.Write(fmt.Sprintf("You're %s on the %s quest, and fond of %s."+door.CRNL, name, quest, color))
|
|
|
+}
|
|
|
+
|
|
|
+func progress_bars(d *door.Door) {
|
|
|
+ d.Write(door.Clrscr)
|
|
|
+
|
|
|
+ bar := door.BarLine{Line: door.Line{DefaultColor: door.ColorText("BOLD YELLOW")}, Width: 20, Style: door.HALF_STEP}
|
|
|
+ bar2 := door.BarLine{Width: 30, Style: door.SOLID, PercentStyle: door.PERCENT_SPACE}
|
|
|
+
|
|
|
+ bar2.ColorRange = []door.BarRange{
|
|
|
+ {2500, door.ColorText("RED")},
|
|
|
+ {5000, door.ColorText("BROWN")},
|
|
|
+ {7500, door.ColorText("BOLD YEL")},
|
|
|
+ {9500, door.ColorText("GREEN")},
|
|
|
+ {10100, door.ColorText("BRI GRE")}}
|
|
|
+
|
|
|
+ bar3 := door.BarLine{Width: 15, Style: door.GRADIENT, Line: door.Line{DefaultColor: door.ColorText("CYAN")}}
|
|
|
+
|
|
|
+ var percentage int64
|
|
|
+ bar.UpdateP = pctUpdate(&percentage)
|
|
|
+ bar2.UpdateP = pctUpdate(&percentage)
|
|
|
+ bar3.UpdateP = pctUpdate(&percentage)
|
|
|
+
|
|
|
+ update_bars := func() {
|
|
|
+ bar.Update()
|
|
|
+ bar2.Update()
|
|
|
+ bar3.Update()
|
|
|
+ }
|
|
|
+
|
|
|
+ d.Write(door.Goto(3, 12) + "Half-Step")
|
|
|
+ d.Write(door.Goto(25, 12) + "% with space and Color Range")
|
|
|
+ d.Write(door.Goto(57, 12) + "Gradient")
|
|
|
+
|
|
|
+ bar_start := door.Goto(3, 15)
|
|
|
+
|
|
|
+ for f := 0; f <= 100; f++ {
|
|
|
+ percentage = int64(f * 100)
|
|
|
+
|
|
|
+ update_bars()
|
|
|
+ d.Write(bar_start + bar.Output() + " " + door.Reset + bar2.Output() + door.Reset + " " + bar3.Output())
|
|
|
+
|
|
|
+ if d.Disconnected {
|
|
|
+ // don't continue to sleep if we're disconnected
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ time.Sleep(time.Millisecond * 100)
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func panel_demo(d *door.Door) {
|
|
|
+ width := 55
|
|
|
+ fmtStr := "%-55s"
|
|
|
+ p := door.Panel{X: 5, Y: 5, Width: width, Style: door.DOUBLE, BorderColor: door.ColorText("CYAN ON BLUE"), Title: "[ Panel Demo ]"}
|
|
|
+
|
|
|
+ lineColor := door.ColorText("BRIGHT WHI ON BLUE")
|
|
|
+ // Add lines to the panel
|
|
|
+ for _, line := range []string{"The BBS Door Panel Demo", "(C) 2021 Red Green Software, https://red-green.com"} {
|
|
|
+ if door.Unicode {
|
|
|
+ line = strings.Replace(line, "(C)", "\u00a9", -1)
|
|
|
+ }
|
|
|
+ l := door.Line{Text: fmt.Sprintf(fmtStr, line), DefaultColor: lineColor}
|
|
|
+ p.Lines = append(p.Lines, l)
|
|
|
+ }
|
|
|
+ p.Lines = append(p.Lines, p.Spacer())
|
|
|
+ p.Lines = append(p.Lines, door.Line{Text: fmt.Sprintf(fmtStr, "Welcome to golang!"), DefaultColor: lineColor})
|
|
|
+
|
|
|
+ d.Write(door.Clrscr)
|
|
|
+ d.Write(p.Output())
|
|
|
+}
|
|
|
+
|
|
|
+func createTable(db *sql.DB) {
|
|
|
+ db.Exec("CREATE TABLE IF NOT EXISTS settings(username TEXT, setting TEXT, value TEXT, PRIMARY KEY(username, setting));")
|
|
|
+ db.Exec("CREATE TABLE IF NOT EXISTS scores ( \"username\" TEXT, \"when\" INTEGER, \"date\" INTEGER, \"hand\" INTEGER, \"won\" INTEGER, \"score\" INTEGER, PRIMARY KEY(\"username\", \"date\", \"hand\"));")
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ var message string
|
|
|
+
|
|
|
+ // Get path to binary, and chdir to it.
|
|
|
+ binaryPath, _ := os.Executable()
|
|
|
+ binaryPath = filepath.Dir(binaryPath)
|
|
|
+ _ = os.Chdir(binaryPath)
|
|
|
+
|
|
|
+ fmt.Println("Starting space-ace")
|
|
|
+
|
|
|
+ rng := rand.New(mt19937.New())
|
|
|
+ rng.Seed(time.Now().UnixNano())
|
|
|
+
|
|
|
+ sqliteDatabase, err := sql.Open("sqlite3", "./space-database.db")
|
|
|
+ if err != nil {
|
|
|
+ fmt.Printf("%#v\n", err)
|
|
|
+ }
|
|
|
+ defer sqliteDatabase.Close()
|
|
|
+ createTable(sqliteDatabase)
|
|
|
+ d := door.Door{}
|
|
|
+
|
|
|
+ d.Init("space-ace")
|
|
|
+
|
|
|
+ s := StarField{}
|
|
|
+ s.Regenerate()
|
|
|
+
|
|
|
+ s.Display(&d)
|
|
|
+ d.Write(door.Goto(1, 1) + door.Reset)
|
|
|
+ d.Key()
|
|
|
+
|
|
|
+ bold := door.Color(1, 37, 40)
|
|
|
+ bolder := door.ColorText("BLI BOLD YEL ON BLUE")
|
|
|
+ d.Write("Welcome to " + bolder + "door32.sys" + door.Reset + door.CRNL + "..." + door.CRNL)
|
|
|
+ key := press_a_key(&d)
|
|
|
+ d.Write(fmt.Sprintf("Key %s%d / %x%s", bold, key, key, door.Reset) + door.CRNL)
|
|
|
+
|
|
|
+ b := door.AlertBox("Warning: golang is in use!", 1)
|
|
|
+ d.Write(door.ColorText("BRI WHI ON GREEN"))
|
|
|
+ for _, line := range b {
|
|
|
+ d.Write(line + door.CRNL)
|
|
|
+ }
|
|
|
+ d.Write(door.Reset + door.CRNL)
|
|
|
+
|
|
|
+ left := d.TimeLeft()
|
|
|
+
|
|
|
+ message = fmt.Sprintf("You have %0.2f minutes / %0.2f seconds remaining..."+door.CRNL, left.Minutes(), left.Seconds())
|
|
|
+ d.Write(message)
|
|
|
+
|
|
|
+ press_a_key(&d)
|
|
|
+
|
|
|
+ mainmenu := MainMenu()
|
|
|
+ var choice int
|
|
|
+
|
|
|
+ for choice >= 0 {
|
|
|
+ d.Write(door.Clrscr)
|
|
|
+ choice = mainmenu.Choose(&d)
|
|
|
+
|
|
|
+ if choice < 0 {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ option := mainmenu.GetOption(choice)
|
|
|
+
|
|
|
+ // fmt.Printf("Choice: %d, Option: %c\n", choice, option)
|
|
|
+
|
|
|
+ switch option {
|
|
|
+ case 'A':
|
|
|
+ display_ansi(&d)
|
|
|
+ press_a_key(&d)
|
|
|
+ case 'D':
|
|
|
+ display_information(&d)
|
|
|
+ press_a_key(&d)
|
|
|
+ case 'I':
|
|
|
+ d.Write(door.Reset + door.CRNL + door.CRNL)
|
|
|
+ input_demo(&d)
|
|
|
+ press_a_key(&d)
|
|
|
+ case 'P':
|
|
|
+ progress_bars(&d)
|
|
|
+ press_a_key(&d)
|
|
|
+ case 'S':
|
|
|
+ panel_demo(&d)
|
|
|
+ press_a_key(&d)
|
|
|
+ case 'Q':
|
|
|
+ choice = -1
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ d.Write(door.Reset + door.CRNL)
|
|
|
+
|
|
|
+ message = fmt.Sprintf("Returning to %s ..."+door.CRNL, d.Config.BBSID)
|
|
|
+ d.Write(message)
|
|
|
+
|
|
|
+ d.WaitKey(1, 0)
|
|
|
+ left = d.TimeLeft()
|
|
|
+
|
|
|
+ message = fmt.Sprintf("You had %0.2f minutes / %0.2f seconds remaining!"+door.CRNL, left.Minutes(), left.Seconds())
|
|
|
+ d.Write(message)
|
|
|
+
|
|
|
+ left = d.TimeUsed()
|
|
|
+ d.Write(fmt.Sprintf("You used %0.2f seconds / %0.2f minutes."+door.CRNL, left.Seconds(), left.Minutes()))
|
|
|
+
|
|
|
+ fmt.Println("Exiting space-ace")
|
|
|
+}
|