201 lines
4.0 KiB
Go
201 lines
4.0 KiB
Go
package input
|
|
|
|
import (
|
|
"aego/core/errs"
|
|
)
|
|
|
|
const (
|
|
ID = "input"
|
|
ScriptModule = "aego/input"
|
|
MaxPlayers = 4
|
|
DefaultContext = "Default"
|
|
DefaultDeadzone = 0.2
|
|
)
|
|
|
|
type Kind uint8
|
|
|
|
const (
|
|
Button Kind = iota
|
|
Axis1D
|
|
Axis2D
|
|
Pointer
|
|
PointerDelta
|
|
kindCount
|
|
)
|
|
|
|
var kindNames = [kindCount]string{"button", "axis1", "axis2", "pointer", "pointerDelta"}
|
|
|
|
func (k Kind) String() string {
|
|
if k < kindCount {
|
|
return kindNames[k]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func KindByName(s string) (Kind, bool) {
|
|
for i, n := range kindNames {
|
|
if n == s {
|
|
return Kind(i), true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
type Trigger uint8
|
|
|
|
const (
|
|
Press Trigger = iota
|
|
Release
|
|
Hold
|
|
Tap
|
|
DoubleTap
|
|
Toggle
|
|
triggerCount
|
|
)
|
|
|
|
var triggerNames = [triggerCount]string{"press", "release", "hold", "tap", "doubleTap", "toggle"}
|
|
|
|
func (t Trigger) String() string {
|
|
if t < triggerCount {
|
|
return triggerNames[t]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TriggerByName(s string) (Trigger, bool) {
|
|
for i, n := range triggerNames {
|
|
if n == s {
|
|
return Trigger(i), true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
type Interaction struct {
|
|
Trigger Trigger
|
|
Time float32
|
|
}
|
|
|
|
type Action struct {
|
|
Name string
|
|
Kind Kind
|
|
Bindings []Binding
|
|
Interaction Interaction
|
|
Buffer float32
|
|
Deadzone float32
|
|
Sensitivity float32
|
|
Smoothing float32
|
|
}
|
|
|
|
type Context struct {
|
|
Name string
|
|
Priority int
|
|
Consume bool
|
|
Release bool
|
|
Actions []string
|
|
}
|
|
|
|
type Config struct {
|
|
Players int
|
|
Deadzone float32
|
|
Stack []string
|
|
Actions []Action
|
|
Contexts []Context
|
|
Migrated bool
|
|
}
|
|
|
|
func mustBindings(list ...string) []Binding {
|
|
out := make([]Binding, len(list))
|
|
for i, s := range list {
|
|
b, err := ParseBinding(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
out[i] = b
|
|
}
|
|
return out
|
|
}
|
|
|
|
func Default() Config {
|
|
return Config{
|
|
Players: 1,
|
|
Deadzone: DefaultDeadzone,
|
|
Stack: []string{DefaultContext},
|
|
Actions: []Action{
|
|
{Name: "horizontal", Kind: Axis1D, Bindings: mustBindings("Key.A/Key.D", "Key.Left/Key.Right")},
|
|
{Name: "vertical", Kind: Axis1D, Bindings: mustBindings("Key.S/Key.W", "Key.Down/Key.Up")},
|
|
},
|
|
Contexts: []Context{{Name: DefaultContext, Actions: []string{"horizontal", "vertical"}}},
|
|
}
|
|
}
|
|
|
|
func bad(msg string) error {
|
|
return errs.New(errs.ProjectBadFile, "input: "+msg)
|
|
}
|
|
|
|
func validName(s string) bool {
|
|
if s == "" {
|
|
return false
|
|
}
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
switch {
|
|
case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c == '_':
|
|
case c >= '0' && c <= '9' && i > 0:
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (c *Config) Validate() error {
|
|
if c.Players < 1 || c.Players > MaxPlayers {
|
|
return bad("players must be from 1 to 4")
|
|
}
|
|
if c.Deadzone < 0 || c.Deadzone >= 1 {
|
|
return bad("deadzone must be in [0, 1)")
|
|
}
|
|
acts := map[string]bool{}
|
|
for i := range c.Actions {
|
|
a := &c.Actions[i]
|
|
switch {
|
|
case !validName(a.Name):
|
|
return bad("action name " + a.Name + " is not a valid identifier")
|
|
case acts[a.Name]:
|
|
return bad("action " + a.Name + " is declared twice")
|
|
case a.Buffer < 0 || a.Deadzone < 0 || a.Deadzone >= 1 || a.Smoothing < 0 || a.Interaction.Time < 0:
|
|
return bad("action " + a.Name + " has a negative or out-of-range setting")
|
|
case (a.Kind == Pointer || a.Kind == PointerDelta) && len(a.Bindings) > 0:
|
|
return bad("action " + a.Name + " of type " + a.Kind.String() + " takes no bindings")
|
|
}
|
|
for j := range a.Bindings {
|
|
if !a.Bindings[j].fits(a.Kind) {
|
|
return bad("action " + a.Name + ": binding " + a.Bindings[j].String() + " does not fit " + a.Kind.String())
|
|
}
|
|
}
|
|
acts[a.Name] = true
|
|
}
|
|
ctxs := map[string]bool{}
|
|
for _, x := range c.Contexts {
|
|
switch {
|
|
case !validName(x.Name):
|
|
return bad("context name " + x.Name + " is not a valid identifier")
|
|
case ctxs[x.Name]:
|
|
return bad("context " + x.Name + " is declared twice")
|
|
}
|
|
for _, a := range x.Actions {
|
|
if !acts[a] {
|
|
return bad("context " + x.Name + " names the unknown action " + a)
|
|
}
|
|
}
|
|
ctxs[x.Name] = true
|
|
}
|
|
for _, s := range c.Stack {
|
|
if !ctxs[s] {
|
|
return bad("stack names the unknown context " + s)
|
|
}
|
|
}
|
|
return nil
|
|
}
|