248 lines
11 KiB
Go
248 lines
11 KiB
Go
package input
|
|
|
|
import (
|
|
"aego/core/mathx"
|
|
"aego/engine"
|
|
raw "aego/platform/input"
|
|
"aego/scene/frame"
|
|
"aego/script/abi"
|
|
"aego/script/bind"
|
|
)
|
|
|
|
func InstallBindings(r *bind.Registry) {
|
|
installScript(r.Module(ScriptModule))
|
|
}
|
|
|
|
func installScript(m *bind.Module) {
|
|
installRaw(m)
|
|
installActions(m)
|
|
}
|
|
|
|
func installRaw(m *bind.Module) {
|
|
var keys []abi.EnumMember
|
|
for k := raw.Scancode(1); k < raw.ScancodeCount; k++ {
|
|
if name, ok := raw.KeyName(k); ok {
|
|
keys = append(keys, bind.Member(name, k))
|
|
}
|
|
}
|
|
buttons := []abi.EnumMember{
|
|
bind.Member("Left", raw.MouseLeft),
|
|
bind.Member("Right", raw.MouseRight),
|
|
bind.Member("Middle", raw.MouseMiddle),
|
|
bind.Member("X1", raw.MouseX1),
|
|
bind.Member("X2", raw.MouseX2),
|
|
}
|
|
bind.Enum[raw.Scancode](m, "Key", keys...)
|
|
bind.Describe(m, "Key", bind.Doc("Key is a physical key, independent of the keyboard layout. Prefer actions; raw keys are for prototypes."))
|
|
bind.Enum[raw.Button](m, "Button", buttons...)
|
|
bind.Describe(m, "Button", bind.Doc("Button is a mouse button."))
|
|
for _, k := range keys {
|
|
bind.Const(m, "Key"+k.Name, raw.Scancode(k.Value), bind.Doc("The "+k.Name+" key."))
|
|
}
|
|
for _, b := range buttons {
|
|
bind.Const(m, "Mouse"+b.Name, raw.Button(b.Value), bind.Doc("The "+b.Name+" mouse button."))
|
|
}
|
|
bind.Func1(m, "Down", func(c *bind.Ctx, k raw.Scancode) bool {
|
|
return c.Input != nil && c.Input.KeyDown(k)
|
|
}, bind.Doc("Down reports whether a raw key is held. For prototypes; release builds use actions."), bind.Params("key"),
|
|
bind.Example("if input.Down(input.KeyShift) {"))
|
|
bind.Func1(m, "KeyPressed", func(c *bind.Ctx, k raw.Scancode) bool {
|
|
return c.Input != nil && c.Input.KeyPressed(k)
|
|
}, bind.Doc("KeyPressed reports whether a raw key went down this frame. For prototypes."), bind.Params("key"),
|
|
bind.Example("if input.KeyPressed(input.KeyF5) {"))
|
|
bind.Func1(m, "KeyReleased", func(c *bind.Ctx, k raw.Scancode) bool {
|
|
return c.Input != nil && c.Input.KeyReleased(k)
|
|
}, bind.Doc("KeyReleased reports whether a raw key went up this frame. For prototypes."), bind.Params("key"),
|
|
bind.Example("if input.KeyReleased(input.KeySpace) {"))
|
|
bind.Func1(m, "MouseDown", func(c *bind.Ctx, b raw.Button) bool {
|
|
return c.Input != nil && c.Input.ButtonDown(b)
|
|
}, bind.Doc("MouseDown reports whether a mouse button is held."), bind.Params("button"),
|
|
bind.Example("if input.MouseDown(input.MouseLeft) {"))
|
|
bind.Func1(m, "MousePressed", func(c *bind.Ctx, b raw.Button) bool {
|
|
return c.Input != nil && c.Input.ButtonPressed(b)
|
|
}, bind.Doc("MousePressed reports whether a mouse button went down this frame."), bind.Params("button"),
|
|
bind.Example("if input.MousePressed(input.MouseRight) {"))
|
|
bind.Func0(m, "MousePos", func(c *bind.Ctx) mathx.Vec2 {
|
|
if c.Input == nil {
|
|
return mathx.Vec2{}
|
|
}
|
|
return c.Input.MousePos()
|
|
}, bind.Doc("MousePos returns the cursor in logical pixels of the game area."), bind.Example("p := input.MousePos()"))
|
|
bind.Func0(m, "MouseWorld", func(c *bind.Ctx) mathx.Vec2 {
|
|
if c.Input == nil {
|
|
return mathx.Vec2{}
|
|
}
|
|
r, ok := engine.ServiceOf[*frame.Runner](c.World, frame.ServiceID)
|
|
if !ok || r == nil {
|
|
return mathx.Vec2{}
|
|
}
|
|
p, _ := r.ScreenToWorld(c.Input.MousePos().Scale(r.PixelScale()))
|
|
return p
|
|
}, bind.Doc("MouseWorld returns the cursor in world units through the camera under it."), bind.Example("target := input.MouseWorld()"))
|
|
}
|
|
|
|
func lookup(c *bind.Ctx, p int64, name string) (*Service, int, bool) {
|
|
s := Of(c.World)
|
|
if s == nil {
|
|
return nil, 0, false
|
|
}
|
|
if !s.Has(name) {
|
|
c.Failf("unknown input action %q, declare it in Project Settings → Input", name)
|
|
return nil, 0, false
|
|
}
|
|
if p < 0 || int(p) >= s.Players() {
|
|
c.Failf("input player %d is out of range, the project has %d", p, s.Players())
|
|
return nil, 0, false
|
|
}
|
|
return s, int(p), true
|
|
}
|
|
|
|
func installActions(m *bind.Module) {
|
|
bind.Func1(m, "Pressed", func(c *bind.Ctx, a string) bool {
|
|
s, p, ok := lookup(c, 0, a)
|
|
return ok && s.PressedP(p, a)
|
|
}, bind.Doc("Pressed reports whether the action started. In Update it is true for one frame; in FixedUpdate for the first fixed step after the press."),
|
|
bind.Params("action"), bind.Example("if input.Pressed(\"Attack\") {"))
|
|
bind.Func1(m, "Released", func(c *bind.Ctx, a string) bool {
|
|
s, p, ok := lookup(c, 0, a)
|
|
return ok && s.ReleasedP(p, a)
|
|
}, bind.Doc("Released reports whether the action ended this frame or fixed step."), bind.Params("action"),
|
|
bind.Example("if input.Released(\"Jump\") {"))
|
|
bind.Func1(m, "Held", func(c *bind.Ctx, a string) bool {
|
|
s, p, ok := lookup(c, 0, a)
|
|
return ok && s.HeldP(p, a)
|
|
}, bind.Doc("Held reports whether the action is active."), bind.Params("action"), bind.Example("if input.Held(\"Run\") {"))
|
|
bind.Func1(m, "Axis", func(c *bind.Ctx, a string) float32 {
|
|
s, p, ok := lookup(c, 0, a)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
return s.AxisP(p, a)
|
|
}, bind.Doc("Axis returns the value of an axis action from -1 to 1, or the press strength of a button action."), bind.Params("action"),
|
|
bind.Example("x := input.Axis(\"Move\")"))
|
|
bind.Func1(m, "Axis2", func(c *bind.Ctx, a string) mathx.Vec2 {
|
|
s, p, ok := lookup(c, 0, a)
|
|
if !ok {
|
|
return mathx.Vec2{}
|
|
}
|
|
return s.Axis2P(p, a)
|
|
}, bind.Doc("Axis2 returns a 2D action as a vector no longer than 1; Y grows downward like the 2D world."), bind.Params("action"),
|
|
bind.Example("v := input.Axis2(\"Move\") * Speed"))
|
|
bind.Func0(m, "Pointer", func(c *bind.Ctx) mathx.Vec2 {
|
|
if s := Of(c.World); s != nil {
|
|
return s.Pointer()
|
|
}
|
|
return mathx.Vec2{}
|
|
}, bind.Doc("Pointer returns the cursor in logical pixels."), bind.Example("p := input.Pointer()"))
|
|
bind.Func0(m, "PointerDelta", func(c *bind.Ctx) mathx.Vec2 {
|
|
if s := Of(c.World); s != nil {
|
|
return s.PointerDelta()
|
|
}
|
|
return mathx.Vec2{}
|
|
}, bind.Doc("PointerDelta returns how far the cursor moved this frame."), bind.Example("look += input.PointerDelta()"))
|
|
bind.Func2(m, "Buffered", func(c *bind.Ctx, a string, window float32) bool {
|
|
s, p, ok := lookup(c, 0, a)
|
|
return ok && s.BufferedP(p, a, window)
|
|
}, bind.Doc("Buffered reports whether the action was pressed in the last window game seconds and not consumed. A window of 0 uses the action's buffer setting."),
|
|
bind.Params("action", "window"), bind.Example("if grounded && input.Buffered(\"Jump\", 0) {\n\tinput.Consume(\"Jump\")\n}"))
|
|
bind.Proc1(m, "Consume", func(c *bind.Ctx, a string) {
|
|
if s, p, ok := lookup(c, 0, a); ok {
|
|
s.ConsumeP(p, a)
|
|
}
|
|
}, bind.Doc("Consume clears the press and the buffer of the action, so later readers this frame do not see it."), bind.Params("action"),
|
|
bind.Example("input.Consume(\"Jump\")"))
|
|
bind.Proc1(m, "PushContext", func(c *bind.Ctx, name string) {
|
|
if s := Of(c.World); s != nil && !s.PushContext(name) {
|
|
c.Failf("unknown input context %q", name)
|
|
}
|
|
}, bind.Doc("PushContext puts an input context on top of the stack."), bind.Params("context"), bind.Example("input.PushContext(\"Menu\")"))
|
|
bind.Proc0(m, "PopContext", func(c *bind.Ctx) {
|
|
if s := Of(c.World); s != nil {
|
|
s.PopContext()
|
|
}
|
|
}, bind.Doc("PopContext removes the top input context."), bind.Example("input.PopContext()"))
|
|
bind.Proc1(m, "SetContext", func(c *bind.Ctx, name string) {
|
|
if s := Of(c.World); s != nil && !s.SetContext(name) {
|
|
c.Failf("unknown input context %q", name)
|
|
}
|
|
}, bind.Doc("SetContext replaces the whole stack with one context."), bind.Params("context"), bind.Example("input.SetContext(\"Gameplay\")"))
|
|
bind.Func2(m, "PressedP", func(c *bind.Ctx, p int64, a string) bool {
|
|
s, pl, ok := lookup(c, p, a)
|
|
return ok && s.PressedP(pl, a)
|
|
}, bind.Doc("PressedP is Pressed for one player."), bind.Params("player", "action"), bind.Example("if input.PressedP(1, \"Jump\") {"))
|
|
bind.Func2(m, "ReleasedP", func(c *bind.Ctx, p int64, a string) bool {
|
|
s, pl, ok := lookup(c, p, a)
|
|
return ok && s.ReleasedP(pl, a)
|
|
}, bind.Doc("ReleasedP is Released for one player."), bind.Params("player", "action"), bind.Example("if input.ReleasedP(1, \"Jump\") {"))
|
|
bind.Func2(m, "HeldP", func(c *bind.Ctx, p int64, a string) bool {
|
|
s, pl, ok := lookup(c, p, a)
|
|
return ok && s.HeldP(pl, a)
|
|
}, bind.Doc("HeldP is Held for one player."), bind.Params("player", "action"), bind.Example("if input.HeldP(1, \"Run\") {"))
|
|
bind.Func2(m, "AxisP", func(c *bind.Ctx, p int64, a string) float32 {
|
|
s, pl, ok := lookup(c, p, a)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
return s.AxisP(pl, a)
|
|
}, bind.Doc("AxisP is Axis for one player."), bind.Params("player", "action"), bind.Example("x := input.AxisP(1, \"Move\")"))
|
|
bind.Func2(m, "Axis2P", func(c *bind.Ctx, p int64, a string) mathx.Vec2 {
|
|
s, pl, ok := lookup(c, p, a)
|
|
if !ok {
|
|
return mathx.Vec2{}
|
|
}
|
|
return s.Axis2P(pl, a)
|
|
}, bind.Doc("Axis2P is Axis2 for one player."), bind.Params("player", "action"), bind.Example("v := input.Axis2P(1, \"Move\")"))
|
|
bind.Func0(m, "ActiveDevice", func(c *bind.Ctx) int64 {
|
|
if s := Of(c.World); s != nil {
|
|
return int64(s.ActiveDevice(0))
|
|
}
|
|
return DeviceNone
|
|
}, bind.Doc("ActiveDevice returns the device player 0 used last: -1 none, 0 keyboard and mouse, 1 to 4 a gamepad."),
|
|
bind.Example("onPad := input.ActiveDevice() > 0"))
|
|
bind.Func0(m, "DeviceChanged", func(c *bind.Ctx) bool {
|
|
s := Of(c.World)
|
|
return s != nil && s.DeviceChanged()
|
|
}, bind.Doc("DeviceChanged is true for the frame a gamepad was connected or removed."), bind.Example("if input.DeviceChanged() {"))
|
|
bind.Func1(m, "Glyph", func(c *bind.Ctx, a string) string {
|
|
s, p, ok := lookup(c, 0, a)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return s.GlyphP(p, a)
|
|
}, bind.Doc("Glyph returns the binding of the action for the active device as text, like [Space] or [A]."), bind.Params("action"),
|
|
bind.Example("hint.Text = \"Jump \" + input.Glyph(\"Jump\")"))
|
|
bind.Func2(m, "BeginRebind", func(c *bind.Ctx, a string, slot int64) bool {
|
|
s, _, ok := lookup(c, 0, a)
|
|
return ok && s.BeginRebind(a, int(slot))
|
|
}, bind.Doc("BeginRebind waits for the next key, button or stick and binds it to the slot of the action. Escape cancels."),
|
|
bind.Params("action", "slot"), bind.Example("input.BeginRebind(\"Jump\", 0)"))
|
|
bind.Func0(m, "Rebinding", func(c *bind.Ctx) bool {
|
|
s := Of(c.World)
|
|
return s != nil && s.Rebinding()
|
|
}, bind.Doc("Rebinding reports whether a rebind waits for input."), bind.Example("if input.Rebinding() {"))
|
|
bind.Func0(m, "RebindConflict", func(c *bind.Ctx) string {
|
|
if s := Of(c.World); s != nil {
|
|
return s.RebindConflict()
|
|
}
|
|
return ""
|
|
}, bind.Doc("RebindConflict names the action that already used the new control, or explains why it was refused."),
|
|
bind.Example("if input.RebindConflict() != \"\" {"))
|
|
bind.Proc1(m, "ResetBinding", func(c *bind.Ctx, a string) {
|
|
if s, _, ok := lookup(c, 0, a); ok {
|
|
s.ResetBinding(a)
|
|
}
|
|
}, bind.Doc("ResetBinding restores the project bindings of the action."), bind.Params("action"), bind.Example("input.ResetBinding(\"Jump\")"))
|
|
bind.Proc0(m, "ResetBindings", func(c *bind.Ctx) {
|
|
if s := Of(c.World); s != nil {
|
|
s.ResetBindings()
|
|
}
|
|
}, bind.Doc("ResetBindings restores every project binding."), bind.Example("input.ResetBindings()"))
|
|
bind.Proc0(m, "SaveBindings", func(c *bind.Ctx) {
|
|
if s := Of(c.World); s != nil {
|
|
if err := s.SaveBindings(); err != nil {
|
|
c.Fail(err.Error())
|
|
}
|
|
}
|
|
}, bind.Doc("SaveBindings writes the player's settings profile now; changes are also saved on their own."), bind.Example("input.SaveBindings()"))
|
|
}
|