@skewedaspect/sage 0.9.0-beta.5 → 0.9.0-beta.7

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.
@@ -1,7 +1,7 @@
1
1
  import { GameEventBus } from "../eventBus.d.ts";
2
2
  import { Action } from "../../interfaces/action.d.ts";
3
3
  import { Binding, ToggleBindingDefinition } from "../../interfaces/binding.d.ts";
4
- import { DeviceValueReader, InputState } from "../../interfaces/input.d.ts";
4
+ import { DeviceType, DeviceValueReader, InputState } from "../../interfaces/input.d.ts";
5
5
  /**
6
6
  * Options for configuring a toggle binding
7
7
  */
@@ -49,6 +49,7 @@ export declare class ToggleBinding implements Binding {
49
49
  readonly action: Action;
50
50
  readonly context?: string;
51
51
  readonly deviceID: string;
52
+ readonly deviceType: DeviceType;
52
53
  readonly reader: DeviceValueReader;
53
54
  private readonly _invert;
54
55
  private readonly _threshold;
@@ -87,10 +88,11 @@ export declare class ToggleBinding implements Binding {
87
88
  *
88
89
  * @param action
89
90
  * @param deviceID
91
+ * @param deviceType
90
92
  * @param reader
91
93
  * @param options
92
94
  */
93
- constructor(action: Action, deviceID: string, reader: DeviceValueReader, options?: ToggleBindingOptions);
95
+ constructor(action: Action, deviceID: string, deviceType: DeviceType, reader: DeviceValueReader, options?: ToggleBindingOptions);
94
96
  /**
95
97
  * Process input state and emit an action event if the toggle flipped.
96
98
  *
@@ -1,7 +1,7 @@
1
1
  import { GameEventBus } from "../eventBus.d.ts";
2
2
  import { Action } from "../../interfaces/action.d.ts";
3
3
  import { Binding, TriggerBindingDefinition } from "../../interfaces/binding.d.ts";
4
- import { DeviceValueReader, InputState } from "../../interfaces/input.d.ts";
4
+ import { DeviceType, DeviceValueReader, InputState } from "../../interfaces/input.d.ts";
5
5
  /**
6
6
  * All supported edge modes for validation
7
7
  */
@@ -43,6 +43,7 @@ export declare class TriggerBinding implements Binding {
43
43
  readonly action: Action;
44
44
  readonly context?: string;
45
45
  readonly deviceID: string;
46
+ readonly deviceType: DeviceType;
46
47
  readonly reader: DeviceValueReader;
47
48
  private readonly _edgeMode;
48
49
  private readonly _threshold;
@@ -56,10 +57,11 @@ export declare class TriggerBinding implements Binding {
56
57
  *
57
58
  * @param action
58
59
  * @param deviceID
60
+ * @param deviceType
59
61
  * @param reader
60
62
  * @param options
61
63
  */
62
- constructor(action: Action, deviceID: string, reader: DeviceValueReader, options?: TriggerBindingOptions);
64
+ constructor(action: Action, deviceID: string, deviceType: DeviceType, reader: DeviceValueReader, options?: TriggerBindingOptions);
63
65
  /**
64
66
  * Process input state and emit an action event on the configured edge transition(s).
65
67
  *
@@ -1,7 +1,7 @@
1
1
  import { GameEventBus } from "../eventBus.d.ts";
2
2
  import { Action } from "../../interfaces/action.d.ts";
3
3
  import { Binding, ValueBindingDefinition } from "../../interfaces/binding.d.ts";
4
- import { DeviceValueReader, InputState } from "../../interfaces/input.d.ts";
4
+ import { DeviceType, DeviceValueReader, InputState } from "../../interfaces/input.d.ts";
5
5
  /**
6
6
  * Options for configuring a value binding
7
7
  */
@@ -61,6 +61,7 @@ export declare class ValueBinding implements Binding {
61
61
  readonly action: Action;
62
62
  readonly context?: string;
63
63
  readonly deviceID: string;
64
+ readonly deviceType: DeviceType;
64
65
  readonly reader: DeviceValueReader;
65
66
  private readonly _scale;
66
67
  private readonly _offset;
@@ -84,7 +85,7 @@ export declare class ValueBinding implements Binding {
84
85
  * @param reader
85
86
  * @param options
86
87
  */
87
- constructor(action: Action, deviceID: string, reader: DeviceValueReader, options?: ValueBindingOptions);
88
+ constructor(action: Action, deviceID: string, deviceType: DeviceType, reader: DeviceValueReader, options?: ValueBindingOptions);
88
89
  /**
89
90
  * Process input state, apply scaling/deadzone/clamping, and emit an action event if the value changed.
90
91
  *
@@ -3,6 +3,26 @@ import { GameEntityBehaviorConstructor } from "../interfaces/entity.d.ts";
3
3
  import { Destroyable } from "../interfaces/lifecycle.d.ts";
4
4
  import { GameEvent, GameEventBus } from "./eventBus.d.ts";
5
5
  import { GameEngine } from "./gameEngine.d.ts";
6
+ /**
7
+ * Result of a request sent to an entity's behaviors.
8
+ * `success: true` means a behavior handled the request and returned a value.
9
+ * `success: false` means no behavior handled it, or a behavior threw an error.
10
+ */
11
+ export type RequestResult<T> = {
12
+ success: true;
13
+ value: T;
14
+ } | {
15
+ success: false;
16
+ error: string;
17
+ };
18
+ /**
19
+ * Narrow interface for entity-to-manager messaging delegation.
20
+ * Avoids a circular import between entity.ts and managers/entity.ts.
21
+ */
22
+ interface EntityMessenger {
23
+ send(target: string, type: string, payload?: unknown, senderID?: string): Promise<void>;
24
+ request<T>(target: string, type: string, payload?: unknown, senderID?: string): Promise<RequestResult<T>>;
25
+ }
6
26
  /**
7
27
  * Options for attaching a behavior at a specific position.
8
28
  * Only one positioning option should be specified.
@@ -27,6 +47,8 @@ interface SimpleGameEntity<EntityState extends object = object> {
27
47
  state: EntityState;
28
48
  eventBus: GameEventBus;
29
49
  tags: ReadonlySet<string>;
50
+ send(target: string, type: string, payload?: unknown): Promise<void>;
51
+ request<T>(target: string, type: string, payload?: unknown): Promise<RequestResult<T>>;
30
52
  }
31
53
  /**
32
54
  * Base class for game entity behaviors. Subclass this to define how an entity reacts to events
@@ -52,6 +74,14 @@ export declare abstract class GameEntityBehavior<RequiredState extends object =
52
74
  */
53
75
  $setEntity(entity: SimpleGameEntity<RequiredState> | null): void;
54
76
  abstract processEvent(event: GameEvent, state: RequiredState): Promise<boolean> | boolean;
77
+ /**
78
+ * Handles a direct request sent to this entity. Return a value to respond,
79
+ * or `undefined` to pass the request to the next behavior.
80
+ *
81
+ * @param event - The request event (type, payload, senderID, targetID)
82
+ * @param state - Current entity state
83
+ */
84
+ processRequest?(event: GameEvent, state: RequiredState): Promise<unknown> | unknown | undefined;
55
85
  update?(dt: number, state: RequiredState): void;
56
86
  destroy?(): Promise<void>;
57
87
  /**
@@ -103,6 +133,7 @@ export declare class GameEntity<EntityState extends object = object> implements
103
133
  behaviors: GameEntityBehavior[];
104
134
  eventBus: GameEventBus;
105
135
  private _gameEngine;
136
+ private _entityManager;
106
137
  private _tags;
107
138
  private subscriptions;
108
139
  /**
@@ -169,6 +200,47 @@ export declare class GameEntity<EntityState extends object = object> implements
169
200
  * @param child
170
201
  */
171
202
  $removeChild(child: GameEntity): void;
203
+ /**
204
+ * Sets the entity manager reference for cross-entity messaging.
205
+ * Called by the EntityManager after entity creation.
206
+ * @param manager
207
+ * @internal
208
+ */
209
+ $setEntityManager(manager: EntityMessenger): void;
210
+ /**
211
+ * Sends a fire-and-forget event directly to a specific entity's behaviors, bypassing the event bus.
212
+ * If the target matches this entity's own ID, dispatches locally.
213
+ *
214
+ * @param target - Entity ID or name
215
+ * @param type - Event type string
216
+ * @param payload - Optional event payload
217
+ */
218
+ send(target: string, type: string, payload?: unknown): Promise<void>;
219
+ /**
220
+ * Sends a request to a specific entity's behaviors and returns the first response.
221
+ * If the target matches this entity's own ID, dispatches locally.
222
+ *
223
+ * @param target - Entity ID or name
224
+ * @param type - Request type string
225
+ * @param payload - Optional request payload
226
+ */
227
+ request<T>(target: string, type: string, payload?: unknown): Promise<RequestResult<T>>;
228
+ /**
229
+ * Sends a fire-and-forget event directly to this entity's own behaviors.
230
+ *
231
+ * @param type - Event type string
232
+ * @param payload - Optional event payload
233
+ * @internal
234
+ */
235
+ $send(type: string, payload?: unknown, senderID?: string): Promise<void>;
236
+ /**
237
+ * Sends a request to this entity's own behaviors and returns the first response.
238
+ *
239
+ * @param type - Request type string
240
+ * @param payload - Optional request payload
241
+ * @internal
242
+ */
243
+ $request<T>(type: string, payload?: unknown, senderID?: string): Promise<RequestResult<T>>;
172
244
  /**
173
245
  * Resets the entity for object pool recycling.
174
246
  * Restores state and tags to defaults, detaches from node, notifies behaviors, and resets children.
@@ -1,5 +1,5 @@
1
1
  import { AbstractMesh } from '@babylonjs/core';
2
- import { InputDevice, InputState } from "../interfaces/input.d.ts";
2
+ import { DeviceType, InputDevice, InputState } from "../interfaces/input.d.ts";
3
3
  /** Payload for input:device:connected events */
4
4
  export interface DeviceConnectedPayload {
5
5
  device: InputDevice;
@@ -14,6 +14,11 @@ export interface InputChangedPayload {
14
14
  device: InputDevice;
15
15
  state: InputState;
16
16
  }
17
+ /** Payload for input:device-type:changed events */
18
+ export interface DeviceTypeChangedPayload {
19
+ deviceType: DeviceType;
20
+ previousDeviceType: DeviceType | null;
21
+ }
17
22
  /** Payload for level:progress events */
18
23
  export interface LevelProgressPayload {
19
24
  levelName: string;
@@ -111,6 +116,7 @@ export interface LibraryEventPayloadMap {
111
116
  'input:device:connected': DeviceConnectedPayload;
112
117
  'input:device:disconnected': DeviceDisconnectedPayload;
113
118
  'input:changed': InputChangedPayload;
119
+ 'input:device-type:changed': DeviceTypeChangedPayload;
114
120
  'level:progress': LevelProgressPayload;
115
121
  'level:complete': LevelCompletePayload;
116
122
  'level:error': LevelErrorPayload;
@@ -1,5 +1,5 @@
1
1
  import { GameEventBus } from "../classes/eventBus.d.ts";
2
- import { DeviceValueReader, DeviceValueReaderDefinition, InputState } from "./input.d.ts";
2
+ import { DeviceType, DeviceValueReader, DeviceValueReaderDefinition, InputState } from "./input.d.ts";
3
3
  import { Action } from "./action.d.ts";
4
4
  /**
5
5
  * Types of bindings supported by the system
@@ -88,6 +88,7 @@ export interface Binding {
88
88
  readonly action: Action;
89
89
  readonly context?: string;
90
90
  readonly deviceID: string;
91
+ readonly deviceType: DeviceType;
91
92
  readonly reader: DeviceValueReader;
92
93
  /**
93
94
  * Process input state and potentially emit an action event
@@ -1,7 +1,7 @@
1
1
  import { GameEventBus } from "../classes/eventBus.d.ts";
2
2
  import { Binding, BindingDefinition, Context } from "../interfaces/binding.d.ts";
3
3
  import { Disposable } from "../interfaces/lifecycle.d.ts";
4
- import { InputDevice, InputState } from "../interfaces/input.d.ts";
4
+ import { DeviceType, InputDevice, InputState } from "../interfaces/input.d.ts";
5
5
  import { Action } from "../interfaces/action.d.ts";
6
6
  import { LoggingUtility } from "../utils/logger.d.ts";
7
7
  /**
@@ -33,6 +33,8 @@ export declare class BindingManager implements Disposable {
33
33
  * Set of all active contexts (both exclusive and non-exclusive)
34
34
  */
35
35
  private _activeContexts;
36
+ /** The most recent device type that produced intentional input */
37
+ private _lastActiveDeviceType;
36
38
  /** Event bus for handling game events */
37
39
  private _eventBus;
38
40
  /** Unsubscribe function for input events */
@@ -68,6 +70,13 @@ export declare class BindingManager implements Disposable {
68
70
  * @param exceptContextName
69
71
  */
70
72
  private _deactivateExclusiveContexts;
73
+ /**
74
+ * Determines whether an input event represents intentional activity that should
75
+ * update the last-active device type. Filters out noise like idle mousemove.
76
+ *
77
+ * @param state
78
+ */
79
+ private _shouldUpdateActiveDevice;
71
80
  /**
72
81
  * Create a binding from a binding definition.
73
82
  *
@@ -108,6 +117,11 @@ export declare class BindingManager implements Disposable {
108
117
  * @param actionName
109
118
  */
110
119
  getAction(actionName: string): Action | null;
120
+ /**
121
+ * Returns the device type that most recently produced intentional input,
122
+ * or `null` if no input has been received yet.
123
+ */
124
+ get lastActiveDeviceType(): DeviceType | null;
111
125
  /**
112
126
  * Registers a context with specific options.
113
127
  * Updates exclusivity if the context already exists.
@@ -168,12 +182,13 @@ export declare class BindingManager implements Disposable {
168
182
  */
169
183
  unregisterBindings(actionName: string, context?: string | null): void;
170
184
  /**
171
- * Gets all bindings for a specific action.
185
+ * Gets all bindings for a specific action, optionally filtered by device type and/or context.
172
186
  *
173
187
  * @param actionName
188
+ * @param deviceType - If provided, filters to only bindings for that device type
174
189
  * @param context - If provided, filters to only bindings in that context
175
190
  */
176
- getBindingsForAction(actionName: string, context?: string | null): Binding[];
191
+ getBindingsForAction(actionName: string, deviceType?: DeviceType, context?: string | null): Binding[];
177
192
  /**
178
193
  * Exports the current configuration to a serializable object.
179
194
  * Includes all actions, bindings, contexts, and their active states.
@@ -1,5 +1,5 @@
1
1
  import { TransformNode } from '@babylonjs/core';
2
- import { GameEntity } from "../classes/entity.d.ts";
2
+ import { GameEntity, RequestResult } from "../classes/entity.d.ts";
3
3
  import { GameEventBus } from "../classes/eventBus.d.ts";
4
4
  import { GameEngine } from "../classes/gameEngine.d.ts";
5
5
  import { GameEntityDefinition } from "../interfaces/entity.d.ts";
@@ -174,6 +174,25 @@ export declare class GameEntityManager implements Disposable {
174
174
  * Gets the total number of entities.
175
175
  */
176
176
  get entityCount(): number;
177
+ /**
178
+ * Sends a fire-and-forget event directly to a specific entity's behaviors, bypassing the event bus.
179
+ * Resolves the target by entity ID first (O(1)), then falls back to name lookup (O(1)).
180
+ *
181
+ * @param target - Entity ID or name
182
+ * @param type - Event type string
183
+ * @param payload - Optional event payload
184
+ */
185
+ send(target: string, type: string, payload?: unknown, senderID?: string): Promise<void>;
186
+ /**
187
+ * Sends a request to a specific entity's behaviors and returns the first response.
188
+ * Resolves the target by entity ID first (O(1)), then falls back to name lookup (O(1)).
189
+ *
190
+ * @param target - Entity ID or name
191
+ * @param type - Request type string
192
+ * @param payload - Optional request payload
193
+ * @param senderID - Optional sender entity ID (stamped on the event)
194
+ */
195
+ request<T>(target: string, type: string, payload?: unknown, senderID?: string): Promise<RequestResult<T>>;
177
196
  /**
178
197
  * Adds a tag to an entity and updates the tag index.
179
198
  * @param entity - Entity instance or entity ID