Files

181 lines
5.9 KiB
Go

package agent
import (
"errors"
"path"
"aego/project/refactor"
"aego/scene/mutate"
"aego/scene/prefab"
"aego/scene/sceneio"
)
func prefabCommands() []*Command {
owner := Arg{Name: "owner", Type: "string", Doc: "component or script owner of the override, for property and component levels"}
field := Arg{Name: "field", Type: "string", Doc: "field name, for the property level"}
level := Arg{Name: "level", Type: "string", Doc: "property, component, node or all (default); orphan for revert"}
return []*Command{
{Name: "prefab.create", Doc: "turn a node into a prefab file and link the node to it", Write: true,
Args: []Arg{argFile, argNode, {Name: "out", Type: "string", Doc: "new prefab file, prefabs/<name>.aeprefab", Required: true, Pos: true}}, Run: (*Session).prefabCreate},
{Name: "prefab.instantiate", Doc: "add an instance of a prefab", Write: true,
Args: []Arg{argFile, {Name: "prefab", Type: "string", Doc: "prefab id or path", Required: true, Pos: true},
{Name: "parent", Type: "string", Doc: "parent node, the root by default"}, {Name: "name", Type: "string", Doc: "name of the instance"}, argIndex}, Run: (*Session).prefabInstantiate},
{Name: "prefab.apply", Doc: "write instance overrides into the prefab file", Write: true,
Args: []Arg{argFile, argNode, level, owner, field, {Name: "nearest", Type: "boolean", Doc: "apply to the nearest nested prefab"}}, Run: (*Session).prefabApply},
{Name: "prefab.revert", Doc: "drop instance overrides", Write: true, Args: []Arg{argFile, argNode, level, owner, field}, Run: (*Session).prefabRevert},
{Name: "prefab.unpack", Doc: "turn an instance into plain nodes", Write: true,
Args: []Arg{argFile, argNode, {Name: "deep", Type: "boolean", Doc: "unpack nested prefabs too"}}, Run: func(s *Session, a Args) (*Output, error) {
return s.sceneTx(a, "Unpack Prefab", mutate.Unpack(a.Str("node"), a.Bool("deep")))
}},
{Name: "prefab.overrides", Doc: "list the overrides of the instance a node belongs to", Args: []Arg{argFile, argNode}, Run: (*Session).prefabOverrides},
}
}
func levelOf(a Args, def string) (prefab.Level, error) {
name := a.Str("level")
if name == "" {
name = def
}
l, ok := prefab.ParseLevel(name)
if !ok {
return 0, errors.New("level is property, component, node, all or orphan")
}
return l, nil
}
func (s *Session) prefabCreate(a Args) (*Output, error) {
rel, before, doc, err := s.scene(a)
if err != nil {
return nil, err
}
out, err := s.rel(a.Str("out"))
if err != nil {
return nil, err
}
if path.Ext(out) != PrefabExt {
out += PrefabExt
}
if err := s.writable(); err != nil {
return nil, err
}
if a.Bool("dry-run") {
h, err := doc.Node(a.Str("node"))
if err != nil {
return nil, err
}
if err := refactor.CheckCreate(doc.Scene, h); err != nil {
return nil, err
}
if s.exists(out) {
return nil, errors.New(out + " already exists")
}
return &Output{DryRun: true, Notes: []string{"would create " + out, "would write " + rel}}, nil
}
id, res, err := refactor.CreatePrefab(s.Pipeline, doc, s.Library, a.Str("node"), out)
if err != nil {
s.reload()
return nil, err
}
files, err := s.save(rel, before, doc.Bytes())
s.reload()
return &Output{Changes: res.Changes, Problems: res.Problems, Created: []string{id.String()}, Files: append([]string{out}, files...)}, err
}
func (s *Session) prefabInstantiate(a Args) (*Output, error) {
op := mutate.Instantiate(a.Str("parent"), a.Str("prefab"))
op.Name = a.Str("name")
if a.Has("index") {
i, err := a.Int("index", 0)
if err != nil {
return nil, err
}
op = op.At(i)
}
return s.sceneTx(a, "Instantiate", op)
}
func (s *Session) prefabApply(a Args) (*Output, error) {
rel, before, doc, err := s.scene(a)
if err != nil {
return nil, err
}
h, err := doc.Node(a.Str("node"))
if err != nil {
return nil, err
}
lvl, err := levelOf(a, "all")
if err != nil {
return nil, err
}
c := refactor.PrefabChange{Node: doc.Scene.ID(h), Level: lvl, Prop: prefab.PropKey{Owner: a.Str("owner"), Field: a.Str("field")}, Nearest: a.Bool("nearest")}
if a.Bool("dry-run") {
plan, err := refactor.PlanApply(doc.Scene, s.Library, c)
if errors.Is(err, mutate.ErrNothing) {
return &Output{DryRun: true, Notes: []string{"nothing to apply"}}, nil
}
if err != nil {
return nil, err
}
edits, err := plan.Edits(doc.Scene.Registry(), s.Pipeline)
if err != nil {
return nil, err
}
out := &Output{DryRun: true}
for _, e := range edits {
out.Notes = append(out.Notes, "would write "+e.Path)
}
out.Notes = append(out.Notes, "would write "+rel)
return out, nil
}
res, err := refactor.ApplyPrefab(doc, s.Library, s.Pipeline, c)
if errors.Is(err, mutate.ErrNothing) {
return &Output{Notes: []string{"nothing to apply"}}, nil
}
if err != nil {
s.reload()
return nil, err
}
edits := append(res.Edits, refactor.Edit{Path: rel, Before: before, After: doc.Bytes()})
files, err := s.write(edits)
s.reload()
if err != nil {
return nil, err
}
return &Output{Changes: res.Result.Changes, Problems: res.Result.Problems, Files: files}, nil
}
func (s *Session) prefabRevert(a Args) (*Output, error) {
lvl, err := levelOf(a, "all")
if err != nil {
return nil, err
}
op := mutate.RevertPrefab(a.Str("node"), lvl.String())
op.Owner, op.Field = a.Str("owner"), a.Str("field")
out, err := s.sceneTx(a, "Revert", op)
if errors.Is(err, mutate.ErrNothing) {
return &Output{Notes: []string{"nothing to revert"}}, nil
}
return out, err
}
func (s *Session) prefabOverrides(a Args) (*Output, error) {
_, _, doc, err := s.scene(a)
if err != nil {
return nil, err
}
h, err := doc.Node(a.Str("node"))
if err != nil {
return nil, err
}
root, _, ok := sceneio.OwnerRoot(doc.Scene, h)
if !ok {
return nil, errors.New(a.Str("node") + " is not part of a prefab instance")
}
inst, err := sceneio.Overrides(doc.Scene, root, s.Library)
if err != nil {
return nil, err
}
text := prefab.FormatOverrides(inst.Overrides)
return &Output{Value: text, Text: text}, nil
}