@sharpee/engine 0.9.113 → 1.0.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 (140) hide show
  1. package/action-context-factory.d.ts +10 -2
  2. package/action-context-factory.d.ts.map +1 -1
  3. package/action-context-factory.js +29 -5
  4. package/action-context-factory.js.map +1 -1
  5. package/command-executor.d.ts +2 -1
  6. package/command-executor.d.ts.map +1 -1
  7. package/command-executor.js +3 -3
  8. package/command-executor.js.map +1 -1
  9. package/game-engine.d.ts +87 -4
  10. package/game-engine.d.ts.map +1 -1
  11. package/game-engine.js +239 -8
  12. package/game-engine.js.map +1 -1
  13. package/index.d.ts +1 -0
  14. package/index.d.ts.map +1 -1
  15. package/index.js +2 -0
  16. package/index.js.map +1 -1
  17. package/package.json +12 -12
  18. package/prose-pipeline/assemble.d.ts +77 -0
  19. package/prose-pipeline/assemble.d.ts.map +1 -0
  20. package/prose-pipeline/assemble.js +96 -0
  21. package/prose-pipeline/assemble.js.map +1 -0
  22. package/prose-pipeline/decorations/index.d.ts +18 -0
  23. package/prose-pipeline/decorations/index.d.ts.map +1 -0
  24. package/prose-pipeline/decorations/index.js +24 -0
  25. package/prose-pipeline/decorations/index.js.map +1 -0
  26. package/prose-pipeline/decorations/parser.d.ts +34 -0
  27. package/prose-pipeline/decorations/parser.d.ts.map +1 -0
  28. package/prose-pipeline/decorations/parser.js +177 -0
  29. package/prose-pipeline/decorations/parser.js.map +1 -0
  30. package/prose-pipeline/decorations/platform-vocabulary.d.ts +27 -0
  31. package/prose-pipeline/decorations/platform-vocabulary.d.ts.map +1 -0
  32. package/prose-pipeline/decorations/platform-vocabulary.js +65 -0
  33. package/prose-pipeline/decorations/platform-vocabulary.js.map +1 -0
  34. package/prose-pipeline/decorations/resolver.d.ts +28 -0
  35. package/prose-pipeline/decorations/resolver.d.ts.map +1 -0
  36. package/prose-pipeline/decorations/resolver.js +39 -0
  37. package/prose-pipeline/decorations/resolver.js.map +1 -0
  38. package/prose-pipeline/decorations/types.d.ts +16 -0
  39. package/prose-pipeline/decorations/types.d.ts.map +1 -0
  40. package/prose-pipeline/decorations/types.js +17 -0
  41. package/prose-pipeline/decorations/types.js.map +1 -0
  42. package/prose-pipeline/handlers/about.d.ts +31 -0
  43. package/prose-pipeline/handlers/about.d.ts.map +1 -0
  44. package/prose-pipeline/handlers/about.js +50 -0
  45. package/prose-pipeline/handlers/about.js.map +1 -0
  46. package/prose-pipeline/handlers/audibility.d.ts +46 -0
  47. package/prose-pipeline/handlers/audibility.d.ts.map +1 -0
  48. package/prose-pipeline/handlers/audibility.js +79 -0
  49. package/prose-pipeline/handlers/audibility.js.map +1 -0
  50. package/prose-pipeline/handlers/banner.d.ts +44 -0
  51. package/prose-pipeline/handlers/banner.d.ts.map +1 -0
  52. package/prose-pipeline/handlers/banner.js +88 -0
  53. package/prose-pipeline/handlers/banner.js.map +1 -0
  54. package/prose-pipeline/handlers/client-query.d.ts +22 -0
  55. package/prose-pipeline/handlers/client-query.d.ts.map +1 -0
  56. package/prose-pipeline/handlers/client-query.js +55 -0
  57. package/prose-pipeline/handlers/client-query.js.map +1 -0
  58. package/prose-pipeline/handlers/command-failed.d.ts +24 -0
  59. package/prose-pipeline/handlers/command-failed.d.ts.map +1 -0
  60. package/prose-pipeline/handlers/command-failed.js +46 -0
  61. package/prose-pipeline/handlers/command-failed.js.map +1 -0
  62. package/prose-pipeline/handlers/domain-message.d.ts +31 -0
  63. package/prose-pipeline/handlers/domain-message.d.ts.map +1 -0
  64. package/prose-pipeline/handlers/domain-message.js +66 -0
  65. package/prose-pipeline/handlers/domain-message.js.map +1 -0
  66. package/prose-pipeline/handlers/game.d.ts +31 -0
  67. package/prose-pipeline/handlers/game.d.ts.map +1 -0
  68. package/prose-pipeline/handlers/game.js +35 -0
  69. package/prose-pipeline/handlers/game.js.map +1 -0
  70. package/prose-pipeline/handlers/generic.d.ts +27 -0
  71. package/prose-pipeline/handlers/generic.d.ts.map +1 -0
  72. package/prose-pipeline/handlers/generic.js +67 -0
  73. package/prose-pipeline/handlers/generic.js.map +1 -0
  74. package/prose-pipeline/handlers/help.d.ts +21 -0
  75. package/prose-pipeline/handlers/help.d.ts.map +1 -0
  76. package/prose-pipeline/handlers/help.js +68 -0
  77. package/prose-pipeline/handlers/help.js.map +1 -0
  78. package/prose-pipeline/handlers/implicit-take.d.ts +20 -0
  79. package/prose-pipeline/handlers/implicit-take.d.ts.map +1 -0
  80. package/prose-pipeline/handlers/implicit-take.js +26 -0
  81. package/prose-pipeline/handlers/implicit-take.js.map +1 -0
  82. package/prose-pipeline/handlers/index.d.ts +24 -0
  83. package/prose-pipeline/handlers/index.d.ts.map +1 -0
  84. package/prose-pipeline/handlers/index.js +40 -0
  85. package/prose-pipeline/handlers/index.js.map +1 -0
  86. package/prose-pipeline/handlers/revealed.d.ts +24 -0
  87. package/prose-pipeline/handlers/revealed.d.ts.map +1 -0
  88. package/prose-pipeline/handlers/revealed.js +54 -0
  89. package/prose-pipeline/handlers/revealed.js.map +1 -0
  90. package/prose-pipeline/handlers/room.d.ts +25 -0
  91. package/prose-pipeline/handlers/room.d.ts.map +1 -0
  92. package/prose-pipeline/handlers/room.js +78 -0
  93. package/prose-pipeline/handlers/room.js.map +1 -0
  94. package/prose-pipeline/handlers/types.d.ts +47 -0
  95. package/prose-pipeline/handlers/types.d.ts.map +1 -0
  96. package/prose-pipeline/handlers/types.js +15 -0
  97. package/prose-pipeline/handlers/types.js.map +1 -0
  98. package/prose-pipeline/index.d.ts +20 -0
  99. package/prose-pipeline/index.d.ts.map +1 -0
  100. package/prose-pipeline/index.js +50 -0
  101. package/prose-pipeline/index.js.map +1 -0
  102. package/prose-pipeline/pipeline.d.ts +58 -0
  103. package/prose-pipeline/pipeline.d.ts.map +1 -0
  104. package/prose-pipeline/pipeline.js +120 -0
  105. package/prose-pipeline/pipeline.js.map +1 -0
  106. package/prose-pipeline/stages/filter.d.ts +26 -0
  107. package/prose-pipeline/stages/filter.d.ts.map +1 -0
  108. package/prose-pipeline/stages/filter.js +38 -0
  109. package/prose-pipeline/stages/filter.js.map +1 -0
  110. package/prose-pipeline/stages/index.d.ts +13 -0
  111. package/prose-pipeline/stages/index.d.ts.map +1 -0
  112. package/prose-pipeline/stages/index.js +19 -0
  113. package/prose-pipeline/stages/index.js.map +1 -0
  114. package/prose-pipeline/stages/sort.d.ts +38 -0
  115. package/prose-pipeline/stages/sort.d.ts.map +1 -0
  116. package/prose-pipeline/stages/sort.js +94 -0
  117. package/prose-pipeline/stages/sort.js.map +1 -0
  118. package/prose-pipeline/types.d.ts +45 -0
  119. package/prose-pipeline/types.d.ts.map +1 -0
  120. package/prose-pipeline/types.js +23 -0
  121. package/prose-pipeline/types.js.map +1 -0
  122. package/sound/dispatcher.d.ts +75 -0
  123. package/sound/dispatcher.d.ts.map +1 -0
  124. package/sound/dispatcher.js +122 -0
  125. package/sound/dispatcher.js.map +1 -0
  126. package/sound/index.d.ts +26 -0
  127. package/sound/index.d.ts.map +1 -0
  128. package/sound/index.js +34 -0
  129. package/sound/index.js.map +1 -0
  130. package/sound/propagation.d.ts +69 -0
  131. package/sound/propagation.d.ts.map +1 -0
  132. package/sound/propagation.js +219 -0
  133. package/sound/propagation.js.map +1 -0
  134. package/story.d.ts +28 -1
  135. package/story.d.ts.map +1 -1
  136. package/story.js.map +1 -1
  137. package/test-helpers/mock-prose-pipeline.d.ts +22 -0
  138. package/test-helpers/mock-prose-pipeline.d.ts.map +1 -0
  139. package/test-helpers/mock-prose-pipeline.js +56 -0
  140. package/test-helpers/mock-prose-pipeline.js.map +1 -0
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ /**
3
+ * Event sorting stage — orders events within a turn for correct prose
4
+ * sequence: lifecycle first, then per-transaction implicit-take →
5
+ * room-description → action.* → others, finally by chain depth.
6
+ *
7
+ * Public interface: `sortEventsForProse`, `getChainMetadata`. Used
8
+ * internally by the prose pipeline as the second per-turn stage,
9
+ * after filtering.
10
+ *
11
+ * Owner context: `@sharpee/engine` — internal prose pipeline.
12
+ *
13
+ * @see ADR-094 Event Chaining (preserved; the sort responsibility
14
+ * ports from `@sharpee/text-service`)
15
+ * @see ADR-174 §Engine-internal prose pipeline
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.sortEventsForProse = sortEventsForProse;
19
+ exports.getChainMetadata = getChainMetadata;
20
+ const LIFECYCLE_EVENTS = new Set([
21
+ 'game.started',
22
+ 'game.starting',
23
+ 'game.loading',
24
+ 'game.loaded',
25
+ 'game.initialized',
26
+ ]);
27
+ /**
28
+ * Sort events for correct prose order within transactions.
29
+ *
30
+ * Stable sort — preserves cross-transaction order while applying
31
+ * within-transaction rules.
32
+ */
33
+ function sortEventsForProse(events) {
34
+ return [...events].sort((a, b) => {
35
+ const aData = a.data;
36
+ const bData = b.data;
37
+ const aTxnId = aData?._transactionId;
38
+ const bTxnId = bData?._transactionId;
39
+ // Game lifecycle events come first, before everything.
40
+ // Ensures the banner displays before the first room description.
41
+ // Note: matches specific lifecycle events only, NOT game.message.
42
+ const aIsGameLifecycle = LIFECYCLE_EVENTS.has(a.type);
43
+ const bIsGameLifecycle = LIFECYCLE_EVENTS.has(b.type);
44
+ if (aIsGameLifecycle && !bIsGameLifecycle)
45
+ return -1;
46
+ if (!aIsGameLifecycle && bIsGameLifecycle)
47
+ return 1;
48
+ // Different transactions or no transaction: maintain original order.
49
+ if (aTxnId !== bTxnId)
50
+ return 0;
51
+ // Implicit take events come first, before the main action result:
52
+ // "first taking the X" before "X reads:..."
53
+ const aIsImplicitTake = a.type === 'if.event.implicit_take';
54
+ const bIsImplicitTake = b.type === 'if.event.implicit_take';
55
+ if (aIsImplicitTake && !bIsImplicitTake)
56
+ return -1;
57
+ if (!aIsImplicitTake && bIsImplicitTake)
58
+ return 1;
59
+ // Room description before action.success (for contents list):
60
+ // "Room Name\nDescription" before "You see X here."
61
+ const aIsRoomDesc = a.type === 'if.event.room.description' ||
62
+ a.type === 'if.event.room_description';
63
+ const bIsRoomDesc = b.type === 'if.event.room.description' ||
64
+ b.type === 'if.event.room_description';
65
+ if (aIsRoomDesc && !bIsRoomDesc)
66
+ return -1;
67
+ if (!aIsRoomDesc && bIsRoomDesc)
68
+ return 1;
69
+ // Within transaction: action.* first (after room description).
70
+ const aIsAction = a.type.startsWith('action.');
71
+ const bIsAction = b.type.startsWith('action.');
72
+ if (aIsAction && !bIsAction)
73
+ return -1;
74
+ if (!aIsAction && bIsAction)
75
+ return 1;
76
+ // Then by chain depth (lower depth first).
77
+ const aDepth = aData?._chainDepth ?? 0;
78
+ const bDepth = bData?._chainDepth ?? 0;
79
+ return aDepth - bDepth;
80
+ });
81
+ }
82
+ /**
83
+ * Extract chain metadata from event data.
84
+ */
85
+ function getChainMetadata(event) {
86
+ const data = event.data;
87
+ return {
88
+ _transactionId: data?._transactionId,
89
+ _chainDepth: data?._chainDepth,
90
+ _chainedFrom: data?._chainedFrom,
91
+ _chainSourceId: data?._chainSourceId,
92
+ };
93
+ }
94
+ //# sourceMappingURL=sort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sort.js","sourceRoot":"","sources":["../../../../../../../../repos/sharpee/packages/engine/src/prose-pipeline/stages/sort.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Prose pipeline service interface — engine-internal home for the
3
+ * `ITextService` contract.
4
+ *
5
+ * Per ADR-174, the responsibility for translating events into blocks
6
+ * moves into `@sharpee/engine`. The transitional `ITextService` name
7
+ * is preserved here so engine code already typed against it (the
8
+ * three `processTurn` call sites in `game-engine.ts` and the
9
+ * `getTextService` / `setTextService` accessors) can keep compiling
10
+ * during sub-phase 1.6's import swap. A follow-up cleanup may rename
11
+ * this to `IProsePipeline` once the legacy alias is no longer
12
+ * imported anywhere.
13
+ *
14
+ * Public interface: `ITextService`. Implemented by `ProsePipeline`
15
+ * (and by `MockProsePipeline` in tests).
16
+ *
17
+ * Owner context: `@sharpee/engine` — internal prose pipeline.
18
+ *
19
+ * @see ADR-174 §Internal interfaces
20
+ */
21
+ import type { ISemanticEvent } from "../../core/index";
22
+ import type { ITextBlock } from "../../text-blocks/index";
23
+ /**
24
+ * Per-turn prose translator.
25
+ *
26
+ * Stateless transformer: takes the events emitted during a turn,
27
+ * returns the structured `ITextBlock[]` the channel layer hands off
28
+ * to renderers. Engine constructs an implementation once during
29
+ * `setStory()` and calls `processTurn` per turn (and per
30
+ * meta-command / restart).
31
+ */
32
+ export interface ITextService {
33
+ /**
34
+ * Process turn events and produce TextBlocks.
35
+ *
36
+ * Called by Engine after each turn completes — and again on the
37
+ * meta-command path (restart, restore) where the same per-turn
38
+ * shape applies.
39
+ *
40
+ * @param events All events from this turn, including chained ones.
41
+ * @returns Blocks in render order.
42
+ */
43
+ processTurn(events: ISemanticEvent[]): ITextBlock[];
44
+ }
45
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/engine/src/prose-pipeline/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;OASG;IACH,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,UAAU,EAAE,CAAC;CACrD"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ /**
3
+ * Prose pipeline service interface — engine-internal home for the
4
+ * `ITextService` contract.
5
+ *
6
+ * Per ADR-174, the responsibility for translating events into blocks
7
+ * moves into `@sharpee/engine`. The transitional `ITextService` name
8
+ * is preserved here so engine code already typed against it (the
9
+ * three `processTurn` call sites in `game-engine.ts` and the
10
+ * `getTextService` / `setTextService` accessors) can keep compiling
11
+ * during sub-phase 1.6's import swap. A follow-up cleanup may rename
12
+ * this to `IProsePipeline` once the legacy alias is no longer
13
+ * imported anywhere.
14
+ *
15
+ * Public interface: `ITextService`. Implemented by `ProsePipeline`
16
+ * (and by `MockProsePipeline` in tests).
17
+ *
18
+ * Owner context: `@sharpee/engine` — internal prose pipeline.
19
+ *
20
+ * @see ADR-174 §Internal interfaces
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/engine/src/prose-pipeline/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Per-turn audibility dispatcher (ADR-172 Phase 6).
3
+ *
4
+ * Closes the loop between `emitSound` (authoring API; Step 6.1) and the
5
+ * `audibility` channel (Phase 5). For each `ISound` buffered during a
6
+ * turn, the dispatcher walks every entity carrying `ListenerTrait`,
7
+ * calls `propagate(sound, listenerId, world, timestamp)`, and emits one
8
+ * `sound.audibility.heard` `ISemanticEvent` per (sound × listener) pair
9
+ * that produced a non-null `IAudibilityEvent`.
10
+ *
11
+ * The dispatcher is pure: same buffer + same world state + same
12
+ * timestamp → same event array. Listeners are processed in entity-id
13
+ * sort order for deterministic event ordering across turns and runs.
14
+ *
15
+ * Owner context: `@sharpee/engine` — runtime / sound subsystem.
16
+ *
17
+ * Public interface:
18
+ * - `class SoundDispatcher` — the dispatcher itself.
19
+ * - `SoundDispatcher.dispatch(buffer, world, timestamp)` — produces
20
+ * the `sound.audibility.heard` events for the turn.
21
+ *
22
+ * @see ADR-172 — Spatial Sound Propagation
23
+ * @see ADR-163 — Channel-Service Platform (audibility channel)
24
+ */
25
+ import { type ISemanticEvent } from "../../core/index";
26
+ import type { IAudibilityEvent, ISound } from "../../if-domain/index";
27
+ import { type WorldModel } from "../../world-model/index";
28
+ /**
29
+ * Semantic-event type fired by the dispatcher when a listener perceives
30
+ * a propagated sound. Mirrors `SOUND_EVENT_TYPES.AUDIBILITY_HEARD` in
31
+ * `@sharpee/stdlib/channels/sound-events`. The constant is duplicated
32
+ * here as a string literal so the engine package does not depend on
33
+ * stdlib at compile time (engine → stdlib is the existing dependency
34
+ * direction; the inverse would be a cycle). The string value is the
35
+ * contract — both sides must agree.
36
+ */
37
+ export declare const AUDIBILITY_HEARD_EVENT_TYPE = "sound.audibility.heard";
38
+ /**
39
+ * Per-turn audibility dispatcher.
40
+ *
41
+ * The class shape (rather than a free function) leaves room for a
42
+ * future propagate-injection point in tests and for caching listener
43
+ * lookups across multi-action turns. For Phase 6 the dispatcher is a
44
+ * thin wrapper around `propagate()`; the class structure is the
45
+ * extension seam for L2's "NPC voice profile" layer.
46
+ */
47
+ export declare class SoundDispatcher {
48
+ /**
49
+ * The propagation function the dispatcher uses. Defaulted to the
50
+ * production `propagate` from `./propagation`; tests may inject a
51
+ * fake to isolate dispatcher behavior from edge-graph + Dijkstra
52
+ * complexity.
53
+ */
54
+ private readonly propagate;
55
+ constructor(propagateFn?: (sound: ISound, listenerId: string, world: WorldModel, timestamp: number) => IAudibilityEvent | null);
56
+ /**
57
+ * Dispatch every buffered sound to every listener.
58
+ *
59
+ * @param buffer The per-turn sound buffer; iterated in insertion
60
+ * order. May be empty (quiet turn).
61
+ * @param world The world model the propagation function reads
62
+ * from. Must be the same instance the actions
63
+ * mutated during the turn.
64
+ * @param timestamp The turn-sequence integer the engine assigns to
65
+ * this turn. Used as the `IAudibilityEvent.timestamp`
66
+ * for ordering across multi-emission turns.
67
+ * @returns Array of `sound.audibility.heard` events, one per
68
+ * (sound × listener) where `propagate()` returned
69
+ * non-null. Order: outer iteration over the buffer in
70
+ * emission order, inner iteration over listeners
71
+ * sorted by entity id ascending.
72
+ */
73
+ dispatch(buffer: readonly ISound[], world: WorldModel, timestamp: number): ISemanticEvent[];
74
+ }
75
+ //# sourceMappingURL=dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/engine/src/sound/dispatcher.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGjF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AAEpE;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAKG;gBAG3B,WAAW,GAAE,CACX,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,KACd,gBAAgB,GAAG,IAAuB;IAKjD;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CACN,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,GAChB,cAAc,EAAE;CAsBpB"}
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ // packages/engine/src/sound/dispatcher.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.SoundDispatcher = exports.AUDIBILITY_HEARD_EVENT_TYPE = void 0;
5
+ /**
6
+ * Per-turn audibility dispatcher (ADR-172 Phase 6).
7
+ *
8
+ * Closes the loop between `emitSound` (authoring API; Step 6.1) and the
9
+ * `audibility` channel (Phase 5). For each `ISound` buffered during a
10
+ * turn, the dispatcher walks every entity carrying `ListenerTrait`,
11
+ * calls `propagate(sound, listenerId, world, timestamp)`, and emits one
12
+ * `sound.audibility.heard` `ISemanticEvent` per (sound × listener) pair
13
+ * that produced a non-null `IAudibilityEvent`.
14
+ *
15
+ * The dispatcher is pure: same buffer + same world state + same
16
+ * timestamp → same event array. Listeners are processed in entity-id
17
+ * sort order for deterministic event ordering across turns and runs.
18
+ *
19
+ * Owner context: `@sharpee/engine` — runtime / sound subsystem.
20
+ *
21
+ * Public interface:
22
+ * - `class SoundDispatcher` — the dispatcher itself.
23
+ * - `SoundDispatcher.dispatch(buffer, world, timestamp)` — produces
24
+ * the `sound.audibility.heard` events for the turn.
25
+ *
26
+ * @see ADR-172 — Spatial Sound Propagation
27
+ * @see ADR-163 — Channel-Service Platform (audibility channel)
28
+ */
29
+ const core_1 = require("../../core/index.js");
30
+ const world_model_1 = require("../../world-model/index.js");
31
+ const propagation_1 = require("./propagation");
32
+ /**
33
+ * Semantic-event type fired by the dispatcher when a listener perceives
34
+ * a propagated sound. Mirrors `SOUND_EVENT_TYPES.AUDIBILITY_HEARD` in
35
+ * `@sharpee/stdlib/channels/sound-events`. The constant is duplicated
36
+ * here as a string literal so the engine package does not depend on
37
+ * stdlib at compile time (engine → stdlib is the existing dependency
38
+ * direction; the inverse would be a cycle). The string value is the
39
+ * contract — both sides must agree.
40
+ */
41
+ exports.AUDIBILITY_HEARD_EVENT_TYPE = 'sound.audibility.heard';
42
+ /**
43
+ * Per-turn audibility dispatcher.
44
+ *
45
+ * The class shape (rather than a free function) leaves room for a
46
+ * future propagate-injection point in tests and for caching listener
47
+ * lookups across multi-action turns. For Phase 6 the dispatcher is a
48
+ * thin wrapper around `propagate()`; the class structure is the
49
+ * extension seam for L2's "NPC voice profile" layer.
50
+ */
51
+ class SoundDispatcher {
52
+ /**
53
+ * The propagation function the dispatcher uses. Defaulted to the
54
+ * production `propagate` from `./propagation`; tests may inject a
55
+ * fake to isolate dispatcher behavior from edge-graph + Dijkstra
56
+ * complexity.
57
+ */
58
+ propagate;
59
+ constructor(propagateFn = propagation_1.propagate) {
60
+ this.propagate = propagateFn;
61
+ }
62
+ /**
63
+ * Dispatch every buffered sound to every listener.
64
+ *
65
+ * @param buffer The per-turn sound buffer; iterated in insertion
66
+ * order. May be empty (quiet turn).
67
+ * @param world The world model the propagation function reads
68
+ * from. Must be the same instance the actions
69
+ * mutated during the turn.
70
+ * @param timestamp The turn-sequence integer the engine assigns to
71
+ * this turn. Used as the `IAudibilityEvent.timestamp`
72
+ * for ordering across multi-emission turns.
73
+ * @returns Array of `sound.audibility.heard` events, one per
74
+ * (sound × listener) where `propagate()` returned
75
+ * non-null. Order: outer iteration over the buffer in
76
+ * emission order, inner iteration over listeners
77
+ * sorted by entity id ascending.
78
+ */
79
+ dispatch(buffer, world, timestamp) {
80
+ if (buffer.length === 0)
81
+ return [];
82
+ const listeners = world
83
+ .findByTrait(world_model_1.TraitType.LISTENER)
84
+ .slice()
85
+ .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
86
+ if (listeners.length === 0)
87
+ return [];
88
+ const events = [];
89
+ for (const sound of buffer) {
90
+ for (const listener of listeners) {
91
+ const audibility = this.propagate(sound, listener.id, world, timestamp);
92
+ if (!audibility)
93
+ continue;
94
+ events.push(buildAudibilityEvent(audibility, listener.id));
95
+ }
96
+ }
97
+ return events;
98
+ }
99
+ }
100
+ exports.SoundDispatcher = SoundDispatcher;
101
+ /**
102
+ * Build the `sound.audibility.heard` semantic event from a propagation
103
+ * result and the listener that perceived it.
104
+ *
105
+ * `entities` mapping:
106
+ * - `actor` → `sourceEntityId` (who emitted the sound)
107
+ * - `location` → `sourceRoomId` (where the sound came from)
108
+ * - `target` → listenerId (who heard it)
109
+ *
110
+ * The `audibility` channel projection (ADR-163, ADR-172 Phase 5) reads
111
+ * the `IAudibilityEvent` from `event.data`; `entities` is provided for
112
+ * downstream filters and handlers that want a fast "who heard / from
113
+ * where" lookup without re-reading `data`.
114
+ */
115
+ function buildAudibilityEvent(audibility, listenerId) {
116
+ return (0, core_1.createEvent)(exports.AUDIBILITY_HEARD_EVENT_TYPE, audibility, {
117
+ actor: audibility.sourceEntityId,
118
+ location: audibility.sourceRoomId,
119
+ target: listenerId,
120
+ });
121
+ }
122
+ //# sourceMappingURL=dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/engine/src/sound/dispatcher.ts"],"names":[],"mappings":";AAAA,0CAA0C;;;AAE1C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,wCAAiE;AAEjE,sDAAiF;AACjF,+CAA8D;AAE9D;;;;;;;;GAQG;AACU,QAAA,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAa,eAAe;IAC1B;;;;;OAKG;IACc,SAAS,CAKG;IAE7B,YACE,cAK+B,uBAAgB;QAE/C,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CACN,MAAyB,EACzB,KAAiB,EACjB,SAAiB;QAEjB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEnC,MAAM,SAAS,GAAG,KAAK;aACpB,WAAW,CAAC,uBAAS,CAAC,QAAQ,CAAC;aAC/B,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,CAAW,EAAE,CAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEtC,MAAM,MAAM,GAAqB,EAAE,CAAC;QAEpC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;gBACxE,IAAI,CAAC,UAAU;oBAAE,SAAS;gBAC1B,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AApED,0CAoEC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,oBAAoB,CAC3B,UAA4B,EAC5B,UAAkB;IAElB,OAAO,IAAA,kBAAW,EAChB,mCAA2B,EAC3B,UAAgD,EAChD;QACE,KAAK,EAAE,UAAU,CAAC,cAAc;QAChC,QAAQ,EAAE,UAAU,CAAC,YAAY;QACjC,MAAM,EAAE,UAAU;KACnB,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @sharpee/engine/sound — spatial sound propagation runtime (ADR-172).
3
+ *
4
+ * Owner context: engine layer. The propagation function lives here
5
+ * because it consumes world-model state, doesn't fit in a pure
6
+ * domain-types layer, and is invoked from the action pipeline (Phase
7
+ * 6) which is engine-adjacent.
8
+ *
9
+ * Public interface:
10
+ * - `propagate(sound, listenerId, world, timestamp)` — produce an
11
+ * `AudibilityEvent` (or null) for a single listener.
12
+ * - `clarityToTier(clarity)` — exposed for testability and for
13
+ * composition layers that need to query "what tier does this
14
+ * clarity value map to?".
15
+ * - `SoundDispatcher` — per-turn fan-out: for each buffered sound,
16
+ * propagate to every `ListenerTrait` entity and emit one
17
+ * `sound.audibility.heard` semantic event per non-silent result
18
+ * (Phase 6 Step 6.2).
19
+ * - `AUDIBILITY_HEARD_EVENT_TYPE` — the semantic-event type string
20
+ * fired by the dispatcher.
21
+ *
22
+ * @see ADR-172 — Spatial Sound Propagation
23
+ */
24
+ export { propagate, clarityToTier } from './propagation';
25
+ export { SoundDispatcher, AUDIBILITY_HEARD_EVENT_TYPE } from './dispatcher';
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/engine/src/sound/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
package/sound/index.js ADDED
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ // packages/engine/src/sound/index.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.AUDIBILITY_HEARD_EVENT_TYPE = exports.SoundDispatcher = exports.clarityToTier = exports.propagate = void 0;
5
+ /**
6
+ * @sharpee/engine/sound — spatial sound propagation runtime (ADR-172).
7
+ *
8
+ * Owner context: engine layer. The propagation function lives here
9
+ * because it consumes world-model state, doesn't fit in a pure
10
+ * domain-types layer, and is invoked from the action pipeline (Phase
11
+ * 6) which is engine-adjacent.
12
+ *
13
+ * Public interface:
14
+ * - `propagate(sound, listenerId, world, timestamp)` — produce an
15
+ * `AudibilityEvent` (or null) for a single listener.
16
+ * - `clarityToTier(clarity)` — exposed for testability and for
17
+ * composition layers that need to query "what tier does this
18
+ * clarity value map to?".
19
+ * - `SoundDispatcher` — per-turn fan-out: for each buffered sound,
20
+ * propagate to every `ListenerTrait` entity and emit one
21
+ * `sound.audibility.heard` semantic event per non-silent result
22
+ * (Phase 6 Step 6.2).
23
+ * - `AUDIBILITY_HEARD_EVENT_TYPE` — the semantic-event type string
24
+ * fired by the dispatcher.
25
+ *
26
+ * @see ADR-172 — Spatial Sound Propagation
27
+ */
28
+ var propagation_1 = require("./propagation");
29
+ Object.defineProperty(exports, "propagate", { enumerable: true, get: function () { return propagation_1.propagate; } });
30
+ Object.defineProperty(exports, "clarityToTier", { enumerable: true, get: function () { return propagation_1.clarityToTier; } });
31
+ var dispatcher_1 = require("./dispatcher");
32
+ Object.defineProperty(exports, "SoundDispatcher", { enumerable: true, get: function () { return dispatcher_1.SoundDispatcher; } });
33
+ Object.defineProperty(exports, "AUDIBILITY_HEARD_EVENT_TYPE", { enumerable: true, get: function () { return dispatcher_1.AUDIBILITY_HEARD_EVENT_TYPE; } });
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/engine/src/sound/index.ts"],"names":[],"mappings":";AAAA,qCAAqC;;;AAErC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,6CAAyD;AAAhD,wGAAA,SAAS,OAAA;AAAE,4GAAA,aAAa,OAAA;AACjC,2CAA4E;AAAnE,6GAAA,eAAe,OAAA;AAAE,yHAAA,2BAA2B,OAAA"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Spatial sound propagation function (ADR-172 Phase 3).
3
+ *
4
+ * Pure logic: given a `Sound` emission, a listener entity-id, and the
5
+ * world-model, returns the `AudibilityEvent` the listener perceives —
6
+ * or `null` if the sound is silent at the listener's location.
7
+ *
8
+ * The function is structured around three pieces:
9
+ *
10
+ * 1. **Edge-graph construction** (`getAcousticEdges`) — for any room,
11
+ * enumerate the rooms it's acoustically connected to, with each
12
+ * edge's cost. Today's edge sources are exits-with-doors and walls
13
+ * (per ADR-173). Future acoustic conduits ride on the same shape
14
+ * without changes here.
15
+ *
16
+ * 2. **Path search** (`findShortestAcousticPath`) — Dijkstra from
17
+ * source room to listener's room. Path cost = sum of edge costs +
18
+ * 1 unit per intermediate room. Wall edges traversed are recorded
19
+ * so the resulting `AudibilityEvent` can name a wall when the path
20
+ * crosses exactly one.
21
+ *
22
+ * 3. **Clarity → tier mapping** (`clarityToTier`) — the ADR-172
23
+ * audibility-tier table.
24
+ *
25
+ * The propagation function does *not* enumerate listeners — that is
26
+ * Phase 5/6's dispatcher. This file is `propagate(sound, listenerId,
27
+ * world, timestamp) → event | null`, intended to be called per
28
+ * listener.
29
+ *
30
+ * Owner context: `@sharpee/engine` — runtime / sound subsystem.
31
+ *
32
+ * @see ADR-172 — Spatial Sound Propagation
33
+ * @see ADR-173 — Wall Adjacency Primitive (substrate)
34
+ */
35
+ import type { EntityId } from "../../core/index";
36
+ import { type AudibilityTier, type IAudibilityEvent, type ISound } from "../../if-domain/index";
37
+ import { WorldModel } from "../../world-model/index";
38
+ /**
39
+ * Propagate a sound emission to a single listener.
40
+ *
41
+ * Returns an `AudibilityEvent` for the listener if the sound reaches
42
+ * them at any tier above `silent`; returns `null` if the sound is
43
+ * silent at the listener's location (no reachable path, cost too high
44
+ * for the volume budget, or the listener has no resolvable room).
45
+ *
46
+ * Same-room emissions short-circuit to `full` audibility regardless of
47
+ * intervening boundaries (degenerate case from ADR-172 §Propagation
48
+ * function step 5).
49
+ *
50
+ * @param sound The emission shape.
51
+ * @param listenerId The entity id of the listener.
52
+ * @param world The world-model carrying rooms, walls, doors,
53
+ * and obstructors.
54
+ * @param timestamp Engine-provided turn-sequence integer for event
55
+ * ordering. Phase 6's dispatcher threads this from
56
+ * the turn manager.
57
+ */
58
+ export declare function propagate(sound: ISound, listenerId: EntityId, world: WorldModel, timestamp: number): IAudibilityEvent | null;
59
+ /**
60
+ * Map a clarity value (volume budget − accumulated path cost) to the
61
+ * ADR-172 audibility tier table.
62
+ *
63
+ * Exposed for testability and so that future composition layers can
64
+ * reuse the mapping (e.g., a conversation-choreography layer that
65
+ * wants to show a "what would the audibility be at this volume from
66
+ * here?" debug overlay).
67
+ */
68
+ export declare function clarityToTier(clarity: number): AudibilityTier;
69
+ //# sourceMappingURL=propagation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propagation.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/engine/src/sound/propagation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAUL,UAAU,EACX,MAAM,sBAAsB,CAAC;AAM9B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,QAAQ,EACpB,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,GAChB,gBAAgB,GAAG,IAAI,CAuBzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAM7D"}