allow blank Ident

This commit is contained in:
Josh Deprez 2021-08-30 15:49:51 +10:00
parent 91523f4769
commit 5a840950a0
3 changed files with 45 additions and 20 deletions

View file

@ -35,6 +35,7 @@ type Game struct {
dbmu sync.RWMutex
byID map[string]Identifier // Named components by ID
byAB map[abKey]map[interface{}]struct{} // Ancestor/behaviour index
par map[interface{}]interface{} // par[x] is parent of x
}
type abKey struct {
@ -67,13 +68,23 @@ func (g *Game) Update() error {
func (g *Game) Ident() string { return "__GAME__" }
// Component returns the component with a given ID, or nil if there is none.
// This only returns sensible values after LoadAndPrepare.
// This only returns sensible values for registered components (e.g. after
// LoadAndPrepare).
func (g *Game) Component(id string) Identifier {
g.dbmu.RLock()
defer g.dbmu.RUnlock()
return g.byID[id]
}
// Parent returns the parent of a given component, or nil if there is none.
// This only returns sensible values for registered components (e.g. after
// LoadAndPrepare).
func (g *Game) Parent(c interface{}) interface{} {
g.dbmu.RLock()
defer g.dbmu.RUnlock()
return g.par[c]
}
// Query looks for components having both a given ancestor and implementing
// a given behaviour (see Behaviors in interface.go). This only returns sensible
// values after LoadAndPrepare. Note that every component is its own ancestor.
@ -128,6 +139,7 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
g.dbmu.Lock()
g.byID = make(map[string]Identifier)
g.byAB = make(map[abKey]map[interface{}]struct{})
g.par = make(map[interface{}]interface{})
if err := Walk(g, g.registerComponent); err != nil {
return err
}
@ -143,16 +155,21 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
}
func (g *Game) registerComponent(c interface{}, path []interface{}) error {
// register in g.par
if l := len(path); l > 0 {
g.par[c] = path[l-1]
}
// register in g.byAB
ct := reflect.TypeOf(c)
for _, b := range Behaviours {
if !ct.Implements(b) {
continue
}
// TODO: sub-quadratic?
// TODO: this is quadratic - do better?
for _, p := range append(path, c) {
i, ok := p.(Identifier)
if !ok || i.Ident() == "" {
if !ok {
continue
}
k := abKey{i.Ident(), b}
@ -163,9 +180,9 @@ func (g *Game) registerComponent(c interface{}, path []interface{}) error {
}
}
// register in g.byID
// register in g.byID if needed
i, ok := c.(Identifier)
if !ok || i.Ident() == "" {
if !ok {
return nil
}
id := i.Ident()
@ -176,16 +193,23 @@ func (g *Game) registerComponent(c interface{}, path []interface{}) error {
return nil
}
func (g *Game) unregisterComponent(c interface{}, path []interface{}) {
// unregister from g.byAB
// UnregisterComponent removes the component from the component database.
func (g *Game) UnregisterComponent(c interface{}) {
g.dbmu.Lock()
g.unregisterComponent(c)
g.dbmu.Unlock()
}
func (g *Game) unregisterComponent(c interface{}) {
// unregister from g.byAB, using g.par to trace the path
ct := reflect.TypeOf(c)
for _, b := range Behaviours {
if !ct.Implements(b) {
continue
}
for _, p := range append(path, c) {
for p := c; p != nil; p = g.par[p] {
i, ok := p.(Identifier)
if !ok || i.Ident() == "" {
if !ok {
continue
}
k := abKey{i.Ident(), b}
@ -196,9 +220,12 @@ func (g *Game) unregisterComponent(c interface{}, path []interface{}) {
}
}
// unregister from g.byID
// unregister from g.par
delete(g.par, c)
// unregister from g.byID if needed
i, ok := c.(Identifier)
if !ok || i.Ident() == "" {
if !ok {
return
}
delete(g.byID, i.Ident())

View file

@ -95,7 +95,7 @@ func (g *Game) cmdTree(dst io.Writer, argv []string) {
}
i, ok := c.(Identifier)
if ok {
fmt.Fprintf(dst, "%s%T %s\n", indent, c, i.Ident())
fmt.Fprintf(dst, "%s%T %q\n", indent, c, i.Ident())
} else {
fmt.Fprintf(dst, "%s%T\n", indent, c)
}
@ -113,22 +113,22 @@ func (g *Game) cmdQuery(dst io.Writer, argv []string) {
return
}
var behav reflect.Type
var behaviour reflect.Type
for _, b := range Behaviours {
if b.Name() == argv[1] {
behav = b
behaviour = b
}
}
if behav == nil {
if behaviour == nil {
fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1])
}
ances := g.Ident()
ancestor := g.Ident()
if len(argv) == 3 {
ances = argv[2]
ancestor = argv[2]
}
x := g.Query(ances, behav)
x := g.Query(ancestor, behaviour)
if len(x) == 0 {
fmt.Fprintln(dst, "No results")
return

View file

@ -9,7 +9,6 @@ import (
// Ensure Sprite satisfies interfaces.
var _ interface {
Identifier
Drawer
Scanner
Updater
@ -21,7 +20,6 @@ func init() {
// Sprite combines an Actor with the ability to Draw from a single spritesheet.
type Sprite struct {
ID
Actor
FrameOffset image.Point
Hidden