@skewedaspect/sage 0.3.0

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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/Readme.md +53 -0
  3. package/dist/classes/bindings/toggle.d.ts +122 -0
  4. package/dist/classes/bindings/trigger.d.ts +79 -0
  5. package/dist/classes/bindings/value.d.ts +104 -0
  6. package/dist/classes/entity.d.ts +83 -0
  7. package/dist/classes/eventBus.d.ts +94 -0
  8. package/dist/classes/gameEngine.d.ts +57 -0
  9. package/dist/classes/input/gamepad.d.ts +94 -0
  10. package/dist/classes/input/keyboard.d.ts +66 -0
  11. package/dist/classes/input/mouse.d.ts +80 -0
  12. package/dist/classes/input/readers/gamepad.d.ts +77 -0
  13. package/dist/classes/input/readers/keyboard.d.ts +60 -0
  14. package/dist/classes/input/readers/mouse.d.ts +45 -0
  15. package/dist/classes/loggers/consoleBackend.d.ts +29 -0
  16. package/dist/classes/loggers/nullBackend.d.ts +14 -0
  17. package/dist/engines/scene.d.ts +11 -0
  18. package/dist/interfaces/action.d.ts +20 -0
  19. package/dist/interfaces/binding.d.ts +144 -0
  20. package/dist/interfaces/entity.d.ts +9 -0
  21. package/dist/interfaces/game.d.ts +26 -0
  22. package/dist/interfaces/input.d.ts +181 -0
  23. package/dist/interfaces/logger.d.ts +88 -0
  24. package/dist/managers/binding.d.ts +185 -0
  25. package/dist/managers/entity.d.ts +70 -0
  26. package/dist/managers/game.d.ts +20 -0
  27. package/dist/managers/input.d.ts +56 -0
  28. package/dist/managers/level.d.ts +55 -0
  29. package/dist/sage.d.ts +20 -0
  30. package/dist/sage.es.js +2208 -0
  31. package/dist/sage.es.js.map +1 -0
  32. package/dist/sage.umd.js +2 -0
  33. package/dist/sage.umd.js.map +1 -0
  34. package/dist/utils/capabilities.d.ts +2 -0
  35. package/dist/utils/graphics.d.ts +10 -0
  36. package/dist/utils/logger.d.ts +66 -0
  37. package/dist/utils/physics.d.ts +2 -0
  38. package/dist/utils/version.d.ts +5 -0
  39. package/docs/architecture.md +129 -0
  40. package/docs/behaviors.md +706 -0
  41. package/docs/binding_system.md +820 -0
  42. package/docs/design/input.md +86 -0
  43. package/docs/entity_system.md +538 -0
  44. package/docs/eventbus.md +225 -0
  45. package/docs/getting_started.md +264 -0
  46. package/docs/images/sage_logo.png +0 -0
  47. package/docs/images/sage_logo_shape.png +0 -0
  48. package/docs/overview.md +38 -0
  49. package/docs/physics_system.md +686 -0
  50. package/docs/scene_system.md +513 -0
  51. package/package.json +69 -0
  52. package/src/classes/bindings/toggle.ts +261 -0
  53. package/src/classes/bindings/trigger.ts +211 -0
  54. package/src/classes/bindings/value.ts +227 -0
  55. package/src/classes/entity.ts +256 -0
  56. package/src/classes/eventBus.ts +259 -0
  57. package/src/classes/gameEngine.ts +125 -0
  58. package/src/classes/input/gamepad.ts +388 -0
  59. package/src/classes/input/keyboard.ts +189 -0
  60. package/src/classes/input/mouse.ts +276 -0
  61. package/src/classes/input/readers/gamepad.ts +179 -0
  62. package/src/classes/input/readers/keyboard.ts +123 -0
  63. package/src/classes/input/readers/mouse.ts +133 -0
  64. package/src/classes/loggers/consoleBackend.ts +135 -0
  65. package/src/classes/loggers/nullBackend.ts +51 -0
  66. package/src/engines/scene.ts +112 -0
  67. package/src/images/sage_logo.svg +172 -0
  68. package/src/images/sage_logo_shape.svg +146 -0
  69. package/src/interfaces/action.ts +30 -0
  70. package/src/interfaces/binding.ts +191 -0
  71. package/src/interfaces/entity.ts +21 -0
  72. package/src/interfaces/game.ts +44 -0
  73. package/src/interfaces/input.ts +221 -0
  74. package/src/interfaces/logger.ts +118 -0
  75. package/src/managers/binding.ts +729 -0
  76. package/src/managers/entity.ts +252 -0
  77. package/src/managers/game.ts +111 -0
  78. package/src/managers/input.ts +233 -0
  79. package/src/managers/level.ts +261 -0
  80. package/src/sage.ts +119 -0
  81. package/src/types/global.d.ts +11 -0
  82. package/src/utils/capabilities.ts +16 -0
  83. package/src/utils/graphics.ts +148 -0
  84. package/src/utils/logger.ts +225 -0
  85. package/src/utils/physics.ts +16 -0
  86. package/src/utils/version.ts +11 -0
@@ -0,0 +1,276 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Mouse Input Resource Access
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ // Interfaces
6
+ import type { ButtonState, MouseDevice, MouseInputState, Position } from '../../interfaces/input.ts';
7
+
8
+ //----------------------------------------------------------------------------------------------------------------------
9
+
10
+ /**
11
+ * Callback type for mouse device connection events
12
+ */
13
+ export type MouseDeviceCallback = (device : MouseDevice) => void;
14
+
15
+ /**
16
+ * Callback type for mouse input events
17
+ */
18
+ export type MouseInputCallback = (device : MouseDevice, state : MouseInputState) => void;
19
+
20
+ /**
21
+ * Responsible for tracking mouse state and notifying subscribers of changes.
22
+ */
23
+ export class MouseInputPlugin
24
+ {
25
+ private _targetElement : HTMLElement;
26
+ private _mouseDevice : MouseDevice;
27
+ private _buttonState : Record<string, ButtonState> = {};
28
+ private _axesState : Record<string, number> = {};
29
+ private _position : Position = {
30
+ absolute: { x: 0, y: 0 },
31
+ relative: { x: 0, y: 0 },
32
+ };
33
+
34
+ private _wheelState = {
35
+ deltaX: 0,
36
+ deltaY: 0,
37
+ deltaZ: 0,
38
+ deltaMode: 0,
39
+ };
40
+
41
+ // Callbacks
42
+ private _onDeviceConnected ?: MouseDeviceCallback;
43
+ private _onInputChanged ?: MouseInputCallback;
44
+
45
+ //------------------------------------------------------------------------------------------------------------------
46
+
47
+ /**
48
+ * Create a new MouseResourceAccess
49
+ *
50
+ * @param targetElement - The DOM element to attach mouse listeners to (defaults to document.body)
51
+ */
52
+ constructor(targetElement : HTMLElement = document.body)
53
+ {
54
+ this._targetElement = targetElement;
55
+
56
+ // Initialize mouse device
57
+ this._mouseDevice = {
58
+ id: 'mouse-0',
59
+ name: 'Mouse',
60
+ type: 'mouse',
61
+ connected: true,
62
+ };
63
+
64
+ // Set up event listeners
65
+ this._setupMouseEvents();
66
+
67
+ // Initialize axes to zero
68
+ this._axesState['axis-x'] = 0;
69
+ this._axesState['axis-y'] = 0;
70
+
71
+ // Notify that device is connected (on next tick to allow callback registration)
72
+ setTimeout(() => this._notifyDeviceConnected(), 0);
73
+ }
74
+
75
+ //------------------------------------------------------------------------------------------------------------------
76
+ // Public API
77
+ //------------------------------------------------------------------------------------------------------------------
78
+
79
+ /**
80
+ * Register a callback for device connected events
81
+ *
82
+ * @param callback - The callback to register
83
+ */
84
+ public onDeviceConnected(callback : MouseDeviceCallback) : void
85
+ {
86
+ this._onDeviceConnected = callback;
87
+ }
88
+
89
+ /**
90
+ * Register a callback for input changed events
91
+ *
92
+ * @param callback - The callback to register
93
+ */
94
+ public onInputChanged(callback : MouseInputCallback) : void
95
+ {
96
+ this._onInputChanged = callback;
97
+ }
98
+
99
+ /**
100
+ * Get the current mouse state
101
+ */
102
+ public getState() : MouseInputState
103
+ {
104
+ return {
105
+ type: 'mouse',
106
+ buttons: { ...this._buttonState },
107
+ axes: { ...this._axesState },
108
+ position: {
109
+ absolute: { ...this._position.absolute },
110
+ relative: { ...this._position.relative },
111
+ },
112
+ wheel: { ...this._wheelState },
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Get the mouse device
118
+ */
119
+ public getDevice() : MouseDevice
120
+ {
121
+ return { ...this._mouseDevice };
122
+ }
123
+
124
+ /**
125
+ * Destroy the mouse resource access and clean up event listeners
126
+ */
127
+ public destroy() : void
128
+ {
129
+ // Remove mouse event listeners
130
+ this._targetElement.removeEventListener('mousedown', this._handleMouseDown);
131
+ this._targetElement.removeEventListener('mouseup', this._handleMouseUp);
132
+ this._targetElement.removeEventListener('mousemove', this._handleMouseMove);
133
+ this._targetElement.removeEventListener('wheel', this._handleMouseWheel);
134
+ }
135
+
136
+ //------------------------------------------------------------------------------------------------------------------
137
+ // Private Methods
138
+ //------------------------------------------------------------------------------------------------------------------
139
+
140
+ /**
141
+ * Set up mouse event listeners
142
+ */
143
+ private _setupMouseEvents() : void
144
+ {
145
+ // Bind handlers to this instance
146
+ this._handleMouseDown = this._handleMouseDown.bind(this);
147
+ this._handleMouseUp = this._handleMouseUp.bind(this);
148
+ this._handleMouseMove = this._handleMouseMove.bind(this);
149
+ this._handleMouseWheel = this._handleMouseWheel.bind(this);
150
+
151
+ // Add event listeners
152
+ this._targetElement.addEventListener('mousedown', this._handleMouseDown);
153
+ this._targetElement.addEventListener('mouseup', this._handleMouseUp);
154
+ this._targetElement.addEventListener('mousemove', this._handleMouseMove);
155
+ this._targetElement.addEventListener('wheel', this._handleMouseWheel);
156
+ }
157
+
158
+ /**
159
+ * Handle mouse button down events
160
+ */
161
+ private _handleMouseDown(event : MouseEvent) : void
162
+ {
163
+ const buttonKey = `button-${ event.button }`;
164
+ const buttonState : ButtonState = {
165
+ pressed: true,
166
+ };
167
+
168
+ this._buttonState[buttonKey] = buttonState;
169
+
170
+ this._notifyInputChanged({
171
+ type: 'mouse',
172
+ event,
173
+ buttons: { ...this._buttonState },
174
+ axes: { ...this._axesState },
175
+ position: { ...this._position },
176
+ });
177
+ }
178
+
179
+ /**
180
+ * Handle mouse button up events
181
+ */
182
+ private _handleMouseUp(event : MouseEvent) : void
183
+ {
184
+ const buttonKey = `button-${ event.button }`;
185
+ const buttonState : ButtonState = {
186
+ pressed: false,
187
+ };
188
+
189
+ this._buttonState[buttonKey] = buttonState;
190
+
191
+ this._notifyInputChanged({
192
+ type: 'mouse',
193
+ event,
194
+ buttons: { ...this._buttonState },
195
+ axes: { ...this._axesState },
196
+ position: { ...this._position },
197
+ });
198
+ }
199
+
200
+ /**
201
+ * Handle mouse move events
202
+ */
203
+ private _handleMouseMove(event : MouseEvent) : void
204
+ {
205
+ // Update position
206
+ this._position = {
207
+ absolute: {
208
+ x: event.clientX,
209
+ y: event.clientY,
210
+ },
211
+ relative: {
212
+ x: event.movementX,
213
+ y: event.movementY,
214
+ },
215
+ };
216
+
217
+ // Update axis values
218
+ this._axesState['axis-x'] = event.clientX;
219
+ this._axesState['axis-y'] = event.clientY;
220
+
221
+ this._notifyInputChanged({
222
+ type: 'mouse',
223
+ event,
224
+ buttons: { ...this._buttonState },
225
+ axes: { ...this._axesState },
226
+ position: { ...this._position },
227
+ });
228
+ }
229
+
230
+ /**
231
+ * Handle mouse wheel events
232
+ */
233
+ private _handleMouseWheel(event : WheelEvent) : void
234
+ {
235
+ // Update wheel state
236
+ this._wheelState = {
237
+ deltaX: event.deltaX,
238
+ deltaY: event.deltaY,
239
+ deltaZ: event.deltaZ,
240
+ deltaMode: event.deltaMode,
241
+ };
242
+
243
+ this._notifyInputChanged({
244
+ type: 'mouse',
245
+ event,
246
+ buttons: { ...this._buttonState },
247
+ axes: { ...this._axesState },
248
+ position: { ...this._position },
249
+ wheel: { ...this._wheelState },
250
+ });
251
+ }
252
+
253
+ /**
254
+ * Notify subscribers of device connected event
255
+ */
256
+ private _notifyDeviceConnected() : void
257
+ {
258
+ if(this._onDeviceConnected)
259
+ {
260
+ this._onDeviceConnected(this._mouseDevice);
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Notify subscribers of input changed event
266
+ */
267
+ private _notifyInputChanged(state : MouseInputState) : void
268
+ {
269
+ if(this._onInputChanged)
270
+ {
271
+ this._onInputChanged(this._mouseDevice, state);
272
+ }
273
+ }
274
+ }
275
+
276
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,179 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Gamepad Value Reader
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ import type { DeviceValueReader, GamepadValueReaderDefinition, InputState } from '../../../interfaces/input.ts';
6
+
7
+ //----------------------------------------------------------------------------------------------------------------------
8
+
9
+ /**
10
+ * Types of gamepad input sources
11
+ */
12
+ export type GamepadSourceType = 'button' | 'axis';
13
+
14
+ /**
15
+ * Options for configuring a gamepad value reader
16
+ */
17
+ export interface GamepadReaderOptions
18
+ {
19
+ /**
20
+ * Whether to use the analog value instead of boolean pressed state for buttons
21
+ */
22
+ useAnalogValue ?: boolean;
23
+
24
+ /**
25
+ * Deadzone value (0.0 - 1.0) for analog axes
26
+ * Input values below this threshold will be treated as 0
27
+ */
28
+ deadzone ?: number;
29
+
30
+ /**
31
+ * Whether to invert the axis values
32
+ */
33
+ invert ?: boolean;
34
+ }
35
+
36
+ //----------------------------------------------------------------------------------------------------------------------
37
+
38
+ /**
39
+ * Reads values from gamepad input states
40
+ */
41
+ export class GamepadValueReader implements DeviceValueReader
42
+ {
43
+ /**
44
+ * The type of gamepad input
45
+ */
46
+ readonly sourceType : GamepadSourceType;
47
+
48
+ /**
49
+ * The specific key for this input
50
+ */
51
+ readonly sourceKey : string;
52
+
53
+ /**
54
+ * Whether to use analog value for buttons
55
+ */
56
+ private readonly useAnalogValue : boolean;
57
+
58
+ /**
59
+ * Deadzone value for axes
60
+ */
61
+ private readonly deadzone : number;
62
+
63
+ /**
64
+ * Whether to invert axis values
65
+ */
66
+ private readonly invert : boolean;
67
+
68
+ /**
69
+ * Creates a new GamepadValueReader
70
+ *
71
+ * @param sourceType - Type of gamepad input to monitor (button or axis)
72
+ * @param sourceKey - The specific key for this input type
73
+ * @param options - Configuration options
74
+ */
75
+ constructor(sourceType : GamepadSourceType, sourceKey : string, options : GamepadReaderOptions = {})
76
+ {
77
+ this.sourceType = sourceType;
78
+ this.sourceKey = sourceKey;
79
+ this.useAnalogValue = options.useAnalogValue || false;
80
+ this.deadzone = options.deadzone || 0.1; // Default 10% deadzone
81
+ this.invert = options.invert || false;
82
+ }
83
+
84
+ /**
85
+ * Gets the value of the input source based on the current state
86
+ *
87
+ * @param state - The current input state
88
+ * @returns The value of the input source
89
+ */
90
+ public getValue(state : InputState) : boolean | number | undefined
91
+ {
92
+ // Check if the state is from the correct device type
93
+ if(state.type !== 'gamepad')
94
+ {
95
+ return undefined;
96
+ }
97
+
98
+ switch (this.sourceType)
99
+ {
100
+ case 'button':
101
+ {
102
+ const buttonState = state.buttons[this.sourceKey];
103
+
104
+ if(!buttonState)
105
+ {
106
+ return undefined;
107
+ }
108
+
109
+ return this.useAnalogValue ? buttonState.value : buttonState.pressed;
110
+ }
111
+
112
+ case 'axis':
113
+ {
114
+ let value = state.axes[this.sourceKey];
115
+
116
+ if(value === undefined)
117
+ {
118
+ return undefined;
119
+ }
120
+
121
+ // Apply deadzone
122
+ if(Math.abs(value) < this.deadzone)
123
+ {
124
+ value = 0;
125
+ }
126
+
127
+ // Apply inversion if needed
128
+ return this.invert ? -value : value;
129
+ }
130
+
131
+ default:
132
+ return undefined;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Creates a GamepadValueReader from a string representation
138
+ *
139
+ * @param sourceTypeString - String in format "sourceType:sourceKey" (e.g., "button:0", "axis:1")
140
+ * @param options - Optional configuration options
141
+ * @returns A new GamepadValueReader instance
142
+ */
143
+ public static fromString(sourceTypeString : string, options : GamepadReaderOptions = {}) : GamepadValueReader
144
+ {
145
+ const [ sourceType, sourceKey ] = sourceTypeString.split(':');
146
+
147
+ if(!sourceType || !sourceKey)
148
+ {
149
+ throw new Error(`Invalid gamepad source format: ${ sourceTypeString }`);
150
+ }
151
+
152
+ return new GamepadValueReader(
153
+ sourceType as GamepadSourceType,
154
+ sourceKey,
155
+ options
156
+ );
157
+ }
158
+
159
+ /**
160
+ * Returns a JSON-serializable representation of this gamepad value reader
161
+ *
162
+ * @returns A simple object representation that can be converted to JSON
163
+ */
164
+ public toJSON() : GamepadValueReaderDefinition
165
+ {
166
+ return {
167
+ type: 'gamepad',
168
+ sourceType: this.sourceType,
169
+ sourceKey: this.sourceKey,
170
+ options: {
171
+ useAnalogValue: this.useAnalogValue,
172
+ deadzone: this.deadzone,
173
+ invert: this.invert,
174
+ },
175
+ };
176
+ }
177
+ }
178
+
179
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,123 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Keyboard Value Reader
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ import type {
6
+ DeviceValueReader,
7
+ InputState,
8
+ KeyboardInputState,
9
+ KeyboardValueReaderDefinition,
10
+ } from '../../../interfaces/input.ts';
11
+
12
+ //----------------------------------------------------------------------------------------------------------------------
13
+
14
+ /**
15
+ * Types of keyboard input sources
16
+ */
17
+ export type KeyboardSourceType = 'key';
18
+
19
+ /**
20
+ * Options for configuring a keyboard value reader
21
+ */
22
+ export interface KeyboardReaderOptions
23
+ {
24
+ /**
25
+ * Whether to use delta state instead of current state
26
+ * When true, the source will only return true on the initial key press
27
+ */
28
+ useDelta ?: boolean;
29
+ }
30
+
31
+ //----------------------------------------------------------------------------------------------------------------------
32
+
33
+ /**
34
+ * Reads values from keyboard input states
35
+ */
36
+ export class KeyboardValueReader implements DeviceValueReader
37
+ {
38
+ /**
39
+ * The type of keyboard input (always 'key')
40
+ */
41
+ public readonly sourceType : KeyboardSourceType = 'key';
42
+
43
+ /**
44
+ * The key code to monitor (e.g., "KeyA", "Space")
45
+ */
46
+ public readonly sourceKey : string;
47
+
48
+ /**
49
+ * Whether to use delta state instead of current state
50
+ */
51
+ private readonly useDelta : boolean;
52
+
53
+ /**
54
+ * Creates a new KeyboardValueReader
55
+ *
56
+ * @param keyCode - The key code to monitor (e.g., "KeyA", "Space")
57
+ * @param options - Configuration options
58
+ */
59
+ constructor(keyCode : string, options : KeyboardReaderOptions = {})
60
+ {
61
+ this.sourceKey = keyCode;
62
+ this.useDelta = options.useDelta || false;
63
+ }
64
+
65
+ /**
66
+ * Gets the value of the key state
67
+ *
68
+ * @param state - The input state
69
+ * @returns The key state value or undefined if the state type is not keyboard
70
+ */
71
+ public getValue(state : InputState) : boolean | undefined
72
+ {
73
+ // Check if the state is from the correct device type
74
+ if(state.type !== 'keyboard')
75
+ {
76
+ return undefined;
77
+ }
78
+
79
+ const keyboardState = state as KeyboardInputState;
80
+
81
+ // Get value from either the current state or delta state based on configuration
82
+ if(this.useDelta)
83
+ {
84
+ return keyboardState.delta[this.sourceKey];
85
+ }
86
+ else
87
+ {
88
+ return keyboardState.keys[this.sourceKey];
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Creates a KeyboardValueReader from a string representation
94
+ *
95
+ * @param sourceKey - The key code (e.g., "KeyA", "Space")
96
+ * @param options - Optional configuration
97
+ * @returns A new KeyboardValueReader instance
98
+ */
99
+ public static fromString(sourceKey : string, options : KeyboardReaderOptions = {}) : KeyboardValueReader
100
+ {
101
+ return new KeyboardValueReader(sourceKey, options);
102
+ }
103
+
104
+ /**
105
+ * Returns a JSON-serializable representation of this keyboard value reader
106
+ *
107
+ * @returns A simple object representation that can be converted to JSON
108
+ */
109
+ public toJSON() : KeyboardValueReaderDefinition
110
+ {
111
+ return {
112
+
113
+ type: 'keyboard',
114
+ sourceType: this.sourceType,
115
+ sourceKey: this.sourceKey,
116
+ options: {
117
+ useDelta: this.useDelta,
118
+ },
119
+ };
120
+ }
121
+ }
122
+
123
+ //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,133 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // Mouse Value Reader
3
+ //----------------------------------------------------------------------------------------------------------------------
4
+
5
+ import type { DeviceValueReader, InputState, MouseValueReaderDefinition } from '../../../interfaces/input.ts';
6
+
7
+ //----------------------------------------------------------------------------------------------------------------------
8
+
9
+ /**
10
+ * Types of mouse input sources
11
+ */
12
+ export type MouseSourceType = 'button' | 'position' | 'wheel';
13
+
14
+ //----------------------------------------------------------------------------------------------------------------------
15
+
16
+ /**
17
+ * Reads values from mouse input states
18
+ */
19
+ export class MouseValueReader implements DeviceValueReader
20
+ {
21
+ /**
22
+ * The type of mouse input
23
+ */
24
+ readonly sourceType : MouseSourceType;
25
+
26
+ /**
27
+ * The specific key for this input
28
+ */
29
+ readonly sourceKey : string;
30
+
31
+ /**
32
+ * Creates a new MouseValueReader
33
+ *
34
+ * @param sourceType - Type of mouse input to monitor (button, position, wheel)
35
+ * @param sourceKey - The specific key for this input type
36
+ */
37
+ constructor(sourceType : MouseSourceType, sourceKey : string)
38
+ {
39
+ this.sourceType = sourceType;
40
+ this.sourceKey = sourceKey;
41
+ }
42
+
43
+ /**
44
+ * Gets the value of the mouse input source
45
+ *
46
+ * @param state - The current input state
47
+ * @returns The value of the input source
48
+ */
49
+ public getValue(state : InputState) : boolean | number | undefined
50
+ {
51
+ // Check if the state is from the correct device type
52
+ if(state.type !== 'mouse')
53
+ {
54
+ return undefined;
55
+ }
56
+
57
+ switch (this.sourceType)
58
+ {
59
+ case 'button':
60
+ {
61
+ const buttonState = state.buttons[this.sourceKey];
62
+ return buttonState ? buttonState.pressed : undefined;
63
+ }
64
+
65
+ case 'position':
66
+ {
67
+ // Position keys are in format "positionType:axis" (e.g., "absolute:x")
68
+ const [ posType, axis ] = this.sourceKey.split(':');
69
+
70
+ if(!posType || !axis || (posType !== 'absolute' && posType !== 'relative')
71
+ || (axis !== 'x' && axis !== 'y'))
72
+ {
73
+ return undefined;
74
+ }
75
+
76
+ return state.position[posType][axis];
77
+ }
78
+
79
+ case 'wheel':
80
+ {
81
+ const isValidWheelKey = this.sourceKey === 'deltaX' || this.sourceKey === 'deltaY'
82
+ || this.sourceKey === 'deltaZ';
83
+ if(state.wheel && isValidWheelKey)
84
+ {
85
+ return state.wheel[this.sourceKey];
86
+ }
87
+ return undefined;
88
+ }
89
+
90
+ default:
91
+ return undefined;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Creates a MouseValueReader from a string representation
97
+ *
98
+ * @param sourceTypeString - String in format "sourceType:sourceKey" (e.g., "button:0", "position:absolute:x")
99
+ * @returns A new MouseValueReader instance
100
+ */
101
+ public static fromString(sourceTypeString : string) : MouseValueReader
102
+ {
103
+ const [ sourceType, ...keyParts ] = sourceTypeString.split(':');
104
+ const sourceKey = keyParts.join(':');
105
+
106
+ if(!sourceType || !sourceKey)
107
+ {
108
+ throw new Error(`Invalid mouse source format: ${ sourceTypeString }`);
109
+ }
110
+
111
+ return new MouseValueReader(
112
+ sourceType as MouseSourceType,
113
+ sourceKey
114
+ );
115
+ }
116
+
117
+ /**
118
+ * Returns a JSON-serializable representation of this mouse value reader
119
+ *
120
+ * @returns A simple object representation that can be converted to JSON
121
+ */
122
+ public toJSON() : MouseValueReaderDefinition
123
+ {
124
+ // Always return consistent 'mouse' type with sourceType and sourceKey
125
+ return {
126
+ type: 'mouse',
127
+ sourceType: this.sourceType,
128
+ sourceKey: this.sourceKey,
129
+ };
130
+ }
131
+ }
132
+
133
+ //----------------------------------------------------------------------------------------------------------------------