@maka/maka-cli 5.63.1 → 5.65.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 (205) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/sideQuest/commands/ar.js +29 -8
  3. package/bundle/typescript/src/commands/game/sideQuest/commands/ar.js.map +1 -1
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.d.ts +42 -0
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js +86 -3
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js.map +1 -1
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.d.ts +29 -2
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js +151 -1
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js.map +1 -1
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.d.ts +40 -13
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +107 -25
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/breach.d.ts +11 -15
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/breach.js +13 -16
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/breach.js.map +1 -1
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.d.ts +8 -0
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +30 -2
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -1
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.d.ts +40 -49
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +121 -160
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +29 -5
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +7 -8
  25. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +13 -13
  26. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  27. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js +22 -1
  28. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js.map +1 -1
  29. package/bundle/typescript/src/commands/game/sideQuest/commands/cover.d.ts +30 -0
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js +65 -0
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js.map +1 -0
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.d.ts +1 -1
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.js +2 -2
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.js.map +1 -1
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +25 -3
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.d.ts +2 -3
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +2 -2
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/jobs.js +18 -10
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/jobs.js.map +1 -1
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/journal.d.ts +44 -0
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/journal.js +136 -0
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/journal.js.map +1 -0
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/look.d.ts +20 -0
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +157 -5
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +18 -4
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
  50. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +1 -1
  51. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.d.ts +2 -3
  52. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js +2 -2
  53. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js.map +1 -1
  54. package/bundle/typescript/src/commands/game/sideQuest/commands/players.js +8 -2
  55. package/bundle/typescript/src/commands/game/sideQuest/commands/players.js.map +1 -1
  56. package/bundle/typescript/src/commands/game/sideQuest/commands/posture.d.ts +45 -0
  57. package/bundle/typescript/src/commands/game/sideQuest/commands/posture.js +115 -0
  58. package/bundle/typescript/src/commands/game/sideQuest/commands/posture.js.map +1 -0
  59. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +46 -12
  60. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
  61. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +30 -1
  62. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
  63. package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js +11 -1
  64. package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js.map +1 -1
  65. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +12 -0
  66. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
  67. package/bundle/typescript/src/commands/game/sideQuest/commands/talk.d.ts +47 -0
  68. package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js +91 -5
  69. package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js.map +1 -1
  70. package/bundle/typescript/src/commands/game/sideQuest/commands/time.d.ts +20 -0
  71. package/bundle/typescript/src/commands/game/sideQuest/commands/time.js +61 -0
  72. package/bundle/typescript/src/commands/game/sideQuest/commands/time.js.map +1 -0
  73. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +1 -1
  74. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
  75. package/bundle/typescript/src/commands/game/sideQuest/commands/use.d.ts +17 -0
  76. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +136 -1
  77. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
  78. package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
  79. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +15 -1
  80. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
  81. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +128 -44
  82. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +551 -254
  83. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  84. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +82 -84
  85. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
  86. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.d.ts +23 -4
  87. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +95 -79
  88. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js.map +1 -1
  89. package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.d.ts +30 -0
  90. package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.js +176 -0
  91. package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.js.map +1 -0
  92. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +104 -0
  93. package/bundle/typescript/src/commands/game/sideQuest/game.js +268 -16
  94. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  95. package/bundle/typescript/src/commands/game/sideQuest/headless-harness.d.ts +78 -0
  96. package/bundle/typescript/src/commands/game/sideQuest/headless-harness.js +188 -0
  97. package/bundle/typescript/src/commands/game/sideQuest/headless-harness.js.map +1 -0
  98. package/bundle/typescript/src/commands/game/sideQuest/headless.d.ts +5 -0
  99. package/bundle/typescript/src/commands/game/sideQuest/headless.js +78 -0
  100. package/bundle/typescript/src/commands/game/sideQuest/headless.js.map +1 -1
  101. package/bundle/typescript/src/commands/game/sideQuest/models/_index.d.ts +0 -1
  102. package/bundle/typescript/src/commands/game/sideQuest/models/_index.js +0 -1
  103. package/bundle/typescript/src/commands/game/sideQuest/models/_index.js.map +1 -1
  104. package/bundle/typescript/src/commands/game/sideQuest/models/device.d.ts +121 -0
  105. package/bundle/typescript/src/commands/game/sideQuest/models/device.js +145 -0
  106. package/bundle/typescript/src/commands/game/sideQuest/models/device.js.map +1 -0
  107. package/bundle/typescript/src/commands/game/sideQuest/models/door.d.ts +27 -8
  108. package/bundle/typescript/src/commands/game/sideQuest/models/door.js +15 -10
  109. package/bundle/typescript/src/commands/game/sideQuest/models/door.js.map +1 -1
  110. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +143 -0
  111. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +253 -30
  112. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  113. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +68 -0
  114. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +102 -0
  115. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  116. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +80 -34
  117. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +126 -54
  118. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  119. package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +1 -4
  120. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +0 -22
  121. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
  122. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +78 -109
  123. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +4 -101
  124. package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +24 -0
  125. package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.d.ts +52 -0
  126. package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.js +2 -0
  127. package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.js.map +1 -0
  128. package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.d.ts +63 -0
  129. package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.js +2 -0
  130. package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.js.map +1 -0
  131. package/bundle/typescript/src/commands/game/sideQuest/types/seed/player-seed.d.ts +6 -0
  132. package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +4 -0
  133. package/bundle/typescript/src/commands/game/sideQuest/types/seed/scene-seed.d.ts +3 -2
  134. package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.d.ts +2 -28
  135. package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js +1 -14
  136. package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js.map +1 -1
  137. package/bundle/typescript/src/commands/game/sideQuest/ui.js +24 -0
  138. package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
  139. package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.d.ts +55 -0
  140. package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.js +36 -0
  141. package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.js.map +1 -0
  142. package/bundle/typescript/src/commands/game/sideQuest/utilities/ar.d.ts +22 -0
  143. package/bundle/typescript/src/commands/game/sideQuest/utilities/ar.js +26 -0
  144. package/bundle/typescript/src/commands/game/sideQuest/utilities/ar.js.map +1 -1
  145. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-dump.js +6 -0
  146. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-dump.js.map +1 -1
  147. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.d.ts +45 -0
  148. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.js +116 -0
  149. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.js.map +1 -0
  150. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.d.ts +73 -1
  151. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js +56 -1
  152. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js.map +1 -1
  153. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +37 -4
  154. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  155. package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.d.ts +26 -0
  156. package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.js +48 -0
  157. package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.js.map +1 -0
  158. package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.d.ts +23 -0
  159. package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.js +55 -0
  160. package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.js.map +1 -0
  161. package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.d.ts +57 -2
  162. package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js +46 -3
  163. package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js.map +1 -1
  164. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.d.ts +38 -0
  165. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js +42 -0
  166. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js.map +1 -1
  167. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +72 -17
  168. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js.map +1 -1
  169. package/bundle/typescript/src/commands/game/sideQuest/utilities/playtester.d.ts +50 -0
  170. package/bundle/typescript/src/commands/game/sideQuest/utilities/playtester.js +43 -0
  171. package/bundle/typescript/src/commands/game/sideQuest/utilities/playtester.js.map +1 -0
  172. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.d.ts +14 -3
  173. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js +45 -31
  174. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js.map +1 -1
  175. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js +66 -8
  176. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js.map +1 -1
  177. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +81 -15
  178. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +142 -33
  179. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -1
  180. package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.d.ts +88 -0
  181. package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.js +113 -0
  182. package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.js.map +1 -0
  183. package/bundle/typescript/src/commands/game/sideQuest/utilities/world-clock.d.ts +83 -0
  184. package/bundle/typescript/src/commands/game/sideQuest/utilities/world-clock.js +106 -0
  185. package/bundle/typescript/src/commands/game/sideQuest/utilities/world-clock.js.map +1 -0
  186. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +54 -5
  187. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
  188. package/bundle/typescript/src/maka.js +1 -1
  189. package/bundle/typescript/src/maka.js.map +1 -1
  190. package/bundle/typescript/src/tools/sdk-loader/relocate.d.ts +19 -2
  191. package/bundle/typescript/src/tools/sdk-loader/relocate.js +22 -2
  192. package/bundle/typescript/src/tools/sdk-loader/relocate.js.map +1 -1
  193. package/package.json +1 -1
  194. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.d.ts +0 -6
  195. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js +0 -72
  196. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js.map +0 -1
  197. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.d.ts +0 -6
  198. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js +0 -26
  199. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js.map +0 -1
  200. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +0 -143
  201. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +0 -366
  202. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +0 -1
  203. package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.d.ts +0 -28
  204. package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.js +0 -2
  205. package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.js.map +0 -1
