allow blank Ident
This commit is contained in:
parent
91523f4769
commit
5a840950a0
3 changed files with 45 additions and 20 deletions
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue