278 lines
5.2 KiB
Go
278 lines
5.2 KiB
Go
package prefab
|
|
|
|
import (
|
|
"maps"
|
|
"slices"
|
|
"strings"
|
|
|
|
"aego/core/ids"
|
|
"aego/core/mathx"
|
|
"aego/schema"
|
|
)
|
|
|
|
type PrefabID = ids.AssetID
|
|
|
|
const (
|
|
OwnerNode = "node"
|
|
OwnerTransform = "transform"
|
|
ScriptPrefix = "script/"
|
|
FieldName = "name"
|
|
FieldEnabled = "enabled"
|
|
FieldLayers = "layers"
|
|
FieldPosition = "position"
|
|
FieldRotation = "rotation"
|
|
FieldScale = "scale"
|
|
FieldActive = "#enabled"
|
|
)
|
|
|
|
type CompKind uint8
|
|
|
|
const (
|
|
CompNode CompKind = iota
|
|
CompTransform
|
|
CompComponent
|
|
CompScript
|
|
CompUnknown
|
|
)
|
|
|
|
func (k CompKind) Removable() bool {
|
|
return k == CompComponent || k == CompScript || k == CompUnknown
|
|
}
|
|
|
|
type Field struct {
|
|
Name string
|
|
Value schema.Value
|
|
}
|
|
|
|
type Comp struct {
|
|
Owner string
|
|
Kind CompKind
|
|
Fields []Field
|
|
Raw any
|
|
}
|
|
|
|
func ScriptOwner(full string) string {
|
|
return ScriptPrefix + full
|
|
}
|
|
|
|
func (c *Comp) Get(name string) (schema.Value, bool) {
|
|
for i := range c.Fields {
|
|
if c.Fields[i].Name == name {
|
|
return c.Fields[i].Value, true
|
|
}
|
|
}
|
|
return schema.Value{}, false
|
|
}
|
|
|
|
func (c *Comp) Set(name string, v schema.Value) {
|
|
for i := range c.Fields {
|
|
if c.Fields[i].Name == name {
|
|
c.Fields[i].Value = v
|
|
return
|
|
}
|
|
}
|
|
c.Fields = append(c.Fields, Field{Name: name, Value: v})
|
|
}
|
|
|
|
func (c Comp) Clone() Comp {
|
|
c.Fields = slices.Clone(c.Fields)
|
|
return c
|
|
}
|
|
|
|
func CloneComps(list []Comp) []Comp {
|
|
if list == nil {
|
|
return nil
|
|
}
|
|
out := make([]Comp, len(list))
|
|
for i := range list {
|
|
out[i] = list[i].Clone()
|
|
}
|
|
return out
|
|
}
|
|
|
|
type PropKey struct {
|
|
Owner string
|
|
Field string
|
|
}
|
|
|
|
func (p PropKey) String() string {
|
|
return p.Owner + "." + p.Field
|
|
}
|
|
|
|
func ParsePropKey(s string) (PropKey, bool) {
|
|
i := strings.LastIndexByte(s, '.')
|
|
if i <= 0 || i == len(s)-1 {
|
|
return PropKey{}, false
|
|
}
|
|
return PropKey{Owner: s[:i], Field: s[i+1:]}, true
|
|
}
|
|
|
|
type Instance struct {
|
|
Prefab PrefabID
|
|
Overrides Overrides
|
|
IDs map[Key]ids.NodeID
|
|
Raw any
|
|
}
|
|
|
|
func (i *Instance) Clone() *Instance {
|
|
if i == nil {
|
|
return nil
|
|
}
|
|
out := &Instance{Prefab: i.Prefab, Overrides: i.Overrides.Clone(), Raw: i.Raw}
|
|
if i.IDs != nil {
|
|
out.IDs = maps.Clone(i.IDs)
|
|
}
|
|
return out
|
|
}
|
|
|
|
type Node struct {
|
|
Local ids.NodeID
|
|
Space3D bool
|
|
Comps []Comp
|
|
Children []*Node
|
|
Instance *Instance
|
|
Key Key
|
|
Origin *Instance
|
|
Missing bool
|
|
}
|
|
|
|
func Transform(space3D bool) Comp {
|
|
if space3D {
|
|
return Comp{Owner: OwnerTransform, Kind: CompTransform, Fields: []Field{
|
|
{Name: FieldPosition, Value: schema.Vec3(mathx.Vec3{})},
|
|
{Name: FieldRotation, Value: schema.Quat(mathx.QuatIdentity())},
|
|
{Name: FieldScale, Value: schema.Vec3(mathx.Vec3{X: 1, Y: 1, Z: 1})},
|
|
}}
|
|
}
|
|
return Comp{Owner: OwnerTransform, Kind: CompTransform, Fields: []Field{
|
|
{Name: FieldPosition, Value: schema.Vec2(mathx.Vec2{})},
|
|
{Name: FieldRotation, Value: schema.Float32(0)},
|
|
{Name: FieldScale, Value: schema.Vec2(mathx.Vec2{X: 1, Y: 1})},
|
|
}}
|
|
}
|
|
|
|
func NewNode(local ids.NodeID, name string, space3D bool) *Node {
|
|
return &Node{
|
|
Local: local,
|
|
Space3D: space3D,
|
|
Key: LocalKey(local),
|
|
Comps: []Comp{
|
|
{Owner: OwnerNode, Kind: CompNode, Fields: []Field{
|
|
{Name: FieldName, Value: schema.String(name)},
|
|
{Name: FieldEnabled, Value: schema.Bool(true)},
|
|
{Name: FieldLayers, Value: schema.Int64(1)},
|
|
}},
|
|
Transform(space3D),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (n *Node) Comp(owner string) *Comp {
|
|
for i := range n.Comps {
|
|
if n.Comps[i].Owner == owner {
|
|
return &n.Comps[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *Node) RemoveComp(owner string) bool {
|
|
for i := range n.Comps {
|
|
if n.Comps[i].Owner == owner {
|
|
n.Comps = slices.Delete(n.Comps, i, i+1)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (n *Node) Get(p PropKey) (schema.Value, bool) {
|
|
if n == nil {
|
|
return schema.Value{}, false
|
|
}
|
|
c := n.Comp(p.Owner)
|
|
if c == nil {
|
|
return schema.Value{}, false
|
|
}
|
|
return c.Get(p.Field)
|
|
}
|
|
|
|
func (n *Node) Name() string {
|
|
v, _ := n.Get(PropKey{Owner: OwnerNode, Field: FieldName})
|
|
return v.S
|
|
}
|
|
|
|
func (n *Node) Clone() *Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
out := *n
|
|
out.Comps = CloneComps(n.Comps)
|
|
out.Instance = n.Instance.Clone()
|
|
out.Children = nil
|
|
for _, c := range n.Children {
|
|
out.Children = append(out.Children, c.Clone())
|
|
}
|
|
return &out
|
|
}
|
|
|
|
func (n *Node) Walk(fn func(n, parent *Node)) {
|
|
n.walk(nil, fn)
|
|
}
|
|
|
|
func (n *Node) walk(parent *Node, fn func(n, parent *Node)) {
|
|
fn(n, parent)
|
|
for _, c := range n.Children {
|
|
c.walk(n, fn)
|
|
}
|
|
}
|
|
|
|
func (n *Node) Find(k Key) *Node {
|
|
var out *Node
|
|
n.Walk(func(c, _ *Node) {
|
|
if out == nil && c.Key == k {
|
|
out = c
|
|
}
|
|
})
|
|
return out
|
|
}
|
|
|
|
func Index(t *Node) (map[Key]*Node, map[Key]Key) {
|
|
nodes := map[Key]*Node{}
|
|
parents := map[Key]Key{}
|
|
t.Walk(func(n, p *Node) {
|
|
nodes[n.Key] = n
|
|
if p != nil {
|
|
parents[n.Key] = p.Key
|
|
}
|
|
})
|
|
return nodes, parents
|
|
}
|
|
|
|
func (n *Node) removeChild(k Key) {
|
|
n.Children = slices.DeleteFunc(n.Children, func(c *Node) bool { return c.Key == k })
|
|
}
|
|
|
|
func (n *Node) reorder(keys []Key) {
|
|
out := make([]*Node, 0, len(n.Children))
|
|
used := make(map[Key]bool, len(keys))
|
|
for _, k := range keys {
|
|
if used[k] {
|
|
continue
|
|
}
|
|
for _, c := range n.Children {
|
|
if c.Key == k {
|
|
out = append(out, c)
|
|
used[k] = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
for _, c := range n.Children {
|
|
if !used[c.Key] {
|
|
out = append(out, c)
|
|
}
|
|
}
|
|
n.Children = out
|
|
}
|