@@ -0,0 +1,145 @@
1
+ import { matchesCredential } from '../utilities/credentials.js';
2
+ import { AFFORDANCE_FLAVOR, verbsFor } from '../utilities/affordances.js';
3
+ import { hint } from '../utilities/hints.js';
4
+ /**
5
+ * A DEVICE: a thing in a room that is shut and can be opened.
6
+ *
7
+ * The replacement for Puzzle, and the difference is not cosmetic. A
8
+ * puzzle was a riddle wearing the costume of an object -- it had an
9
+ * answer, a prerequisite, a "solved" flag and a burnable shortcut, and
10
+ * the fiction had to be bent around that shape. A device is an OBJECT
11
+ * with an interface, and the verbs that work it are the ones that would
12
+ * work the real thing. The user's framing is the whole specification:
13
+ * "A lock is not a puzzle, it has a key. A keypad has an interface (to
14
+ * use). An NPC in the doorway can be persuaded/bluffed/conned to move
15
+ * aside. A persistent forcewall can be dispelled."
16
+ *
17
+ * A DEVICE IS ALWAYS ROOM-OWNED. There is no Device hanging off a Door,
18
+ * because a keypad beside a grate physically stands in ONE room -- the
19
+ * one you have to be standing in to reach it. Puzzles could be attached
20
+ * to a door OR placed in a room, which meant every consumer had to
21
+ * handle both, two validators had to agree about it, and a normaliser
22
+ * had to reconcile them; the comments record that fault family killing
23
+ * two consecutive generations. Being in a room is not a special case
24
+ * here, it is the only case.
25
+ *
26
+ * STATE IS EXACTLY ONE FIELD: `open`. Not solved-and-burned-and-locked;
27
+ * a thing is shut or it isn't. Everything else -- whether you may try,
28
+ * what it says when you get it wrong, what opening it does -- is
29
+ * derived from the kind and the config.
30
+ *
31
+ * A CODE ONLY BELONGS ON A PANEL. User ruling, verbatim: "if it's a
32
+ * _mechanical_ lock, it has a key or can be picked. You can't talk at a
33
+ * _mechanical_ lock." A 'lock' therefore opens by key or by force, and
34
+ * a 'ward' by the Art -- neither has anything to hear a phrase with.
35
+ * The migration enforces this by dropping the phrase off anything that
36
+ * isn't a panel.
37
+ */
38
+ export class Device {
39
+ kind;
40
+ name;
41
+ description;
42
+ code;
43
+ keyItem;
44
+ grantsItem;
45
+ atSpot;
46
+ opensExit;
47
+ openMessage;
48
+ refuseMessage;
49
+ /** The whole of this device's state. */
50
+ open = false;
51
+ constructor(config) {
52
+ this.kind = config.kind;
53
+ this.name = config.name;
54
+ this.description = config.description;
55
+ // Enforced here as well as in the migration, because a generated
56
+ // seed can reach this constructor directly.
57
+ this.code = config.kind === 'panel' ? config.code : undefined;
58
+ this.keyItem = config.keyItem;
59
+ this.grantsItem = config.grantsItem;
60
+ this.atSpot = config.atSpot;
61
+ this.opensExit = config.opensExit;
62
+ this.openMessage = config.openMessage || 'It opens.';
63
+ this.refuseMessage = config.refuseMessage || `Nothing happens.`;
64
+ }
65
+ isOpen() {
66
+ return this.open;
67
+ }
68
+ /** Every verb that works this device, for telling the player exactly
69
+ * what they may type. */
70
+ verbs() {
71
+ return verbsFor(this.kind);
72
+ }
73
+ /**
74
+ * OFFER a credential or a key to this device -- the deterministic
75
+ * route, from solve(). Succeeds when the code matches or the right
76
+ * item is in hand; a device with neither can only be forced.
77
+ *
78
+ * No prerequisite branch: `requiresPuzzle` is gone. A thing that
79
+ * needs something else to happen first is modelled by that something
80
+ * else -- a door that is locked, a person in the way -- not by a
81
+ * device refusing on behalf of a puzzle elsewhere.
82
+ */
83
+ offer(providedAnswer, held) {
84
+ if (this.open) {
85
+ return { success: false, message: `${this.name} is already open.` };
86
+ }
87
+ const byKey = this.keyItem ? held.hasItem(this.keyItem.name) : false;
88
+ const byCode = matchesCredential(this.code, providedAnswer);
89
+ if (byKey || byCode) {
90
+ this.open = true;
91
+ return { success: true, message: this.openMessage };
92
+ }
93
+ return { success: false, message: this.refuseMessage };
94
+ }
95
+ /**
96
+ * MAY this device be forced by the given verb -- the gate, from
97
+ * checkBypassGate(). Two of that method's four refusals are gone with
98
+ * the concepts behind them: 'locked' (no prerequisites) and 'burned'
99
+ * (no one-shot shortcut; a lock you failed to pick is a lock you can
100
+ * try again, which is what a lock is).
101
+ */
102
+ canForce(verb) {
103
+ if (this.open) {
104
+ return { allowed: false, message: `${this.name} is already open.` };
105
+ }
106
+ if (!this.verbs().includes(verb)) {
107
+ return { allowed: false, message: this.wrongRouteMessage() };
108
+ }
109
+ return { allowed: true };
110
+ }
111
+ /**
112
+ * Apply an outcome decided elsewhere (a skill roll in the command).
113
+ * Assumes canForce() passed. No burn write -- failing to force a
114
+ * thing does not spend the thing.
115
+ */
116
+ force(succeeded) {
117
+ if (succeeded) {
118
+ this.open = true;
119
+ return { success: true, message: this.openMessage, alarmed: false };
120
+ }
121
+ return { success: false, message: AFFORDANCE_FLAVOR[this.kind].failureMessage, alarmed: true };
122
+ }
123
+ /**
124
+ * The wrong-verb answer, in this device's own fiction -- keyed on what
125
+ * it IS, never on what was typed.
126
+ */
127
+ wrongRouteMessage() {
128
+ const safeRoute = this.code
129
+ ? `(The code itself always works, if you turn it up: "use ${this.name.toLowerCase()} <the code>".)`
130
+ : this.keyItem
131
+ ? `(The right key opens it clean: "unlock" with ${this.keyItem.name} in hand.)`
132
+ : `(There's no code on this one -- force is the only way through.)`;
133
+ return `That finds no purchase here. ${AFFORDANCE_FLAVOR[this.kind].resists}\n${safeRoute}`;
134
+ }
135
+ /** The coaching line, behind the hints toggle. */
136
+ getHint() {
137
+ const text = hint(AFFORDANCE_FLAVOR[this.kind].hint);
138
+ return text.length > 0 ? text : undefined;
139
+ }
140
+ /** Persistence-only: one flag in, nothing out. */
141
+ restoreState(state) {
142
+ this.open = state.open;
143
+ }
144
+ }
145
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/models/device.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,32 @@
1
1
  import { Logger } from '../utilities/logger.js';
