@rbxts/axis 0.2.3-v.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 teakzc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
19
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
21
+ OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @rbxts/axis
2
+
3
+ Typings for [axis](https://neond00m.github.io/Axis/)
@@ -0,0 +1,27 @@
1
+ local match = require(script.Parent.match)
2
+ local types = require(script.Parent.types)
3
+ local previousInputDevice = "Desktop"
4
+
5
+ local function getInputDevice(input: Enum.UserInputType): types.DeviceType
6
+ local result = match(input.Name) {
7
+ "Gamepad1", "Gamepad",
8
+ "Gamepad2", "Gamepad",
9
+ "Gamepad3", "Gamepad",
10
+ "Gamepad4", "Gamepad",
11
+ "Gamepad5", "Gamepad",
12
+ "Gamepad6", "Gamepad",
13
+ "Gamepad7", "Gamepad",
14
+ "Gamepad8", "Gamepad",
15
+ "Touch", "Touch",
16
+ "MouseButton1", "Desktop",
17
+ "MouseButton2", "Desktop",
18
+ "MouseButton3", "Desktop",
19
+ "MouseMovement", "Desktop",
20
+ "MouseWheel", "Desktop",
21
+ "Keyboard", "Desktop"
22
+ }
23
+ previousInputDevice = result or previousInputDevice
24
+ return previousInputDevice
25
+ end
26
+
27
+ return getInputDevice
package/out/index.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ export namespace Axis {
2
+ /**
3
+ * @within Axis
4
+ * @type DeviceType "Desktop" | "Touch" | "Controller"
5
+ */
6
+ type DeviceType = "Desktop" | "Touch" | "Controller";
7
+
8
+ /**
9
+ * @within Axis
10
+ * @type Map<T> { [Enum | string]: T } & { Enum.KeyCode | Enum.UserInputType }
11
+ */
12
+ type _Map = Map<defined, defined>; //Map<Enum | string, T> & Array<Enum.KeyCode | Enum.UserInputType>;
13
+
14
+ interface Input<T> {
15
+ read: (controller?: number) => LuaTuple<[T, T]>;
16
+ pressing: (controller?: number) => boolean;
17
+ pressed: (controller?: number) => boolean;
18
+ released: (controller?: number) => boolean;
19
+ changed: (controller?: number) => boolean;
20
+ hold: (value: T, controller?: number) => void;
21
+ move: (value: T, controller?: number) => void;
22
+ map: (keyMap: _Map) => void;
23
+ update: () => void;
24
+
25
+ deadzone?: number;
26
+ vector: boolean;
27
+ current: T[];
28
+ previous: T[];
29
+ active: Array<Map<defined, T>>;
30
+ resets: Map<defined, defined>;
31
+ connections: RBXScriptConnection[];
32
+ keyMap: _Map;
33
+ inputMap: Map<Enum.KeyCode | Enum.UserInputType, T>;
34
+ }
35
+
36
+ type InputConstructor = <T>(input: _Map | (_Map & { deadzone?: number })) => Input<T>;
37
+
38
+ interface Axis {
39
+ device: (inputType: Enum.UserInputType | undefined) => DeviceType;
40
+ update: (inputs: Map<defined, Input<defined>>) => void;
41
+ input: InputConstructor;
42
+ }
43
+
44
+ function input<T>(inputMap: (_Map & { deadzone?: number }) | _Map): Input<T>;
45
+
46
+ function update(inputs: Map<defined, Input<defined>>): void;
47
+
48
+ function device(input?: Enum.UserInputType): DeviceType;
49
+ }
package/out/init.luau ADDED
@@ -0,0 +1,63 @@
1
+ local UserInputService = game:GetService("UserInputService")
2
+ local types = require(script.types)
3
+ local constructor = require(script.input)
4
+ local getInputDevice = require(script.getInputDevice)
5
+
6
+ --[=[
7
+ @class Axis
8
+ The Axis library used to define input axes, get devices, and update inputs
9
+ ]=]
10
+ --[=[
11
+ @within Axis
12
+ @function input
13
+ @param keyMap Map<T>
14
+ @return Input<T>
15
+ Creates a new input axis with the provided keymap
16
+ ```lua
17
+ local attack = Axis.input {
18
+ Enum.KeyCode.E,
19
+ Enum.KeyCode.ButtonX,
20
+ }
21
+ ```
22
+ ]=]
23
+ local Axis = {
24
+ input = constructor,
25
+ }
26
+
27
+ --[=[
28
+ @within Axis
29
+ @function update
30
+ Updates all the provided inputs as a shorthand
31
+ ```lua
32
+ Axis.update(inputMap)
33
+ ```
34
+ ]=]
35
+ function Axis.update(inputs: { [any]: types.Input<any> })
36
+ for _, input in inputs do
37
+ input:update()
38
+ end
39
+ end
40
+
41
+ --[=[
42
+ @within Axis
43
+ @function device
44
+ @param input Enum.UserInputType?
45
+ @return DeviceType
46
+ Gets the device of the provided UserInputType (or the last UserInputType if none is provided)
47
+ ```lua
48
+ local device = Axis.device() --gets device of last input
49
+ if device == "Desktop" then
50
+ print("yay")
51
+ end
52
+ ```
53
+ ]=]
54
+ function Axis.device(input: Enum.UserInputType?): types.DeviceType
55
+ return getInputDevice(input or UserInputService:GetLastInputType())
56
+ end
57
+
58
+ export type DeviceType = types.DeviceType
59
+ export type Map<T> = types.Map<T>
60
+ export type Input<T> = types.Input<T>
61
+ export type Axis = types.Axis
62
+
63
+ return Axis :: types.Axis
package/out/input.luau ADDED
@@ -0,0 +1,337 @@
1
+ --!strict
2
+ --!optimize 2
3
+ --[=[
4
+ @class Input
5
+ The Input class used to define input axes
6
+ ]=]
7
+ local UserInputService = game:GetService("UserInputService")
8
+
9
+ local types = require(script.Parent.types)
10
+ local match = require(script.Parent.match)
11
+
12
+ local NEXT_HOLD_INDEX = 1
13
+
14
+ local function deadzone<T>(input: types.Input<T>, value: number): number
15
+ if value < (input.deadzone or 0.3) then
16
+ return 0
17
+ end
18
+ return value
19
+ end
20
+
21
+ --[=[
22
+ @within Input
23
+ @method getGamepad
24
+ @param inputType
25
+ @return number
26
+ Returns the gamepad index of the given inputType, 1 if not a gamepad
27
+ ]=]
28
+ local function getGamepad(inputType: Enum.UserInputType)
29
+ -- subtract from the Gamepad1 value to get the input
30
+ if inputType.Value < Enum.UserInputType.Gamepad1.Value or inputType.Value > Enum.UserInputType.Gamepad8.Value then return 1 end
31
+ return inputType.Value - Enum.UserInputType.Gamepad1.Value + 1
32
+ end
33
+
34
+ local function _reset<T>(input: types.Input<T>, controller: number, inputType: any)
35
+ input.resets[inputType] = {controller, false}-- maybe divide by controller too?
36
+ end
37
+
38
+ --[=[
39
+ @within Input
40
+ @method read
41
+ @param controller number?
42
+ @return T, T
43
+ Reads current and previous values for the axis
44
+ ```lua
45
+ local current, previous = attack:read()
46
+ ```
47
+ ]=]
48
+ local function read<T>(input: types.Input<T>, controller: number?): (T, T)
49
+ local default = if input.vector then vector.zero else 0
50
+ return (input.current[controller or 1] or default) :: T, (input.previous[controller or 1] or default) :: T
51
+ end
52
+
53
+ --[=[
54
+ @within Input
55
+ @method pressing
56
+ @param controller number?
57
+ @return boolean
58
+ Gets whether the axis has any active input
59
+ ]=]
60
+ local function pressing<T>(input: types.Input<T>, controller: number?): boolean
61
+ local value = read(input, controller)
62
+ local magnitude = if input.vector then vector.magnitude(value) else value
63
+ return magnitude ~= 0
64
+ end
65
+
66
+ --[=[
67
+ @within Input
68
+ @method changed
69
+ @param controller number?
70
+ @return boolean
71
+ Gets whether an axis has changed since the last update
72
+ ]=]
73
+ local function changed<T>(input: types.Input<T>, controller: number?): boolean
74
+ return input.current[controller or 1] ~= input.previous[controller or 1]
75
+ end
76
+
77
+ --[=[
78
+ @within Input
79
+ @method pressed
80
+ @param controller number?
81
+ @return boolean
82
+ Gets whether the axis was activated this update
83
+ ]=]
84
+ local function pressed<T>(input: types.Input<T>, controller: number?): boolean
85
+ return changed(input, controller) and pressing(input, controller)
86
+ end
87
+
88
+ --[=[
89
+ @within Input
90
+ @method released
91
+ @param controller number?
92
+ @return boolean
93
+ Gets whether the axis was deactivated this update
94
+ ]=]
95
+ local function released<T>(input: types.Input<T>, controller: number?): boolean
96
+ return changed(input, controller) and not pressing(input, controller)
97
+ end
98
+
99
+ --[=[
100
+ @within Input
101
+ @method hold
102
+ @param value T
103
+ @param controller number?
104
+ @return () -> ()
105
+ Adds a temporary manual input to the axis, and provides a function to release it
106
+ ```lua
107
+ local release = attack:hold(1) -- adds 1 to the axis
108
+ --release later
109
+ release() -- removes the added value
110
+ ```
111
+ ]=]
112
+ local function hold<T>(input: types.Input<T>, value: T, controller: number?): () -> ()
113
+ local id = NEXT_HOLD_INDEX
114
+ NEXT_HOLD_INDEX += 1
115
+ input.active[controller or 1][id] = (value or 1) :: T
116
+ return function()
117
+ input.active[controller or 1][id] = nil
118
+ end
119
+ end
120
+
121
+ --[=[
122
+ @within Input
123
+ @method move
124
+ @param value T
125
+ @param controller number?
126
+ Adds an input to the axis for a single frame
127
+ ]=]
128
+ local function move<T>(input: types.Input<T>, value: T, controller: number?)
129
+ local id = NEXT_HOLD_INDEX
130
+ NEXT_HOLD_INDEX += 1
131
+ input.active[controller or 1][id] = (value or 1) :: T
132
+ _reset(input, controller or 1, id)
133
+ end
134
+
135
+
136
+ --[=[
137
+ @within Input
138
+ @method map
139
+ @param keyMap Map<T>
140
+ Maps input types to an input axis
141
+ ```lua
142
+ attack:map {
143
+ Enum.KeyCode.Q,
144
+ Enum.KeyCode.ButtonA,
145
+ }
146
+ -- or to clear all input mappings
147
+ attack:map {}
148
+ ```
149
+ ]=]
150
+ local function map<T>(input: types.Input<T>, keyMap: types.Map<T>)
151
+ --disconnect previous connections
152
+ for _, connection in input.connections do
153
+ connection:Disconnect()
154
+ end
155
+
156
+ --for detecting the type of value for this input axis for this keymap
157
+ input.vector = nil -- nil, to be detected
158
+ local previousType = nil
159
+ local function setVector(vectorType: boolean, inputType: any)
160
+ if input.vector ~= nil and input.vector ~= vectorType then
161
+ local pre = `{previousType} was {if input.vector then "" else "not "}a vector input`
162
+ local now = `{inputType} is {if vectorType then "" else "not "}a vector input`
163
+ error(`[Axis] Input axis cannot be both vector and scalar.\n\t({pre} and {now})`)
164
+ end
165
+ input.vector = vectorType
166
+ previousType = inputType
167
+ end
168
+
169
+ if next(keyMap) == nil then
170
+ return -- let the player clear the keymap with an empty table
171
+ end
172
+
173
+ --set up connections
174
+ local group = {}
175
+ local newMap = {}
176
+ input.keyMap = keyMap
177
+ input.inputMap = newMap
178
+ for index, value in keyMap :: any do
179
+ local inputType = if type(index) == "number" then value else index
180
+ local modifier = if type(index) == "number" then 1 else value
181
+ -- print(`received {index} -> {value}, newmap[{inputType}] = {modifier}`)
182
+ newMap[inputType] = modifier
183
+ local autoType = type(modifier) ~= "number"
184
+
185
+ if inputType == Enum.UserInputType.MouseMovement then
186
+ setVector(true, inputType)
187
+ table.insert(input.connections, UserInputService.InputChanged:Connect(function(inputObject)
188
+ if inputObject.UserInputType ~= Enum.UserInputType.MouseMovement then
189
+ return
190
+ end
191
+ input.active[1][Enum.UserInputType.MouseMovement] = vector.create(inputObject.Delta.X, -inputObject.Delta.Y) * modifier
192
+ _reset(input, 1, Enum.UserInputType.MouseMovement)
193
+ end))
194
+ elseif inputType == Enum.UserInputType.MouseWheel then
195
+ setVector(autoType, inputType)
196
+ table.insert(input.connections, UserInputService.InputChanged:Connect(function(object, processed)
197
+ if processed then
198
+ return
199
+ end
200
+ if object.UserInputType ~= Enum.UserInputType.MouseWheel then
201
+ return
202
+ end
203
+ local position = object.Position.Z
204
+ input.active[1][Enum.UserInputType.MouseWheel] = position * modifier
205
+ _reset(input, 1, Enum.UserInputType.MouseWheel)
206
+ end))
207
+ elseif inputType == Enum.KeyCode.Thumbstick1 then
208
+ setVector(true, inputType)
209
+ table.insert(input.connections, UserInputService.InputChanged:Connect(function(object)
210
+ if object.KeyCode ~= Enum.KeyCode.Thumbstick1 then
211
+ return
212
+ end
213
+ local position = object.Position
214
+ input.active[getGamepad(object.UserInputType)][Enum.KeyCode.Thumbstick1] =
215
+ vector.create(deadzone(input, position.X), deadzone(input, position.Y)) * modifier
216
+ end))
217
+ elseif inputType == Enum.KeyCode.Thumbstick2 then
218
+ setVector(true, inputType)
219
+ table.insert(input.connections, UserInputService.InputChanged:Connect(function(object)
220
+ if object.KeyCode ~= Enum.KeyCode.Thumbstick2 then
221
+ return
222
+ end
223
+ local position = object.Position
224
+ input.active[getGamepad(object.UserInputType)][Enum.KeyCode.Thumbstick1] =
225
+ vector.create(deadzone(input, position.X), deadzone(input, position.Y)) * modifier
226
+ end))
227
+ elseif inputType == UserInputService.TouchSwipe then
228
+ setVector(true, inputType)
229
+ table.insert(input.connections, UserInputService.TouchSwipe:Connect(function(direction: Enum.SwipeDirection)
230
+ input.active[1][UserInputService.TouchSwipe] = modifier * match(direction) {
231
+ Enum.SwipeDirection.Left, vector.create(-1, 0),
232
+ Enum.SwipeDirection.Right, vector.create(1, 0),
233
+ Enum.SwipeDirection.Up, vector.create(0, 1),
234
+ Enum.SwipeDirection.Down, vector.create(0, -1),
235
+ }
236
+ end))
237
+ elseif inputType == UserInputService.TouchPinch then
238
+ setVector(autoType, inputType)
239
+ table.insert(input.connections, UserInputService.TouchPinch:Connect(function(_, scale: number, _, state, sunk)
240
+ if state == Enum.UserInputState.End then
241
+ input.active[1][UserInputService.TouchPinch] = nil
242
+ return
243
+ end
244
+ input.active[1][UserInputService.TouchPinch] = scale :: number & T
245
+ end))
246
+ else
247
+ setVector(autoType, inputType)
248
+ table.insert(group, inputType)
249
+ end
250
+ end
251
+
252
+ local function inputHappened(inputObject)
253
+ local controller = getGamepad(inputObject.UserInputType)
254
+ input.active[controller] = input.active[controller] or {}
255
+ local active = input.active[controller]
256
+ for _, enum in group do
257
+ if inputObject.KeyCode ~= enum and inputObject.UserInputType ~= enum then
258
+ continue
259
+ end
260
+
261
+ active[enum] = (if inputObject.UserInputState == Enum.UserInputState.Begin or
262
+ inputObject.UserInputState == Enum.UserInputState.Change then input.inputMap[enum] else nil) :: (nil & T)
263
+ end
264
+ end
265
+
266
+ local inputBegan = function(inputObject, sunk)
267
+ if not sunk then
268
+ inputHappened(inputObject)
269
+ end
270
+ end
271
+ table.insert(input.connections, UserInputService.InputBegan:Connect(inputBegan))
272
+ table.insert(input.connections, UserInputService.InputEnded:Connect(inputHappened))
273
+ end
274
+
275
+ --[=[
276
+ @within Input
277
+ @method update
278
+ Updates the current and previous values of the input axis
279
+ ]=]
280
+ local function update<T>(input: types.Input<T>)
281
+ -- reset old values
282
+ for inputType, reset in input.resets do
283
+ if not reset[2] then
284
+ reset[2] = true
285
+ continue
286
+ end
287
+ input.active[reset[1]][inputType] = nil
288
+ input.resets[inputType] = nil
289
+ end
290
+
291
+ -- iterate through all controllers
292
+ for i, set in input.active do
293
+ -- set last frame
294
+ input.previous[i] = input.current[i]
295
+
296
+ local result: T? = nil -- sum current values
297
+ for key, value: T in set do
298
+ if not result then
299
+ result = value
300
+ continue
301
+ end
302
+ result += value
303
+ end
304
+
305
+ input.current[i] = result :: T
306
+ end
307
+ end
308
+
309
+ local function new<T>(inputMap: types.Map<T> & { deadzone: number? }): types.Input<T>
310
+ local input: types.Input<T> = {
311
+ vector = false,
312
+ current = {},
313
+ previous = {},
314
+ active = {
315
+ {}, -- separate input axes for each controller
316
+ },
317
+ resets = {}, --resets input on next update
318
+ connections = {},
319
+ inputMap = inputMap,
320
+ deadzone = inputMap.deadzone,
321
+
322
+ read = read,
323
+ pressing = pressing,
324
+ pressed = pressed,
325
+ released = released,
326
+ changed = changed,
327
+ hold = hold,
328
+ map = map,
329
+ update = update,
330
+ move = move,
331
+ }
332
+ map(input, inputMap)
333
+
334
+ return input
335
+ end
336
+
337
+ return new :: types.InputConstructor
package/out/match.luau ADDED
@@ -0,0 +1,25 @@
1
+ --[[match statement that allows for custom equals metatable function
2
+ ```lua
3
+ local result = match "hello" {
4
+ "world", function()
5
+ return "failed"
6
+ end,
7
+ "hello", function()
8
+ return "success!"
9
+ end,
10
+ }
11
+ ```]]
12
+ return function<T, V...>(value: T, ...: V...)
13
+ local args = {...}
14
+ return function<R>(cases: {R | (T, V...) -> R}): R?
15
+ assert(type(cases) == 'table', "cases needs to be table")
16
+ for i = 1, #cases, 2 do
17
+ if value == cases[i] then
18
+ local result = cases[i + 1]
19
+ return if type(result) == "function" then
20
+ result(value,unpack(args)) else result
21
+ end
22
+ end
23
+ return nil
24
+ end
25
+ end
package/out/types.luau ADDED
@@ -0,0 +1,46 @@
1
+ --!strict
2
+ --[=[
3
+ @within Axis
4
+ @type DeviceType "Desktop" | "Touch" | "Controller"
5
+ ]=]
6
+ export type DeviceType = "Desktop" | "Touch" | "Controller"
7
+
8
+ --[=[
9
+ @within Axis
10
+ @type Map<T> { [Enum | string]: T } & { Enum.KeyCode | Enum.UserInputType }
11
+ ]=]
12
+ export type Map<T> = { [any]: any }
13
+ --{ [Enum.UserInputType | Enum.KeyCode | string]: T } & { Enum.KeyCode | Enum.UserInputType }
14
+
15
+ export type Input<T> = {
16
+ read: (Input<T>, controller: number?) -> (T, T),
17
+ pressing: (Input<T>, controller: number?) -> boolean,
18
+ pressed: (Input<T>, controller: number?) -> boolean,
19
+ released: (Input<T>, controller: number?) -> boolean,
20
+ changed: (Input<T>, controller: number?) -> boolean,
21
+ hold: (Input<T>, value: T, controller: number?) -> (),
22
+ move: (Input<T>, value: T, controller: number?) -> (),
23
+ map: (Input<T>, keyMap: Map<T>) -> nil,
24
+ update: (Input<T>) -> nil,
25
+
26
+ deadzone: number?,
27
+ vector: boolean,
28
+ current: { T },
29
+ previous: { T },
30
+ active: { { [any]: T } },
31
+ resets: { [any]: { any } },
32
+ connections: { RBXScriptConnection },
33
+ keyMap: Map<T>,
34
+ inputMap: { [Enum.KeyCode | Enum.UserInputType]: T },
35
+ }
36
+
37
+ export type InputConstructor = <T>(Map<T> | (Map<T> & { deadzone: number? })) -> Input<T>
38
+
39
+ export type Axis = {
40
+ device: (Enum.UserInputType?) -> DeviceType,
41
+ update: (inputs: { [any]: Input<any> } ) -> nil,
42
+ clear: (any) -> nil,
43
+ input: InputConstructor,
44
+ }
45
+
46
+ return {}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@rbxts/axis",
3
+ "version": "0.2.3-v.1",
4
+ "description": "roblox-ts typings for NeonD00m/axis",
5
+ "main": "out/init.lua",
6
+ "scripts": {
7
+ "build": "rbxtsc",
8
+ "watch": "rbxtsc -w",
9
+ "prepublishOnly": "npm run build"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/teakzc/axis-types.git"
14
+ },
15
+ "homepage": "https://neond00m.github.io/Axis/",
16
+ "keywords": [],
17
+ "author": "teakzc",
18
+ "license": "MIT",
19
+ "type": "commonjs",
20
+ "types": "out/index.d.ts",
21
+ "files": [
22
+ "out",
23
+ "!**/*.tsbuildinfo"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "devDependencies": {
29
+ "@rbxts/compiler-types": "^3.0.0-types.0",
30
+ "@rbxts/types": "^1.0.906",
31
+ "@typescript-eslint/eslint-plugin": "^8.55.0",
32
+ "@typescript-eslint/parser": "^8.55.0",
33
+ "eslint": "^9.39.2",
34
+ "eslint-config-prettier": "^10.1.8",
35
+ "eslint-plugin-prettier": "^5.5.5",
36
+ "eslint-plugin-roblox-ts": "^1.3.1",
37
+ "prettier": "^3.8.1",
38
+ "roblox-ts": "^3.0.0",
39
+ "typescript": "^5.9.3"
40
+ }
41
+ }