2021-08-26 10:03:11 +10:00
|
|
|
package engine
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
2021-08-27 15:39:10 +10:00
|
|
|
"reflect"
|
2021-08-26 10:03:11 +10:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2021-08-26 10:06:51 +10:00
|
|
|
// REPL runs a read-evaluate-print-loop. Commands are taken from src and output
|
|
|
|
// is written to dst. assets is needed for commands like reload.
|
2021-08-26 10:03:11 +10:00
|
|
|
func (g *Game) REPL(src io.Reader, dst io.Writer, assets fs.FS) error {
|
2021-08-27 14:30:39 +10:00
|
|
|
const prompt = "game> "
|
|
|
|
fmt.Fprint(dst, prompt)
|
2021-08-31 20:26:20 +10:00
|
|
|
sc := bufio.NewScanner(src) // TODO: use a repl library?
|
2021-08-26 10:03:11 +10:00
|
|
|
for sc.Scan() {
|
2021-08-27 13:08:04 +10:00
|
|
|
argv := strings.Split(sc.Text(), " ")
|
|
|
|
if len(argv) == 0 {
|
2021-08-26 10:03:11 +10:00
|
|
|
continue
|
|
|
|
}
|
2021-08-27 13:08:04 +10:00
|
|
|
switch argv[0] {
|
2021-08-26 10:03:11 +10:00
|
|
|
case "quit":
|
|
|
|
os.Exit(0)
|
|
|
|
case "pause":
|
|
|
|
g.Disable()
|
|
|
|
case "resume", "unpause":
|
|
|
|
g.Enable()
|
|
|
|
case "save":
|
2021-08-27 13:08:04 +10:00
|
|
|
g.cmdSave(dst, argv)
|
2021-08-26 10:03:11 +10:00
|
|
|
case "reload":
|
2021-08-27 13:08:04 +10:00
|
|
|
g.cmdReload(dst, assets)
|
2021-08-26 10:03:11 +10:00
|
|
|
case "tree":
|
2021-08-27 13:08:04 +10:00
|
|
|
g.cmdTree(dst, argv)
|
2021-08-27 15:39:10 +10:00
|
|
|
case "query":
|
|
|
|
g.cmdQuery(dst, argv)
|
2021-08-31 20:24:38 +10:00
|
|
|
case "hide":
|
|
|
|
g.cmdHide(dst, argv)
|
|
|
|
case "show":
|
|
|
|
g.cmdShow(dst, argv)
|
2021-08-27 13:08:04 +10:00
|
|
|
}
|
2021-08-27 14:30:39 +10:00
|
|
|
fmt.Fprint(dst, prompt)
|
2021-08-27 13:08:04 +10:00
|
|
|
}
|
|
|
|
return sc.Err()
|
|
|
|
}
|
2021-08-26 10:03:11 +10:00
|
|
|
|
2021-08-27 13:08:04 +10:00
|
|
|
func (g *Game) cmdSave(dst io.Writer, argv []string) {
|
2021-08-31 20:24:38 +10:00
|
|
|
c := g.cmdutilComponentArg1(dst, argv)
|
2021-08-27 13:08:04 +10:00
|
|
|
if c == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
s, ok := c.(Saver)
|
|
|
|
if !ok {
|
2021-08-31 20:24:38 +10:00
|
|
|
fmt.Fprintf(dst, "Component not saveable (type %T)\n", c)
|
2021-08-27 13:08:04 +10:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := s.Save(); err != nil {
|
2021-08-31 20:24:38 +10:00
|
|
|
fmt.Fprintf(dst, "Couldn't save: %v\n", err)
|
2021-08-27 13:08:04 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) cmdReload(dst io.Writer, assets fs.FS) {
|
|
|
|
g.Disable()
|
|
|
|
g.Hide()
|
2021-08-27 13:56:50 +10:00
|
|
|
if err := g.LoadAndPrepare(assets); err != nil {
|
2021-08-27 13:08:04 +10:00
|
|
|
fmt.Fprintf(dst, "Couldn't load: %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
g.Enable()
|
|
|
|
g.Show()
|
|
|
|
}
|
2021-08-26 10:03:11 +10:00
|
|
|
|
2021-08-27 13:08:04 +10:00
|
|
|
func (g *Game) cmdTree(dst io.Writer, argv []string) {
|
|
|
|
if len(argv) < 1 || len(argv) > 2 {
|
|
|
|
fmt.Println(dst, "Usage: tree [ID]")
|
2021-08-28 18:16:36 +10:00
|
|
|
return
|
2021-08-27 13:08:04 +10:00
|
|
|
}
|
2021-08-27 15:39:10 +10:00
|
|
|
c := interface{}(g)
|
2021-08-27 13:08:04 +10:00
|
|
|
if len(argv) == 2 { // subtree
|
|
|
|
id := argv[1]
|
|
|
|
c = g.Component(id)
|
|
|
|
if c == nil {
|
|
|
|
fmt.Fprintf(dst, "Component %q not found\n", id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2021-09-03 09:13:12 +10:00
|
|
|
PreorderWalk(c, func(w, _ interface{}) error {
|
2021-08-27 13:08:04 +10:00
|
|
|
indent := ""
|
2021-08-30 16:25:50 +10:00
|
|
|
l := 0
|
2021-09-03 09:13:12 +10:00
|
|
|
for p := w; p != c; p = g.par[p] {
|
2021-08-30 16:25:50 +10:00
|
|
|
l++
|
|
|
|
}
|
|
|
|
if l > 0 {
|
|
|
|
indent = strings.Repeat(" ", l-1) + "↳ "
|
2021-08-27 13:08:04 +10:00
|
|
|
}
|
2021-09-03 09:13:12 +10:00
|
|
|
i, ok := w.(Identifier)
|
2021-08-27 13:08:04 +10:00
|
|
|
if ok {
|
2021-09-03 09:13:12 +10:00
|
|
|
fmt.Fprintf(dst, "%s%T %q\n", indent, w, i.Ident())
|
2021-08-27 13:08:04 +10:00
|
|
|
} else {
|
2021-09-03 09:13:12 +10:00
|
|
|
fmt.Fprintf(dst, "%s%T\n", indent, w)
|
2021-08-27 13:08:04 +10:00
|
|
|
}
|
2021-08-27 13:56:50 +10:00
|
|
|
return nil
|
|
|
|
})
|
2021-08-26 10:03:11 +10:00
|
|
|
}
|
2021-08-27 15:39:10 +10:00
|
|
|
|
|
|
|
func (g *Game) cmdQuery(dst io.Writer, argv []string) {
|
|
|
|
if len(argv) < 2 || len(argv) > 3 {
|
|
|
|
fmt.Fprintln(dst, "Usage: query BEHAVIOUR [ANCESTOR_ID]")
|
2021-08-28 18:24:26 +10:00
|
|
|
fmt.Fprint(dst, "Behaviours:")
|
|
|
|
for _, b := range Behaviours {
|
|
|
|
fmt.Fprintf(dst, " %s", b.Name())
|
|
|
|
}
|
2021-08-28 18:16:36 +10:00
|
|
|
return
|
2021-08-27 15:39:10 +10:00
|
|
|
}
|
|
|
|
|
2021-08-30 15:49:51 +10:00
|
|
|
var behaviour reflect.Type
|
2021-08-27 15:39:10 +10:00
|
|
|
for _, b := range Behaviours {
|
|
|
|
if b.Name() == argv[1] {
|
2021-08-30 15:49:51 +10:00
|
|
|
behaviour = b
|
2021-08-27 15:39:10 +10:00
|
|
|
}
|
|
|
|
}
|
2021-08-30 15:49:51 +10:00
|
|
|
if behaviour == nil {
|
2021-08-27 15:39:10 +10:00
|
|
|
fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1])
|
|
|
|
}
|
|
|
|
|
2021-08-30 15:49:51 +10:00
|
|
|
ancestor := g.Ident()
|
2021-08-27 15:39:10 +10:00
|
|
|
if len(argv) == 3 {
|
2021-08-30 15:49:51 +10:00
|
|
|
ancestor = argv[2]
|
2021-08-27 15:39:10 +10:00
|
|
|
}
|
|
|
|
|
2021-08-30 15:49:51 +10:00
|
|
|
x := g.Query(ancestor, behaviour)
|
2021-08-27 15:39:10 +10:00
|
|
|
if len(x) == 0 {
|
|
|
|
fmt.Fprintln(dst, "No results")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-30 15:03:22 +10:00
|
|
|
for c := range x {
|
2021-08-27 15:39:10 +10:00
|
|
|
i, ok := c.(Identifier)
|
|
|
|
if ok {
|
2021-08-30 15:50:22 +10:00
|
|
|
fmt.Fprintf(dst, "%T %q\n", c, i.Ident())
|
2021-08-27 15:39:10 +10:00
|
|
|
} else {
|
|
|
|
fmt.Fprintf(dst, "%T\n", c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-31 20:24:38 +10:00
|
|
|
|
|
|
|
func (g *Game) cmdutilComponentArg1(dst io.Writer, argv []string) interface{} {
|
|
|
|
if len(argv) != 2 {
|
|
|
|
fmt.Fprintln(dst, "Usage: hide ID")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
id := argv[1]
|
|
|
|
c := g.Component(id)
|
|
|
|
if c == nil {
|
|
|
|
fmt.Fprintf(dst, "Component %q not found\n", id)
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) cmdHide(dst io.Writer, argv []string) {
|
|
|
|
c := g.cmdutilComponentArg1(dst, argv)
|
|
|
|
if c == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
h, ok := c.(Hider)
|
|
|
|
if !ok {
|
|
|
|
fmt.Fprintf(dst, "Component not hidable (type %T)\n", c)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
h.Hide()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) cmdShow(dst io.Writer, argv []string) {
|
|
|
|
c := g.cmdutilComponentArg1(dst, argv)
|
|
|
|
if c == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
h, ok := c.(Hider)
|
|
|
|
if !ok {
|
|
|
|
fmt.Fprintf(dst, "Component not showable (type %T)\n", c)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
h.Show()
|
|
|
|
}
|