2
2
  import { Room } from './room.js';
3
- import { Puzzle } from './puzzle.js';
4
3
  import { AbstractDoor } from '../types/shared/abstracts.js';
5
4
  export interface IDoorConstructorConfig {
6
5
  roomA: Room;
7
6
  roomB: Room;
8
7
  isLocked?: boolean;
9
8
  unlockItemOrSolution?: string;
10
- puzzle?: Puzzle;
9
+ }
10
+ /**
11
+ * WHICH DEVICE HOLDS THIS DOOR, and where that device stands.
12
+ *
13
+ * A Door is ONE object filed in BOTH rooms' exits maps, so a device in
14
+ * the chapel locks the same door a player standing in the crypt below
15
+ * is looking at -- with no device visible from their side. Without
16
+ * this, that player gets "the way up is locked" and nothing to act on:
17
+ * the dead-end refusal this codebase has spent four waves removing.
18
+ *
19
+ * Written ONCE, at wiring time (scene-factory step 4.9), and read by
20
+ * go.ts's refusal. Deliberately NOT a lookup across every room's
21
+ * devices at refusal time: that would be a second source of truth free
22
+ * to disagree with the lock itself, computed in the hot path of every
23
+ * failed move.
24
+ */
25
+ export interface IDoorHolder {
26
+ /** The device's name, as the player would say it. */
27
+ device: string;
28
+ /** The room it physically stands in. */
29
+ room: string;
11
30
  }
