String methods, no separate walkers

This commit is contained in:
Josh Deprez 2021-09-23 14:17:18 +10:00
parent aab99dab8f
commit 4dba7b22c7
12 changed files with 81 additions and 70 deletions

View file

@ -127,3 +127,5 @@ func (a *Actor) Prepare(g *Game) error {
a.game = g a.game = g
return nil return nil
} }
func (a *Actor) String() string { return "Actor@" + a.Pos.String() }

View file

@ -90,6 +90,8 @@ func (c *Camera) Scan(visit func(interface{}) error) error {
return visit(c.Child) return visit(c.Child)
} }
func (c *Camera) String() string { return "Camera@" + c.Centre.String() }
// Transform returns the camera transform. // Transform returns the camera transform.
func (c *Camera) Transform() (opts ebiten.DrawImageOptions) { func (c *Camera) Transform() (opts ebiten.DrawImageOptions) {
opts.GeoM.Translate(geom.CFloat(c.Centre.Mul(-1))) opts.GeoM.Translate(geom.CFloat(c.Centre.Mul(-1)))

View file

@ -18,6 +18,8 @@ func (c Container) Scan(visit func(interface{}) error) error {
return nil return nil
} }
func (c Container) String() string { return "Container" }
// Register records component in the slice, if parent is the container. // Register records component in the slice, if parent is the container.
func (c *Container) Register(component, parent interface{}) error { func (c *Container) Register(component, parent interface{}) error {
if parent == c { if parent == c {

View file

@ -120,6 +120,8 @@ func (d *DrawDAG) Scan(visit func(interface{}) error) error {
return visit(d.Child) return visit(d.Child)
} }
func (d *DrawDAG) String() string { return "DrawDAG" }
// Update checks for any changes to descendants, and updates its internal // Update checks for any changes to descendants, and updates its internal
// data structures accordingly. // data structures accordingly.
func (d *DrawDAG) Update() error { func (d *DrawDAG) Update() error {
@ -325,7 +327,8 @@ func newDAG() *dag {
} }
} }
func (d *dag) String() string { // Dot returns a dot-syntax-like description of the graph.
func (d *dag) Dot() string {
var sb strings.Builder var sb strings.Builder
sb.WriteString("digraph {\n") sb.WriteString("digraph {\n")
for v, e := range d.out { for v, e := range d.out {

View file

@ -58,3 +58,5 @@ func (d *DrawDFS) draw(component interface{}, screen *ebiten.Image, opts ebiten.
func (d *DrawDFS) Scan(visit func(interface{}) error) error { func (d *DrawDFS) Scan(visit func(interface{}) error) error {
return visit(d.Child) return visit(d.Child)
} }
func (d *DrawDFS) String() string { return "DrawDFS" }

View file

@ -169,46 +169,6 @@ func (g *Game) Scan(visit func(interface{}) error) error {
return visit(g.Root) return visit(g.Root)
} }
// PreorderWalk calls visit with every component and its parent, reachable from
// the given component via Scan, for as long as visit returns nil. The parent
// value passed to visit when visiting component will be nil. The parent will be
// visited before the children.
func PreorderWalk(component interface{}, visit func(component, parent interface{}) error) error {
return preorderWalk(component, nil, visit)
}
func preorderWalk(component, parent interface{}, visit func(component, parent interface{}) error) error {
if err := visit(component, parent); err != nil {
return err
}
if sc, ok := component.(Scanner); ok {
return sc.Scan(func(c interface{}) error {
return preorderWalk(c, component, visit)
})
}
return nil
}
// PostorderWalk calls visit with every component and its parent, reachable from
// the given component via Scan, for as long as visit returns nil. The parent
// value passed to visit when visiting component will be nil. The children will
// be visited before the parent.
func PostorderWalk(component interface{}, visit func(component, parent interface{}) error) error {
return postorderWalk(component, nil, visit)
}
func postorderWalk(component, parent interface{}, visit func(component, parent interface{}) error) error {
if sc, ok := component.(Scanner); ok {
scv := func(c interface{}) error {
return postorderWalk(c, component, visit)
}
if err := sc.Scan(scv); err != nil {
return err
}
}
return visit(component, parent)
}
// Load loads a component and all subcomponents recursively. // Load loads a component and all subcomponents recursively.
// Note that this method does not implement Loader. // Note that this method does not implement Loader.
func (g *Game) Load(component interface{}, assets fs.FS) error { func (g *Game) Load(component interface{}, assets fs.FS) error {
@ -258,14 +218,9 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
// Build the component databases // Build the component databases
startBuild := time.Now() startBuild := time.Now()
g.dbmu.Lock() if err := g.build(); err != nil {
g.byID = make(map[string]Identifier)
g.byAB = make(map[abKey]map[interface{}]struct{})
g.par = make(map[interface{}]interface{})
if err := PreorderWalk(g, g.registerOne); err != nil {
return err return err
} }
g.dbmu.Unlock()
log.Printf("finished building db in %v", time.Since(startBuild)) log.Printf("finished building db in %v", time.Since(startBuild))
// Prepare all the Preppers // Prepare all the Preppers
@ -277,6 +232,15 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
return nil return nil
} }
func (g *Game) build() error {
g.dbmu.Lock()
defer g.dbmu.Unlock()
g.byID = make(map[string]Identifier)
g.byAB = make(map[abKey]map[interface{}]struct{})
g.par = make(map[interface{}]interface{})
return g.registerRecursive(g, nil)
}
// Register registers a component into the component database (as the // Register registers a component into the component database (as the
// child of a given parent). Passing a nil component or parent is an error. // child of a given parent). Passing a nil component or parent is an error.
// Registering multiple components with the same ID is also an error. // Registering multiple components with the same ID is also an error.
@ -291,8 +255,19 @@ func (g *Game) Register(component, parent interface{}) error {
} }
g.dbmu.Lock() g.dbmu.Lock()
defer g.dbmu.Unlock() defer g.dbmu.Unlock()
// preorderWalk goes in the right order for registering. return g.registerRecursive(component, parent)
return preorderWalk(component, parent, g.registerOne) }
func (g *Game) registerRecursive(component, parent interface{}) error {
if err := g.registerOne(component, parent); err != nil {
return err
}
if sc, ok := component.(Scanner); ok {
return sc.Scan(func(x interface{}) error {
return g.registerRecursive(x, component)
})
}
return nil
} }
func (g *Game) registerOne(component, parent interface{}) error { func (g *Game) registerOne(component, parent interface{}) error {
@ -335,12 +310,21 @@ func (g *Game) Unregister(component interface{}) {
return return
} }
g.dbmu.Lock() g.dbmu.Lock()
// postorderWalk goes in the right order for unregistering. g.unregisterRecursive(component)
postorderWalk(component, nil, g.unregisterOne)
g.dbmu.Unlock() g.dbmu.Unlock()
} }
func (g *Game) unregisterOne(component, _ interface{}) error { func (g *Game) unregisterRecursive(component interface{}) {
if sc, ok := component.(Scanner); ok {
sc.Scan(func(x interface{}) error {
g.unregisterRecursive(x)
return nil
})
}
g.unregisterOne(component)
}
func (g *Game) unregisterOne(component interface{}) {
// unregister from g.byAB, using g.par to trace the path // unregister from g.byAB, using g.par to trace the path
ct := reflect.TypeOf(component) ct := reflect.TypeOf(component)
for _, b := range Behaviours { for _, b := range Behaviours {
@ -361,9 +345,10 @@ func (g *Game) unregisterOne(component, _ interface{}) error {
if id, ok := component.(Identifier); ok && id.Ident() != "" { if id, ok := component.(Identifier); ok && id.Ident() != "" {
delete(g.byID, id.Ident()) delete(g.byID, id.Ident())
} }
return nil
} }
func (g *Game) String() string { return "Game" }
// --------- Helper stuff --------- // --------- Helper stuff ---------
type abKey struct { type abKey struct {

View file

@ -57,3 +57,5 @@ func (r *ImageRef) Load(assets fs.FS) error {
imageCache[assetKey{assets, r.Path}] = r.image imageCache[assetKey{assets, r.Path}] = r.image
return nil return nil
} }
func (r *ImageRef) String() string { return "ImageRef{" + r.Path + "}" }

View file

@ -43,6 +43,8 @@ func (p *Parallax) Scan(visit func(interface{}) error) error {
return visit(p.Child) return visit(p.Child)
} }
func (p *Parallax) String() string { return "Parallax" }
// Transform returns a GeoM translation of Factor * camera.Centre. // Transform returns a GeoM translation of Factor * camera.Centre.
func (p *Parallax) Transform() (opts ebiten.DrawImageOptions) { func (p *Parallax) Transform() (opts ebiten.DrawImageOptions) {
x, y := geom.CFloat(p.camera.Centre) x, y := geom.CFloat(p.camera.Centre)

View file

@ -125,6 +125,8 @@ func (m *PrismMap) Scan(visit func(interface{}) error) error {
return nil return nil
} }
func (m *PrismMap) String() string { return "PrismMap" }
// Transform retrurns a translation by the draw offset. // Transform retrurns a translation by the draw offset.
func (m *PrismMap) Transform() (opts ebiten.DrawImageOptions) { func (m *PrismMap) Transform() (opts ebiten.DrawImageOptions) {
opts.GeoM.Translate(geom.CFloat(m.DrawOffset)) opts.GeoM.Translate(geom.CFloat(m.DrawOffset))

View file

@ -88,23 +88,26 @@ func (g *Game) cmdTree(dst io.Writer, argv []string) {
return return
} }
} }
PreorderWalk(c, func(w, _ interface{}) error { g.printTreeRecursive(dst, 0, c)
}
func (g *Game) printTreeRecursive(dst io.Writer, depth int, c interface{}) {
indent := "" indent := ""
l := 0 if depth > 0 {
for p := w; p != c; p = g.par[p] { indent = strings.Repeat(" ", depth-1) + "↳ "
l++
} }
if l > 0 { i, ok := c.(Identifier)
indent = strings.Repeat(" ", l-1) + "↳ "
}
i, ok := w.(Identifier)
if ok { if ok {
fmt.Fprintf(dst, "%s%T %q\n", indent, w, i.Ident()) fmt.Fprintf(dst, "%s%v %q\n", indent, c, i.Ident())
} else { } else {
fmt.Fprintf(dst, "%s%T\n", indent, w) fmt.Fprintf(dst, "%s%v\n", indent, c)
} }
if sc, ok := c.(Scanner); ok {
sc.Scan(func(x interface{}) error {
g.printTreeRecursive(dst, depth+1, x)
return nil return nil
}) })
}
} }
func (g *Game) cmdQuery(dst io.Writer, argv []string) { func (g *Game) cmdQuery(dst io.Writer, argv []string) {

View file

@ -43,6 +43,8 @@ func (s *Scene) Scan(visit func(interface{}) error) error {
return visit(s.Child) return visit(s.Child)
} }
func (s *Scene) String() string { return "Scene" }
// SceneRef loads a gzipped, gob-encoded Scene from the asset FS. // SceneRef loads a gzipped, gob-encoded Scene from the asset FS.
// After Load, Scene is usable. // After Load, Scene is usable.
// This is mostly useful for scenes that refer to other scenes, e.g. // This is mostly useful for scenes that refer to other scenes, e.g.
@ -83,3 +85,5 @@ func (r *SceneRef) Load(assets fs.FS) error {
func (r *SceneRef) Save() error { func (r *SceneRef) Save() error {
return SaveGobz(r.Scene, filepath.Base(r.Path)) return SaveGobz(r.Scene, filepath.Base(r.Path))
} }
func (r *SceneRef) String() string { return "SceneRef{" + r.Path + "}" }

View file

@ -57,3 +57,5 @@ func (s *Sheet) SubImage(i int) *ebiten.Image {
r := image.Rectangle{p, p.Add(s.CellSize)} r := image.Rectangle{p, p.Add(s.CellSize)}
return s.Src.Image().SubImage(r).(*ebiten.Image) return s.Src.Image().SubImage(r).(*ebiten.Image)
} }
func (s *Sheet) String() string { return "Sheet" }