gohan/component.go

111 lines
2.4 KiB
Go

package gohan
import (
"reflect"
)
// componentID is a component identifier. Each Component is assigned a unique ID
// via world.NewComponentID, and implements a componentID method returning its ID.
type componentID int
// newComponentID returns the next available componentID.
func newComponentID() componentID {
w.maxComponentID++
for i := Entity(1); i <= w.maxEntityID; i++ {
w.components[i] = append(w.components[i], nil)
}
return w.maxComponentID
}
func componentIDByValue(v interface{}) componentID {
sV := reflect.ValueOf(v)
sT := reflect.TypeOf(v)
if sV.Kind() == reflect.Ptr {
sV = sV.Elem()
sT = sT.Elem()
}
componentName := sV.Type().String()
return componentIDByName(componentName)
}
func componentIDByName(name string) componentID {
if len(name) == 0 {
return 0
}
if name[0:1] == "*" {
name = name[1:]
}
if !w.haveSystemComponentName[name] {
w.systemComponentNames = append(w.systemComponentNames, name)
w.haveSystemComponentName[name] = true
id := newComponentID()
return id
}
for i, savedName := range w.systemComponentNames {
if savedName == name {
return componentID(i)
}
}
return 0
}
// AddComponent adds a Component to an Entity.
func (e Entity) AddComponent(component interface{}) {
w.componentMutex.Lock()
defer w.componentMutex.Unlock()
id := componentIDByValue(component)
w.components[e][id] = component
w.entityMutex.Lock()
defer w.entityMutex.Unlock()
w.modifiedEntities = append(w.modifiedEntities, e)
}
// With accepts any function which takes one or more components as arguments.
// This function will block until the provided function returns.
func (e Entity) With(f interface{}) {
components := w.components[e]
if components == nil {
return
}
t := reflect.TypeOf(f)
v := reflect.ValueOf(f)
numIn := t.NumIn()
if t.Kind() != reflect.Func || v.IsNil() || numIn == 0 {
panic("component.With() must be provided with a function containing one or more components as arguments")
}
args := make([]reflect.Value, numIn)
for i := 0; i < numIn; i++ {
id := componentIDByName(t.In(i).String())
arg := reflect.ValueOf(components[id])
if components[id] == nil {
arg = reflect.New(t.In(i)).Elem()
}
args[i] = arg
}
v.Call(args)
}
// getComponent gets a Component of an Entity.
func (e Entity) getComponent(componentID componentID) interface{} {
components := w.components[e]
if components == nil {
return nil
}
return components[componentID]
}