12
31
  export declare class Door extends AbstractDoor {
13
32
  name: string;
@@ -15,17 +34,17 @@ export declare class Door extends AbstractDoor {
15
34
  roomB: Room;
16
35
  isLocked: boolean;
17
36
  unlockItemOrSolution?: string;
18
- puzzle?: Puzzle;
37
+ /** Set when a Device holds this door shut -- see IDoorHolder. */
38
+ heldBy?: IDoorHolder;
19
39
  logger: Logger;
20
40
  isLockable: boolean;
21
41
  constructor(config: IDoorConstructorConfig);
22
42
  /**
23
- * Wired in after synthesis has built every puzzle in the scene (see
24
- * scene-factory.ts) -- a door's guarding puzzle may not exist yet at the
25
- * point the door itself is created from "exits[]". Locks the door if it
26
- * wasn't already (a puzzle-guarded door starts locked by definition).
43
+ * Marks this door as held by a Device, and locks it. Called once at
44
+ * wiring time; the back-reference is what lets a refusal on EITHER
45
+ * side name where the mechanism actually is.
27
46
  */
28
- attachPuzzle(puzzle: Puzzle): void;
47
+ heldByDevice(deviceName: string, roomName: string): void;
29
48
  getOtherSide(currentRoom: Room): Room;
30
49
  getOtherSideToName(currentRoom: Room): string;
31
50
  attemptUnlock(itemOrSolution: string): boolean;
@@ -6,7 +6,8 @@ export class Door extends AbstractDoor {
6
6
  roomB; // The other side of the door
7
7
  isLocked;
8
8
  unlockItemOrSolution;
9
- puzzle;
9
+ /** Set when a Device holds this door shut -- see IDoorHolder. */
10
+ heldBy;
10
11
  logger;
11
12
  isLockable;
12
13
  constructor(config) {
@@ -14,21 +15,25 @@ export class Door extends AbstractDoor {
14
15
  this.roomA = config.roomA;
15
16
  this.roomB = config.roomB;
16
17
  this.name = `${this.roomA.name} <-> ${this.roomB.name}`;
17
- this.isLocked = config.isLocked || !!config.puzzle || false;
18
+ // A door starts locked only if the seed says so, or if a key is
19
+ // required. It NO LONGER infers a locked start from carrying a
20
+ // puzzle -- there is no puzzle. A Device that opens this door locks
21
+ // it explicitly at wiring time (scene-factory step 4.9), and the
22
+ // golden test asserts that, because a dropped lock step fails
23
+ // silently: the hub's only gated content would simply stand open.
24
+ this.isLocked = config.isLocked || false;
18
25
  this.unlockItemOrSolution = config.unlockItemOrSolution;
19
- this.puzzle = config.puzzle;
20
26
  this.logger = Logger.getInstance();
21
- this.isLockable = !!config.unlockItemOrSolution || !!config.puzzle;
27
+ this.isLockable = !!config.unlockItemOrSolution;
22
28
  this.logger.write(`Door created between ${this.roomA.name} and ${this.roomB.name}. Is Locked: ${this.isLocked}`);
23
29
  }
24
30
  /**
25
- * Wired in after synthesis has built every puzzle in the scene (see
26
- * scene-factory.ts) -- a door's guarding puzzle may not exist yet at the
27
- * point the door itself is created from "exits[]". Locks the door if it
28
- * wasn't already (a puzzle-guarded door starts locked by definition).
31
+ * Marks this door as held by a Device, and locks it. Called once at
32
+ * wiring time; the back-reference is what lets a refusal on EITHER
33
+ * side name where the mechanism actually is.
29
34
  */
30
- attachPuzzle(puzzle) {
31
- this.puzzle = puzzle;
35
+ heldByDevice(deviceName, roomName) {
36
+ this.heldBy = { device: deviceName, room: roomName };
32
37
  this.isLocked = true;
33
38
  this.isLockable = true;
34
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"door.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/models/door.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAIhD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAU5D,MAAM,OAAO,IAAK,SAAQ,YAAY;IACpC,IAAI,CAAS;IACb,KAAK,CAAO,CAAE,uBAAuB;IACrC,KAAK,CAAO,CAAE,6BAA6B;IAC3C,QAAQ,CAAU;IAClB,oBAAoB,CAAU;IAC9B,MAAM,CAAU;IAChB,MAAM,CAAS;IACf,UAAU,CAAS;IAEnB,YAAY,MAA8B;QACxC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxD,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,KAAK,CAAC;QAC5D,IAAI,CAAC,oBAAoB,GAAG,MAAM,CAAC,oBAAoB,CAAC;QACxD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;QACnC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,oBAAoB,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;QAEnE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,IAAI,CAAC,KAAK,CAAC,IAAI,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,gBAAgB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnH,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,MAAc;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,YAAY,CAAC,WAAiB;QAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;IAChE,CAAC;IAED,kBAAkB,CAAC,WAAiB;QAClC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1F,CAAC;IAED,aAAa,CAAC,cAAsB;QAClC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,oBAAoB,KAAK,cAAc,EAAE,CAAC;YAC/F,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,IAAI,CAAC,IAAI,WAAW,cAAc,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,IAAI,CAAC,IAAI,WAAW,cAAc,EAAE,CAAC,CAAC;QAClF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF"}
1
+ {"version":3,"file":"door.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/models/door.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA+B5D,MAAM,OAAO,IAAK,SAAQ,YAAY;IACpC,IAAI,CAAS;IACb,KAAK,CAAO,CAAE,uBAAuB;IACrC,KAAK,CAAO,CAAE,6BAA6B;IAC3C,QAAQ,CAAU;IAClB,oBAAoB,CAAU;IAC9B,iEAAiE;IACjE,MAAM,CAAe;IACrB,MAAM,CAAS;IACf,UAAU,CAAS;IAEnB,YAAY,MAA8B;QACxC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxD,gEAAgE;QAChE,+DAA+D;QAC/D,oEAAoE;QACpE,iEAAiE;QACjE,8DAA8D;QAC9D,kEAAkE;QAClE,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC;QACzC,IAAI,CAAC,oBAAoB,GAAG,MAAM,CAAC,oBAAoB,CAAC;QACxD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;QACnC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,oBAAoB,CAAC;QAEhD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,IAAI,CAAC,KAAK,CAAC,IAAI,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,gBAAgB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnH,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,UAAkB,EAAE,QAAgB;QAC/C,IAAI,CAAC,MAAM,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QACrD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,YAAY,CAAC,WAAiB;QAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;IAChE,CAAC;IAED,kBAAkB,CAAC,WAAiB;QAClC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1F,CAAC;IAED,aAAa,CAAC,cAAsB;QAClC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,oBAAoB,KAAK,cAAc,EAAE,CAAC;YAC/F,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,IAAI,CAAC,IAAI,WAAW,cAAc,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,IAAI,CAAC,IAAI,WAAW,cAAc,EAAE,CAAC,CAAC;QAClF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF"}
@@ -8,6 +8,87 @@ export interface INPCConstructorConfig {
8
8
  description: string;
9
9
  playerConstructorConfig: IPlayerConstructorConfig;
10
10
  }
11
+ /**
12
+ * One barrier as the prompt needs to see it.
13
+ *
14
+ * STRUCTURAL on purpose, and that is the entire reason this interface
15
+ * exists rather than the functions below taking a `Puzzle`. The
16
+ * puzzle/solve mechanic is being replaced by a `Device` model, and these
17
+ * two functions are the last thing standing between an NPC and inventing
18
+ * a door code. Typing them over a SHAPE rather than a class means the
19
+ * type swap is a change of adapter at one call site, with these
20
+ * functions and their tests untouched -- so "the rewrite preserved the
21
+ * behaviour" becomes something the suite proves rather than something we
22
+ * hope.
23
+ *
24
+ * `description` is optional and interpolated raw, which preserves an
25
+ * existing wart exactly: a barrier with no description renders the
26
+ * literal string "undefined" into the prompt today. Recorded rather than
27
+ * fixed, because this extraction is behaviour-preserving by contract and
28
+ * a silent improvement here would make the swap unprovable.
29
+ *
30
+ * `sealingDirection` is a plain string so this pair depends on nothing:
31
+ * a `Direction` is assignable to it, and the functions stay importable
32
+ * from anywhere.
33
+ */
34
+ export interface IBarrierPost {
35
+ name: string;
36
+ /**
37
+ * MANDATORY, as of the Device flip. It was optional while Puzzle fed
38
+ * this interface (a riddle genuinely could be absent), and the cost
39
+ * was that barrierPostNote interpolated it raw and rendered the
40
+ * literal string "undefined" into an NPC's prompt. Device cannot be
41
+ * constructed without a description, so that branch is DEAD rather
42
+ * than guarded -- a deletion, not a fix.
43
+ */
44
+ description: string;
45
+ /** The credential that opens it. Absent means even its keeper doesn't know. */
46
+ code?: string;
47
+ /**
48
+ * The item that opens it outright, if one exists. Present ONLY so the
49
+ * brief can stop asserting "no key" about a lock that takes exactly
50
+ * one key -- see the note in barrierPostNote. A plain string, like
51
+ * sealingDirection, so this pair keeps depending on nothing.
52
+ */
53
+ keyItem?: string;
54
+ open: boolean;
55
+ /** Set only when this barrier seals an EXIT rather than sitting in the room. */
56
+ sealingDirection?: string;
57
+ }
58
+ /**
59
+ * BARRIERS AT YOUR POST (a real session: a beaten sentry announced he
60
+ * was "unlocking" a spell-sealed hatch he could never open -- his AI had
61
+ * no idea what guarded his own door, tried a doomed "unlock", and
62
+ * claimed success). An NPC standing at a barrier knows what seals it,
63
+ * that he cannot wish it open, and HOW it opens -- as a guarded secret,
64
+ * so coercion and bribery become a real path to the pass.
65
+ *
66
+ * The CALLER decides whose barriers these are. Companions get an empty
67
+ * list: a bound spirit that knew the code would simply hand it to its
68
+ * master and skip the barrier entirely.
69
+ *
70
+ * Exported and pure for the reason the note below is: it fails SILENTLY.
71
+ * Nothing throws when this stops handing over the real pass -- the NPC
72
+ * just starts inventing one, and the only detector is a player three
73
+ * rooms away typing a code that was never real.
74
+ */
75
+ export declare function barrierPostNote(barriers: IBarrierPost[]): string;
76
+ /**
77
+ * How many real credentials this NPC was actually handed -- the number
78
+ * credentialHonestyNote turns into "you know the pass for the barrier at
79
+ * your own post, and NO others" versus "you hold NONE".
80
+ *
81
+ * A barrier only counts if it is SHUT and has a code. An open one is
82
+ * nothing to guard, and one with no code is a secret its own keeper
83
+ * doesn't have.
84
+ *
85
+ * It counts DEVICES only. A guard once carried a passphrase of their
86
+ * own and it was counted here; the user removed the concept
87
+ * (2026-08-27) because the way past a person is social or violent, and
88
+ * because a guard's phrase had no clue guarantee behind it -- see the
89
+ * note where Player.passphrase used to be.
90
+ */
91
+ export declare function passesHeldBy(barriers: IBarrierPost[]): number;
11
92
  /**
12
93
  * THE CAST PIN -- geographySummary's missing twin. See the note at its
13
94
  * call site in respondTo(). Exported and PURE for the same reason as
@@ -30,6 +111,68 @@ export declare function castPin(names: string[]): string;
30
111
  * notice.
31
112
  */
32
113
  export declare function credentialHonestyNote(passesHeld: number): string;
114
+ /**
115
+ * WHAT YOU KNOW ENDS AT THIS ROOM -- audit findings #9 and #10, the last
116
+ * two unpinned facts from the credential sweep, and the same shape as
117
+ * the credential hole: pinned for the NPC's own room, invented for
118
+ * anywhere else.
119
+ *
120
+ * #9 an NPC asked where something is names a REAL room the item is
121
+ * not in. Real, because geography is pinned -- which makes it
122
+ * worse, not better. A vague answer costs a player nothing; a
123
+ * plausible, checkable, wrong one costs them the walk there, the
124
+ * search, and their trust in the next true thing they hear.
125
+ * #10 an NPC asked what a door ELSEWHERE needs invents a requirement.
126
+ * They only ever see their own room's exits.
127
+ *
128
+ * Deliberately NOT an index of the world's items and doors: that would
129
+ * spoil the game and cost prompt budget to do it. Pin what the NPC can
130
+ * legitimately know, deny the rest, index nothing.
131
+ *
132
+ * The scoping matters and is the whole difficulty. An NPC absolutely may
133
+ * say where things are when they were TOLD -- the client's briefing, the
134
+ * legwork facts ask.ts hands over, their own scripted talking points are
135
+ * all real knowledge, engine-supplied, and a blanket denial would gag
136
+ * the Johnson mid-brief. What is banned is GUESSING TO BE HELPFUL, which
137
+ * is the specific thing a model does when it has no answer and wants to
138
+ * give one.
139
+ */
140
+ export declare function localKnowledgeNote(roomName?: string): string;
141
+ /**
142
+ * OUT-OF-CHARACTER LEAKAGE on the exposition channel.
143
+ *
144
+ * Playtest 2026-08-27 (Sherry): "The response from Mr. Krow was OOC" --
145
+ * Krow answered in the game's own voice instead of in his.
146
+ *
147
+ * This is the SAME SEAM as splitSpokenLines, one layer up, and widening
148
+ * it was the right move rather than building a second mechanism. The
149
+ * exposition channel is the only place a model's non-fiction voice can
150
+ * reach the screen: `say` lines are attributed dialogue and [COMMAND]
151
+ * lines are parsed, so anything that is neither prints as narration in
152
+ * narration blue. A filter for exactly this already existed and was
153
+ * built from ONE report -- the first-person director ("I'll have Deacon
154
+ * Ruy stay guarded but crack slightly under the pressure...") -- so it
155
+ * only ever matched that one shape.
156
+ *
157
+ * The shapes it missed are the ordinary ones: the assistant preamble
158
+ * ("Certainly! Here's how he responds"), the frame ("As Mr. Krow, I'd
159
+ * say..."), the explicit marker ("OOC:", "Note:"), and talk about the
160
+ * player rather than to them ("The player is trying to...").
161
+ *
162
+ * SUPPRESSED, not restyled. The player also asked for OOC to be visibly
163
+ * distinct from fiction, and that is a real and separate feature -- but
164
+ * "Certainly! Here's what he'd say" is not out-of-character INFORMATION,
165
+ * it is the model failing to stay in the fiction, and there is nothing
166
+ * in it worth showing in any colour. The genuine OOC channels
167
+ * (logger.meta for mechanics, hint() for coaching) already exist and are
168
+ * already visibly distinct; routing leakage into them would fill the
169
+ * player's straight-talk channel with noise.
170
+ *
171
+ * Exported and PURE because it fails silently in the usual way: nothing
172
+ * throws when a pattern stops matching, the line just prints, and the
173
+ * only detector is a player watching their fixer break character.
174
+ */
175
+ export declare function isOutOfCharacter(line: string, npcName?: string): boolean;
33
176
  /**
34
177
  * SPEECH REPAIR: turns a model reply's raw lines into lines the
35
178
  * classifier below can actually read. Exported and PURE so the rules