262 lines
6.5 KiB
Go
262 lines
6.5 KiB
Go
package input
|
|
|
|
import (
|
|
"aego/engine/module"
|
|
"aego/format/aetext"
|
|
)
|
|
|
|
const (
|
|
Version = 2
|
|
ListActions = "actions"
|
|
ListContexts = "contexts"
|
|
)
|
|
|
|
func Section() module.SectionDesc[Config] {
|
|
return module.SectionDesc[Config]{
|
|
ID: ID,
|
|
Title: "Input",
|
|
Order: 200,
|
|
Version: Version,
|
|
Default: Default,
|
|
Decode: Decode,
|
|
Encode: Encode,
|
|
Validate: (*Config).Validate,
|
|
Migrate: Migrate,
|
|
Names: func(c *Config, list string) []string {
|
|
var out []string
|
|
switch list {
|
|
case ListActions:
|
|
for _, a := range c.Actions {
|
|
out = append(out, a.Name)
|
|
}
|
|
case ListContexts:
|
|
for _, x := range c.Contexts {
|
|
out = append(out, x.Name)
|
|
}
|
|
}
|
|
return out
|
|
},
|
|
}
|
|
}
|
|
|
|
func nameOf(v aetext.Value) string {
|
|
if v.Kind == aetext.VString {
|
|
return v.Str
|
|
}
|
|
return v.Ident
|
|
}
|
|
|
|
func namesOf(v aetext.Value) []string {
|
|
out := make([]string, 0, len(v.List))
|
|
for _, x := range v.List {
|
|
out = append(out, nameOf(x))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func idents(list []string) aetext.Value {
|
|
vals := make([]aetext.Value, len(list))
|
|
for i, s := range list {
|
|
vals[i] = aetext.Ident(s)
|
|
}
|
|
return aetext.List(vals...)
|
|
}
|
|
|
|
func Decode(n *aetext.Node, c *Config) error {
|
|
*c = Config{Players: 1, Deadzone: DefaultDeadzone}
|
|
for _, f := range n.Fields {
|
|
switch f.Name {
|
|
case "players":
|
|
c.Players = int(f.Value.AsInt())
|
|
case "deadzone":
|
|
c.Deadzone = float32(f.Value.AsFloat())
|
|
case "stack":
|
|
c.Stack = namesOf(f.Value)
|
|
case "migrated":
|
|
c.Migrated = f.Value.Bool
|
|
default:
|
|
return bad("unknown setting " + f.Name)
|
|
}
|
|
}
|
|
for _, b := range n.Blocks {
|
|
switch b.Head {
|
|
case "action":
|
|
a, err := decodeAction(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.Actions = append(c.Actions, a)
|
|
case "context":
|
|
x, err := decodeContext(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.Contexts = append(c.Contexts, x)
|
|
default:
|
|
return bad("unknown block " + b.Head)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func decodeAction(n *aetext.Node) (Action, error) {
|
|
var a Action
|
|
for _, f := range n.Fields {
|
|
v := f.Value
|
|
switch f.Name {
|
|
case "name":
|
|
a.Name = nameOf(v)
|
|
case "type":
|
|
k, ok := KindByName(nameOf(v))
|
|
if !ok {
|
|
return a, bad("action " + a.Name + ": unknown type " + nameOf(v))
|
|
}
|
|
a.Kind = k
|
|
case "bind":
|
|
for _, x := range v.List {
|
|
b, err := ParseBinding(nameOf(x))
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.Bindings = append(a.Bindings, b)
|
|
}
|
|
case "interaction":
|
|
t, ok := TriggerByName(nameOf(v))
|
|
if !ok {
|
|
return a, bad("action " + a.Name + ": unknown interaction " + nameOf(v))
|
|
}
|
|
a.Interaction.Trigger = t
|
|
case "time":
|
|
a.Interaction.Time = float32(v.AsFloat())
|
|
case "buffer":
|
|
a.Buffer = float32(v.AsFloat())
|
|
case "deadzone":
|
|
a.Deadzone = float32(v.AsFloat())
|
|
case "sensitivity":
|
|
a.Sensitivity = float32(v.AsFloat())
|
|
case "smoothing":
|
|
a.Smoothing = float32(v.AsFloat())
|
|
default:
|
|
return a, bad("action " + a.Name + ": unknown field " + f.Name)
|
|
}
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
func decodeContext(n *aetext.Node) (Context, error) {
|
|
var x Context
|
|
for _, f := range n.Fields {
|
|
switch f.Name {
|
|
case "name":
|
|
x.Name = nameOf(f.Value)
|
|
case "priority":
|
|
x.Priority = int(f.Value.AsInt())
|
|
case "consume":
|
|
x.Consume = f.Value.Bool
|
|
case "release":
|
|
x.Release = f.Value.Bool
|
|
case "actions":
|
|
x.Actions = namesOf(f.Value)
|
|
default:
|
|
return x, bad("context " + x.Name + ": unknown field " + f.Name)
|
|
}
|
|
}
|
|
return x, nil
|
|
}
|
|
|
|
func field(name string, v aetext.Value) aetext.Field {
|
|
return aetext.Field{Name: name, Value: v}
|
|
}
|
|
|
|
func Encode(c *Config, n *aetext.Node) {
|
|
if c.Players > 1 {
|
|
n.Fields = append(n.Fields, field("players", aetext.Int(int64(c.Players))))
|
|
}
|
|
if c.Deadzone != DefaultDeadzone {
|
|
n.Fields = append(n.Fields, field("deadzone", aetext.Float32(c.Deadzone)))
|
|
}
|
|
if len(c.Stack) > 0 {
|
|
n.Fields = append(n.Fields, field("stack", idents(c.Stack)))
|
|
}
|
|
for i := range c.Actions {
|
|
n.Blocks = append(n.Blocks, encodeAction(&c.Actions[i]))
|
|
}
|
|
for i := range c.Contexts {
|
|
x := &c.Contexts[i]
|
|
b := &aetext.Node{Head: "context", Fields: []aetext.Field{field("name", aetext.Ident(x.Name))}}
|
|
if x.Priority != 0 {
|
|
b.Fields = append(b.Fields, field("priority", aetext.Int(int64(x.Priority))))
|
|
}
|
|
if x.Consume {
|
|
b.Fields = append(b.Fields, field("consume", aetext.Bool(true)))
|
|
}
|
|
if x.Release {
|
|
b.Fields = append(b.Fields, field("release", aetext.Bool(true)))
|
|
}
|
|
b.Fields = append(b.Fields, field("actions", idents(x.Actions)))
|
|
n.Blocks = append(n.Blocks, b)
|
|
}
|
|
}
|
|
|
|
func encodeAction(a *Action) *aetext.Node {
|
|
b := &aetext.Node{Head: "action", Fields: []aetext.Field{
|
|
field("name", aetext.Ident(a.Name)),
|
|
field("type", aetext.Ident(a.Kind.String())),
|
|
}}
|
|
if len(a.Bindings) > 0 {
|
|
vals := make([]aetext.Value, len(a.Bindings))
|
|
for i := range a.Bindings {
|
|
vals[i] = aetext.String(a.Bindings[i].String())
|
|
}
|
|
b.Fields = append(b.Fields, field("bind", aetext.List(vals...)))
|
|
}
|
|
if a.Interaction.Trigger != Press {
|
|
b.Fields = append(b.Fields, field("interaction", aetext.Ident(a.Interaction.Trigger.String())))
|
|
}
|
|
for _, f := range []struct {
|
|
name string
|
|
v float32
|
|
}{{"time", a.Interaction.Time}, {"buffer", a.Buffer}, {"deadzone", a.Deadzone}, {"sensitivity", a.Sensitivity}, {"smoothing", a.Smoothing}} {
|
|
if f.v != 0 {
|
|
b.Fields = append(b.Fields, field(f.name, aetext.Float32(f.v)))
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
func Migrate(n *aetext.Node, from, to int) error {
|
|
if from >= 2 {
|
|
return nil
|
|
}
|
|
var acts []*aetext.Node
|
|
var names []aetext.Value
|
|
for _, b := range n.Blocks {
|
|
if b.Head != "axes" {
|
|
return bad("unknown block " + b.Head + " in a version 1 input section")
|
|
}
|
|
for _, ax := range b.Blocks {
|
|
var binds []aetext.Value
|
|
for _, pr := range [2][2]string{{"negative", "positive"}, {"altNegative", "altPositive"}} {
|
|
neg, ok1 := ax.Field(pr[0])
|
|
pos, ok2 := ax.Field(pr[1])
|
|
if ok1 && ok2 {
|
|
binds = append(binds, aetext.String("Key."+nameOf(neg)+"/Key."+nameOf(pos)))
|
|
}
|
|
}
|
|
acts = append(acts, &aetext.Node{Head: "action", Fields: []aetext.Field{
|
|
field("name", aetext.Ident(ax.Head)),
|
|
field("type", aetext.Ident(Axis1D.String())),
|
|
field("bind", aetext.List(binds...)),
|
|
}})
|
|
names = append(names, aetext.Ident(ax.Head))
|
|
}
|
|
}
|
|
ctx := &aetext.Node{Head: "context", Fields: []aetext.Field{
|
|
field("name", aetext.Ident(DefaultContext)),
|
|
field("actions", aetext.List(names...)),
|
|
}}
|
|
n.Blocks = append(acts, ctx)
|
|
n.Fields = append(n.Fields, field("stack", aetext.List(aetext.Ident(DefaultContext))), field("migrated", aetext.Bool(true)))
|
|
return nil
|
|
}
|