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
|
dbmu sync.RWMutex
|
||||||
byID map[string]Identifier // Named components by ID
|
byID map[string]Identifier // Named components by ID
|
||||||
byAB map[abKey]map[interface{}]struct{} // Ancestor/behaviour index
|
byAB map[abKey]map[interface{}]struct{} // Ancestor/behaviour index
|
||||||
|
par map[interface{}]interface{} // par[x] is parent of x
|
||||||
}
|
}
|
||||||
|
|
||||||
type abKey struct {
|
type abKey struct {
|
||||||
|
@ -67,13 +68,23 @@ func (g *Game) Update() error {
|
||||||
func (g *Game) Ident() string { return "__GAME__" }
|
func (g *Game) Ident() string { return "__GAME__" }
|
||||||
|
|
||||||
// Component returns the component with a given ID, or nil if there is none.
|
// 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 {
|
func (g *Game) Component(id string) Identifier {
|
||||||
g.dbmu.RLock()
|
g.dbmu.RLock()
|
||||||
defer g.dbmu.RUnlock()
|
defer g.dbmu.RUnlock()
|
||||||
return g.byID[id]
|
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
|
// Query looks for components having both a given ancestor and implementing
|
||||||
// a given behaviour (see Behaviors in interface.go). This only returns sensible
|
// a given behaviour (see Behaviors in interface.go). This only returns sensible
|
||||||
// values after LoadAndPrepare. Note that every component is its own ancestor.
|
// 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.dbmu.Lock()
|
||||||
g.byID = make(map[string]Identifier)
|
g.byID = make(map[string]Identifier)
|
||||||
g.byAB = make(map[abKey]map[interface{}]struct{})
|
g.byAB = make(map[abKey]map[interface{}]struct{})
|
||||||
|
g.par = make(map[interface{}]interface{})
|
||||||
if err := Walk(g, g.registerComponent); err != nil {
|
if err := Walk(g, g.registerComponent); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -143,16 +155,21 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) registerComponent(c interface{}, path []interface{}) 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
|
// register in g.byAB
|
||||||
ct := reflect.TypeOf(c)
|
ct := reflect.TypeOf(c)
|
||||||
for _, b := range Behaviours {
|
for _, b := range Behaviours {
|
||||||
if !ct.Implements(b) {
|
if !ct.Implements(b) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// TODO: sub-quadratic?
|
// TODO: this is quadratic - do better?
|
||||||
for _, p := range append(path, c) {
|
for _, p := range append(path, c) {
|
||||||
i, ok := p.(Identifier)
|
i, ok := p.(Identifier)
|
||||||
if !ok || i.Ident() == "" {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
k := abKey{i.Ident(), b}
|
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)
|
i, ok := c.(Identifier)
|
||||||
if !ok || i.Ident() == "" {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
id := i.Ident()
|
id := i.Ident()
|
||||||
|
@ -176,16 +193,23 @@ func (g *Game) registerComponent(c interface{}, path []interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) unregisterComponent(c interface{}, path []interface{}) {
|
// UnregisterComponent removes the component from the component database.
|
||||||
// unregister from g.byAB
|
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)
|
ct := reflect.TypeOf(c)
|
||||||
for _, b := range Behaviours {
|
for _, b := range Behaviours {
|
||||||
if !ct.Implements(b) {
|
if !ct.Implements(b) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, p := range append(path, c) {
|
for p := c; p != nil; p = g.par[p] {
|
||||||
i, ok := p.(Identifier)
|
i, ok := p.(Identifier)
|
||||||
if !ok || i.Ident() == "" {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
k := abKey{i.Ident(), b}
|
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)
|
i, ok := c.(Identifier)
|
||||||
if !ok || i.Ident() == "" {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delete(g.byID, i.Ident())
|
delete(g.byID, i.Ident())
|
||||||
|
|
|
@ -95,7 +95,7 @@ func (g *Game) cmdTree(dst io.Writer, argv []string) {
|
||||||
}
|
}
|
||||||
i, ok := c.(Identifier)
|
i, ok := c.(Identifier)
|
||||||
if ok {
|
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 {
|
} else {
|
||||||
fmt.Fprintf(dst, "%s%T\n", indent, c)
|
fmt.Fprintf(dst, "%s%T\n", indent, c)
|
||||||
}
|
}
|
||||||
|
@ -113,22 +113,22 @@ func (g *Game) cmdQuery(dst io.Writer, argv []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var behav reflect.Type
|
var behaviour reflect.Type
|
||||||
for _, b := range Behaviours {
|
for _, b := range Behaviours {
|
||||||
if b.Name() == argv[1] {
|
if b.Name() == argv[1] {
|
||||||
behav = b
|
behaviour = b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if behav == nil {
|
if behaviour == nil {
|
||||||
fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1])
|
fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
ances := g.Ident()
|
ancestor := g.Ident()
|
||||||
if len(argv) == 3 {
|
if len(argv) == 3 {
|
||||||
ances = argv[2]
|
ancestor = argv[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
x := g.Query(ances, behav)
|
x := g.Query(ancestor, behaviour)
|
||||||
if len(x) == 0 {
|
if len(x) == 0 {
|
||||||
fmt.Fprintln(dst, "No results")
|
fmt.Fprintln(dst, "No results")
|
||||||
return
|
return
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
// Ensure Sprite satisfies interfaces.
|
// Ensure Sprite satisfies interfaces.
|
||||||
var _ interface {
|
var _ interface {
|
||||||
Identifier
|
|
||||||
Drawer
|
Drawer
|
||||||
Scanner
|
Scanner
|
||||||
Updater
|
Updater
|
||||||
|
@ -21,7 +20,6 @@ func init() {
|
||||||
|
|
||||||
// Sprite combines an Actor with the ability to Draw from a single spritesheet.
|
// Sprite combines an Actor with the ability to Draw from a single spritesheet.
|
||||||
type Sprite struct {
|
type Sprite struct {
|
||||||
ID
|
|
||||||
Actor
|
Actor
|
||||||
FrameOffset image.Point
|
FrameOffset image.Point
|
||||||
Hidden
|
Hidden
|
||||||
|
|
Loading…
Reference in a new issue