@maka/maka-cli 5.7.0 → 5.9.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 (178) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/deploy/ubuntu.sub.cmd.js +16 -31
  3. package/bundle/typescript/src/commands/deploy/ubuntu.sub.cmd.js.map +1 -1
  4. package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +6 -4
  5. package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -1
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js +3 -2
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js.map +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js +19 -7
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/ask.js.map +1 -1
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js +3 -2
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js.map +1 -1
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +35 -9
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js.map +1 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +1 -1
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +2 -1
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -1
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +31 -4
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +16 -2
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -1
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +7 -0
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +118 -29
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.d.ts +33 -0
  26. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js +138 -0
  27. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js.map +1 -0
  28. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js +2 -1
  29. package/bundle/typescript/src/commands/game/sideQuest/commands/contacts.js.map +1 -1
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +5 -4
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js.map +1 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js +2 -1
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js.map +1 -1
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/give.js +5 -0
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/give.js.map +1 -1
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/go.d.ts +6 -0
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +138 -19
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.d.ts +38 -0
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js +178 -0
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/grapple.js.map +1 -0
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +14 -5
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js +7 -1
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js.map +1 -1
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/hints.d.ts +10 -0
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/hints.js +24 -0
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/hints.js.map +1 -0
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js +2 -1
  50. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js.map +1 -1
  51. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +2 -1
  52. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js.map +1 -1
  53. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js +25 -3
  54. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js.map +1 -1
  55. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js +2 -1
  56. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js.map +1 -1
  57. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +53 -8
  58. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  59. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +3 -2
  60. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
  61. package/bundle/typescript/src/commands/game/sideQuest/commands/move.d.ts +41 -0
  62. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +160 -0
  63. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js.map +1 -0
  64. package/bundle/typescript/src/commands/game/sideQuest/commands/order.d.ts +19 -0
  65. package/bundle/typescript/src/commands/game/sideQuest/commands/order.js +152 -0
  66. package/bundle/typescript/src/commands/game/sideQuest/commands/order.js.map +1 -0
  67. package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js +5 -0
  68. package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js.map +1 -1
  69. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js +1 -1
  70. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js.map +1 -1
  71. package/bundle/typescript/src/commands/game/sideQuest/commands/project.js +2 -1
  72. package/bundle/typescript/src/commands/game/sideQuest/commands/project.js.map +1 -1
  73. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js +2 -1
  74. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js.map +1 -1
  75. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +3 -2
  76. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
  77. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +1 -1
  78. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
  79. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +24 -6
  80. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
  81. package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js +7 -1
  82. package/bundle/typescript/src/commands/game/sideQuest/commands/sell.js.map +1 -1
  83. package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js +2 -1
  84. package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js.map +1 -1
  85. package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js +3 -2
  86. package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js.map +1 -1
  87. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js +11 -1
  88. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js.map +1 -1
  89. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +24 -13
  90. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js.map +1 -1
  91. package/bundle/typescript/src/commands/game/sideQuest/commands/summon.d.ts +19 -0
  92. package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js +113 -0
  93. package/bundle/typescript/src/commands/game/sideQuest/commands/summon.js.map +1 -0
  94. package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js +4 -0
  95. package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js.map +1 -1
  96. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +37 -4
  97. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
  98. package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js +6 -0
  99. package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js.map +1 -1
  100. package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js +5 -0
  101. package/bundle/typescript/src/commands/game/sideQuest/commands/treat.js.map +1 -1
  102. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +9 -2
  103. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
  104. package/bundle/typescript/src/commands/game/sideQuest/commands/work.js +2 -1
  105. package/bundle/typescript/src/commands/game/sideQuest/commands/work.js.map +1 -1
  106. package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js +2 -1
  107. package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js.map +1 -1
  108. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js +2 -1
  109. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js.map +1 -1
  110. package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js +5 -0
  111. package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js.map +1 -1
  112. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +11 -0
  113. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +138 -23
  114. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  115. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +12 -0
  116. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
  117. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +91 -0
  118. package/bundle/typescript/src/commands/game/sideQuest/game.js +501 -23
  119. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  120. package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +2 -0
  121. package/bundle/typescript/src/commands/game/sideQuest/models/item.js +3 -0
  122. package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
  123. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +44 -1
  124. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +349 -9
  125. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  126. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +10 -2
  127. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +36 -7
  128. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  129. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +2 -0
  130. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +9 -1
  131. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +1 -1
  132. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +13 -0
  133. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +57 -4
  134. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  135. package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +28 -0
  136. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +172 -4
  137. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
  138. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +76 -20
  139. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +157 -34
  140. package/bundle/typescript/src/commands/game/sideQuest/types/seed/exit-seed.d.ts +1 -0
  141. package/bundle/typescript/src/commands/game/sideQuest/types/seed/item-seed.d.ts +1 -0
  142. package/bundle/typescript/src/commands/game/sideQuest/types/seed/player-seed.d.ts +1 -0
  143. package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.d.ts +1 -0
  144. package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +6 -0
  145. package/bundle/typescript/src/commands/game/sideQuest/ui.js +81 -5
  146. package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
  147. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +21 -5
  148. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js.map +1 -1
  149. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.d.ts +25 -3
  150. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +128 -28
  151. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  152. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.d.ts +7 -4
  153. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js +8 -5
  154. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js.map +1 -1
  155. package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.d.ts +36 -0
  156. package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.js +57 -0
  157. package/bundle/typescript/src/commands/game/sideQuest/utilities/grapple.js.map +1 -0
  158. package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.d.ts +4 -0
  159. package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.js +55 -0
  160. package/bundle/typescript/src/commands/game/sideQuest/utilities/hints.js.map +1 -0
  161. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.d.ts +14 -0
  162. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js +15 -0
  163. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js.map +1 -0
  164. package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.d.ts +16 -0
  165. package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.js +49 -4
  166. package/bundle/typescript/src/commands/game/sideQuest/utilities/logger.js.map +1 -1
  167. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +31 -7
  168. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  169. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +121 -0
  170. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +315 -0
  171. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -0
  172. package/bundle/typescript/src/commands/ssl.js +10 -15
  173. package/bundle/typescript/src/commands/ssl.js.map +1 -1
  174. package/bundle/typescript/src/templates/react/kit/mui/auth/password-reset/basic-password-reset-page.js.tsx +3 -4
  175. package/bundle/typescript/src/tools/ssl/ssl.class.d.ts +8 -0
  176. package/bundle/typescript/src/tools/ssl/ssl.class.js +53 -0
  177. package/bundle/typescript/src/tools/ssl/ssl.class.js.map +1 -1
  178. package/package.json +1 -1
@@ -7,10 +7,46 @@ import { getArchetype, outfitArchetype } from './archetypes.js';
7
7
  import { STANCE_CYCLE } from './models/player.js';
8
8
  import { AutoFight } from './utilities/auto-fight.js';
9
9
  import { TERMINAL_RESET_SEQUENCE } from './utilities/terminal-reset.js';
10
+ // Windows Terminal draws astral-plane emoji (📞 call icon, 📍 HUD pin,
11
+ // 👥 companions, 💀) TWO columns wide, but blessed's 2015-era unicode
12
+ // tables predate emoji being classified wide and count them as ONE. The
13
+ // one-cell drift on every icon row pushed box borders sideways into the
14
+ // next panel and left smartCSR's partial redraws smearing stray orphan
15
+ // characters (two player screenshots; the text-presentation ⚔, one cell
16
+ // on both sides, stayed flush). Teaching charWidth the pictograph blocks
17
+ // fixes the model: strWidth (wrapping) and the cell renderer both route
18
+ // through this same export. BMP dingbats stay untouched -- the terminal
19
+ // really does draw those narrow.
20
+ // TWO patches, and they only work as a pair (blessed's own draw comment:
21
+ // "eat the next character which should be a space due to parseContent's
22
+ // behavior"): charWidth makes wrapping count 2 and the emitter skip the
23
+ // filler cell; chars.all makes parseContent actually INSERT that \x03
24
+ // filler after each pictograph. Either alone leaves rows misaligned or
25
+ // eats a real character.
26
+ {
27
+ const uni = blessed.unicode;
28
+ const origCharWidth = uni.charWidth;
29
+ uni.charWidth = function (text, i) {
30
+ const point = typeof text === 'number' ? text : uni.codePointAt(text, i ?? 0);
31
+ if (point >= 0x1F300 && point <= 0x1FAFF)
32
+ return 2;
33
+ return origCharWidth.call(this, text, i);
34
+ };
35
+ // U+1F300-1FAFF as surrogate pairs. Wrapped in a fresh outer group so
36
+ // parseContent's "$1\x03" replacement still means "the whole match".
37
+ const pictographs = '\\ud83c[\\udf00-\\udfff]|\\ud83d[\\udc00-\\udfff]|\\ud83e[\\udc00-\\udeff]';
38
+ uni.chars.all = new RegExp(`(${uni.chars.all.source}|${pictographs})`, 'g');
39
+ }
10
40
  export async function main(playerName, sceneSeed, options) {
11
41
  // Create a screen object
12
42
  const screen = blessed.screen({
13
43
  smartCSR: true,
44
+ // Without this, blessed strips every astral-plane character (all
45
+ // modern emoji -- the 📞 call icon, companion icons) to "?" during
46
+ // content parsing, no matter what the terminal can render. BMP
47
+ // symbols like ⚔ slipped through, which is why a session showed
48
+ // swords but question-mark phones.
49
+ fullUnicode: true,
14
50
  title: 'Side Quest Adventure Game',
15
51
  });
16
52
  // TERMINAL HYGIENE: blessed arms xterm modes (mouse tracking, alt
@@ -75,6 +111,7 @@ export async function main(playerName, sceneSeed, options) {
75
111
  ? `{yellow-fg}${options.generationNotice}{/yellow-fg}\n\n`
76
112
  : '';
77
113
  game.logBox.setContent(notice + ready); // getWelcomeMessage should return the initial game introduction or room description
114
+ gameLog.transcript(notice + ready); // the transcript opens the way the session did
78
115
  game.updateEquipment(game.player.equipment);
79
116
  game.updateInventory(game.player.inventory, capitalCase(game.scene.getCurrencyType()));
80
117
  game.updateExits(game.player.currentLocation);
@@ -101,8 +138,10 @@ export async function main(playerName, sceneSeed, options) {
101
138
  game.autoFight.cancelPending();
102
139
  // white for manual, not gray -- gray is ANSI bright-black, illegible
103
140
  // on stock dark palettes (same fix as the HUD stance in game.ts).
141
+ // Mechanics ticker, not the narrative: the HUD already shows the
142
+ // standing stance; this is the change notice (readability pass).
104
143
  const color = next === 'aggressive' ? 'red' : next === 'guarded' ? 'yellow' : 'white';
105
- gameLog.logWithColor(`⚔ Stance: ${next.toUpperCase()} -- ${AutoFight.describe(next)}.`, color);
144
+ gameLog.meta(`⚔ Stance: ${next.toUpperCase()} -- ${AutoFight.describe(next)}.`, color);
106
145
  game.updateStatus();
107
146
  screen.render();
108
147
  });
@@ -121,6 +160,30 @@ export async function main(playerName, sceneSeed, options) {
121
160
  // whatever the player had started typing NEXT during the wait.
122
161
  game.inputBox.clearValue();
123
162
  screen.render();
163
+ // The transcript reads like the session: the typed command
164
+ // first, the game's answer after (see Logger.transcript).
165
+ if (input.trim().length > 0)
166
+ gameLog.transcript(`> ${input}`);
167
+ // TURN BLOCKS (readability pass): echo the command into the
168
+ // visible log too, behind a blank line, so each command's
169
+ // output reads as its own block -- BEFORE handleInput, so a
170
+ // slow command (AI round trip, combat beats) shows what was
171
+ // asked while it works. Cyan: blessed has no dim tag and gray
172
+ // is banned; cyan is already this game's machine voice.
173
+ // pushLine, not Logger -- the transcript line above already
174
+ // recorded it. "clear" is exempt (echo-then-wipe is noise),
175
+ // and "say" keeps only the blank separator -- your words
176
+ // already land as the yellow speech line, and "> say hoi"
177
+ // above "hoi" read as a stutter (playtest note).
178
+ const echoVerb = input.trim().split(' ')[0]?.toLowerCase();
179
+ if (input.trim().length > 0 && echoVerb !== 'clear') {
180
+ game.logBox.pushLine('');
181
+ if (echoVerb !== 'say') {
182
+ game.logBox.pushLine(`{cyan-fg}> ${input.trim()}{/cyan-fg}`);
183
+ }
184
+ game.followLog();
185
+ screen.render();
186
+ }
124
187
  const response = await game.handleInput(input); // handleInput is the method that processes commands
125
188
  if (['take', 'equip', 'unequip'].includes(input.split(' ')[0])) {
126
189
  // Example usage: update the inventory with a new list of items
@@ -137,15 +200,28 @@ export async function main(playerName, sceneSeed, options) {
137
200
  game.updateStatus();
138
201
  if (input.split(' ')[0] === 'clear') {
139
202
  game.logBox.setContent('');
203
+ game.metaBox.setContent('');
204
+ // Blessed repositions a box's LABEL on 'scroll' events --
205
+ // setContent('') resets the scroll base without firing one,
206
+ // stranding the title mid-box (screenshot bug: "Game Log"
207
+ // sitting at the bottom after "clear"). Scrolling home
208
+ // fires the event and the label snaps back to the border.
209
+ game.logBox.scrollTo(0);
210
+ game.metaBox.scrollTo(0);
140
211
  }
141
- gameLog.write(`Input response: ${response}`);
212
+ if (response)
213
+ gameLog.transcript(response);
142
214
  if (response === null) {
143
215
  screen.render();
144
216
  gameLog.write(`<<<<<<<<--------- Terminating game`);
145
217
  process.exit(0); // Exit the game
146
218
  }
147
- game.logBox.pushLine(response);
148
- game.logBox.scrollTo(game.logBox.getScrollHeight());
219
+ // Empty responses stop printing blank filler -- the turn-block
220
+ // echo above is the separator now.
221
+ if (response) {
222
+ game.logBox.pushLine(response);
223
+ game.followLog();
224
+ }
149
225
  }
150
226
  catch (error) {
151
227
  // A crashed command must FAIL VISIBLY -- the old silent
@@ -155,7 +231,7 @@ export async function main(playerName, sceneSeed, options) {
155
231
  // only in the log file).
156
232
  gameLog.error(`Error handling input: ${error}`);
157
233
  game.logBox.pushLine(`{red-fg}Something glitched handling that command -- it's been logged. The game is still running.{/red-fg}`);
158
- game.logBox.scrollTo(game.logBox.getScrollHeight());
234
+ game.followLog();
159
235
  }
160
236
  finally {
161
237
  // Focus restoration runs on EVERY path -- the same real session
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../../../../src/commands/game/sideQuest/ui.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../../../../src/commands/game/sideQuest/ui.ts"],"names":[],"mappings":"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"}
@@ -90,10 +90,24 @@ export class AutoFight {
90
90
  // onExchangeSettled reschedules us. No waiting here.
91
91
  if (player.inExchange || target.inExchange)
92
92
  return;
93
- // Guarded honors a surrender -- the fight is over, hold fire. The
94
- // stand-down is announced once; no reschedule, so the autopilot stays
95
- // quiet unless violence actually resumes (a new exchange re-arms it).
93
+ // Guarded honors a surrender -- but doesn't trust it: the final
94
+ // action is a RESTRAIN (player request; SR5e Subduing p.195, the
95
+ // surrendered branch is an automatic no-roll hold). Hands-up
96
+ // prisoners get zip-tied, not taken at their word. One-shot: an
97
+ // already-held target (by anyone) means truly stand down, and off
98
+ // the meat plane there are no hands to hold with.
96
99
  if (player.autoStance === 'guarded' && target.surrendered) {
100
+ if (player.plane === 'meat' && target.plane === 'meat'
101
+ && player.grappling !== target.name && !target.grappledBy) {
102
+ const cmd = `restrain ${target.name}`;
103
+ logger.meta(`⚔ [guarded] ${cmd}`, 'cyan');
104
+ const result = await this.game.handleInput(cmd);
105
+ if (result) {
106
+ logger.log(result);
107
+ }
108
+ this.game.updateStatus();
109
+ return;
110
+ }
97
111
  logger.logWithColor(`⚔ ${target.name} has surrendered -- you hold fire. (guarded)`, 'yellow');
98
112
  this.game.updateStatus();
99
113
  return;
@@ -101,7 +115,9 @@ export class AutoFight {
101
115
  const cmd = this.chooseAction(player, target);
102
116
  // Cyan, not gray: the autopilot announcing it acted must be legible on
103
117
  // stock dark palettes (gray = ANSI bright-black, near-invisible there).
104
- logger.logWithColor(`⚔ [${player.autoStance}] ${cmd}`, 'cyan');
118
+ // Mechanics ticker, not the narrative -- the strike it triggers tells
119
+ // the story in the world log (readability pass).
120
+ logger.meta(`⚔ [${player.autoStance}] ${cmd}`, 'cyan');
105
121
  const result = await this.game.handleInput(cmd);
106
122
  // Player-initiated exchanges self-announce ('' result); anything else
107
123
  // (a dry-gun reload prompt, a refusal) is information the player needs.
@@ -141,7 +157,7 @@ export class AutoFight {
141
157
  static describe(stance) {
142
158
  switch (stance) {
143
159
  case 'manual': return 'counters when struck (every stance does) -- but pressing the fight is yours to type';
144
- case 'guarded': return 'presses the fight for you; subdues instead of killing when they are nearly down, holds fire on surrender';
160
+ case 'guarded': return 'presses the fight for you; subdues instead of killing when they are nearly down, and RESTRAINS them when they surrender';
145
161
  case 'aggressive': return 'presses the fight to the death; surrender means nothing';
146
162
  }
147
163
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auto-fight.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/utilities/auto-fight.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auto-fight.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/utilities/auto-fight.ts"],"names":[],"mappings":"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"}
@@ -91,6 +91,7 @@ export declare class CombatExchange {
91
91
  second: Player | NPC;
92
92
  extraStriker: Player | NPC | null;
93
93
  lines: string[];
94
+ metaLines: string[];
94
95
  };
95
96
  /**
96
97
  * The player should see a fight's blow-by-blow only when they're actually
@@ -108,7 +109,7 @@ export declare class CombatExchange {
108
109
  * fight an NPC started. The status box refreshes with every block so the
109
110
  * condition monitor visibly ticks as damage lands.
110
111
  */
111
- announce(lines: string[]): Promise<void>;
112
+ announce(lines: string[], meta?: string[]): Promise<void>;
112
113
  /**
113
114
  * The default, weapon-based strike profile. A firearm with a dry
114
115
  * magazine automatically falls back to bare fists -- this covers the
@@ -117,8 +118,19 @@ export declare class CombatExchange {
117
118
  * blocked with a reload prompt before ever reaching here (attack.ts).
118
119
  */
119
120
  weaponProfile(attacker: Player | NPC, defender: Player | NPC): IStrikeProfile;
120
- /** One directed strike: attack vs defense, then soak. Mutates the defender. */
121
- resolveStrike(attacker: Player | NPC, defender: Player | NPC, profile?: IStrikeProfile): string[];
121
+ /**
122
+ * One directed strike: attack vs defense, then soak. Mutates the
123
+ * defender. Readability split: `meta` carries the dice anatomy
124
+ * (Attack/Defense/Soak rolls, net-hit math) for the Mechanics ticker;
125
+ * `world` carries the narrative -- one compact header with the
126
+ * outcome, then the damage/flavor lines the player feels. Callers
127
+ * rebuild AI-history reports as [...world, ...meta] so an NPC's
128
+ * memory of the fight keeps the dice.
129
+ */
130
+ resolveStrike(attacker: Player | NPC, defender: Player | NPC, profile?: IStrikeProfile): {
131
+ world: string[];
132
+ meta: string[];
133
+ };
122
134
  /**
123
135
  * Death/knockout narration is announced BEFORE the removal side effects
124
136
  * run -- removeActor() fires the "drops what they carried" observer line
@@ -128,6 +140,16 @@ export declare class CombatExchange {
128
140
  * full STUN track -- see cast.ts drain) rather than a death: same
129
141
  * out-of-the-run consequences, honestly narrated as unconsciousness.
130
142
  */
143
+ /**
144
+ * The PLAYER dropped from a full STUN track: out of the fight, not out
145
+ * of the game (RAW p.379 "out of action for the rest of the fight";
146
+ * instant death is physical overflow only, p.170). No endGame here --
147
+ * Scene.beginPlayerKnockout defers judgment until the room settles:
148
+ * hostiles all down/surrendered/gone means the player comes to with a
149
+ * Body + Willpower recovery (p.207); a hostile left standing over them
150
+ * with nobody fighting for them means the street decides, as ever.
151
+ */
152
+ resolvePlayerKnockout(killer: Player | NPC): Promise<string[]>;
131
153
  resolveDeath(fallen: Player | NPC, killer: Player | NPC, opts?: {
132
154
  fallLine?: string;
133
155
  gameOverLine?: string;
@@ -1,3 +1,4 @@
1
+ import { NPC } from '../models/npc.js';
1
2
  import { rollPool, formatRoll } from './dice.js';
2
3
  import { leaveMatrix, leaveDrone } from './planes.js';
3
4
  /**
@@ -87,13 +88,21 @@ export class CombatExchange {
87
88
  // not every exchange of it -- "fresh" meaning this pairing wasn't
88
89
  // already trading blows moments ago. Armed here (before the opponent
89
90
  // bookkeeping overwrites the evidence), cashed in by rollInitiative.
91
+ // The same freshness check prices a SPIRIT companion's combat: one
92
+ // service per FIGHT joined (RAW p.302), ticked when the fight opens.
90
93
  const FIGHT_GAP_MS = 60000;
91
94
  for (const [combatant, other] of [[this.actor, target], [target, this.actor]]) {
92
- if (!combatant.hasQuality('combat-paralysis'))
93
- continue;
94
95
  const freshFight = combatant.combatOpponent !== other || now - combatant.lastExchangeAt > FIGHT_GAP_MS;
95
- if (freshFight)
96
+ if (freshFight && combatant.hasQuality('combat-paralysis'))
96
97
  combatant.paralysisFreeze = true;
98
+ if (freshFight) {
99
+ const spiritEntry = this.scene.ownerGame?.companions?.find(c => c.npc === combatant && c.kind === 'spirit');
100
+ if (spiritEntry && (spiritEntry.services ?? 0) > 0) {
101
+ spiritEntry.services = (spiritEntry.services ?? 0) - 1;
102
+ // Resource arithmetic -- Mechanics ticker (readability pass).
103
+ this.logger.meta(`👥 ${combatant.name} joins the fight -- one service spent (${spiritEntry.services} left).`, 'green');
104
+ }
105
+ }
97
106
  }
98
107
  this.actor.combatOpponent = target;
99
108
  this.actor.lastExchangeAt = now;
@@ -161,12 +170,22 @@ export class CombatExchange {
161
170
  const net = Math.abs(aggRoll.hits - defRoll.hits);
162
171
  const extraStriker = net >= 3 ? first : null;
163
172
  this.logger.write(`Initiative: ${aggressor.name} ${aggRoll.hits} vs ${defender.name} ${defRoll.hits} -- ${first.name} first${extraStriker ? ' with an extra strike' : ''}.`);
173
+ // Readability split: the dice anatomy is Mechanics-ticker material
174
+ // (metaLines); the world log keeps only what the player FEELS --
175
+ // who froze, who moves first (lines). Callers rebuild AI-history
176
+ // reports as [...lines, ...metaLines] so NPC memories keep the dice.
177
+ // One line per roller (playtest note: the single "A vs B" line was
178
+ // hard to read) -- each carries its own right-justified wave.
179
+ const metaLines = [
180
+ `Initiative:`,
181
+ ` ${aggressor.name}: ${formatRoll(aggRoll)}`,
182
+ ` ${defender.name}: ${formatRoll(defRoll)}`,
183
+ ];
164
184
  const lines = [
165
185
  ...freezeLines,
166
- `Initiative: ${aggressor.name} ${formatRoll(aggRoll)} vs ${defender.name} ${formatRoll(defRoll)}`,
167
- ` ${first.name} moves first.${extraStriker ? ` And they're FAST -- an extra strike this exchange.` : ''}`,
186
+ `${first.name} moves first.${extraStriker ? ` And they're FAST -- an extra strike this exchange.` : ''}`,
168
187
  ];
169
- return { first, second, extraStriker, lines };
188
+ return { first, second, extraStriker, lines, metaLines };
170
189
  }
171
190
  /**
172
191
  * The player should see a fight's blow-by-blow only when they're actually
@@ -190,10 +209,16 @@ export class CombatExchange {
190
209
  * fight an NPC started. The status box refreshes with every block so the
191
210
  * condition monitor visibly ticks as damage lands.
192
211
  */
193
- async announce(lines) {
194
- if (lines.length === 0 || !this.playerWitnesses())
212
+ async announce(lines, meta = []) {
213
+ if ((lines.length === 0 && meta.length === 0) || !this.playerWitnesses())
195
214
  return;
196
- this.logger.log(lines.join('\n'));
215
+ // Dice anatomy ticks in the Mechanics panel; the world log gets the
216
+ // narrative behind a leading blank line -- the per-block separator
217
+ // the old wall-of-text never had (readability pass).
218
+ for (const m of meta)
219
+ this.logger.meta(m);
220
+ if (lines.length > 0)
221
+ this.logger.log(`\n${lines.join('\n')}`);
197
222
  this.scene.updateStatus();
198
223
  await new Promise(resolve => setTimeout(resolve, CombatExchange.BEAT_MS));
199
224
  }
@@ -254,10 +279,20 @@ export class CombatExchange {
254
279
  dealStun: fistsAreStun,
255
280
  };
256
281
  }
257
- /** One directed strike: attack vs defense, then soak. Mutates the defender. */
282
+ /**
283
+ * One directed strike: attack vs defense, then soak. Mutates the
284
+ * defender. Readability split: `meta` carries the dice anatomy
285
+ * (Attack/Defense/Soak rolls, net-hit math) for the Mechanics ticker;
286
+ * `world` carries the narrative -- one compact header with the
287
+ * outcome, then the damage/flavor lines the player feels. Callers
288
+ * rebuild AI-history reports as [...world, ...meta] so an NPC's
289
+ * memory of the fight keeps the dice.
290
+ */
258
291
  resolveStrike(attacker, defender, profile) {
259
292
  const strike = profile ?? this.weaponProfile(attacker, defender);
260
- const lines = [];
293
+ const world = [];
294
+ const meta = [];
295
+ const lines = world; // the damage branches below narrate into the world
261
296
  // Firing costs a round; the last one gets a warning line.
262
297
  if (strike.weaponItem?.isFirearm()) {
263
298
  strike.weaponItem.fireRound();
@@ -265,29 +300,61 @@ export class CombatExchange {
265
300
  }
266
301
  const attack = rollPool(strike.pool);
267
302
  const defense = rollPool(defender.getDefensePool());
268
- lines.push(`${attacker.name} attacks ${defender.name} ${strike.label}:`);
269
- lines.push(` Attack: ${formatRoll(attack)}`);
270
- lines.push(` Defense: ${formatRoll(defense)}`);
303
+ const header = `${attacker.name} attacks ${defender.name} ${strike.label}`;
304
+ meta.push(`${header}:`);
305
+ meta.push(` Attack: ${formatRoll(attack)}`);
306
+ meta.push(` Defense: ${formatRoll(defense)}`);
271
307
  const dryWarning = strike.weaponItem?.isFirearm() && strike.weaponItem.ammo === 0
272
308
  ? [` ${attacker.name}'s ${strike.weaponItem.name} clicks empty -- that was the last round.`]
273
309
  : [];
274
310
  const netHits = attack.hits - defense.hits;
275
311
  if (attack.criticalGlitch) {
276
- lines.push(` ${attacker.name}'s attack goes catastrophically wrong -- a clean miss, and everyone nearby heard it.`);
277
- return [...lines, ...dryWarning];
312
+ world.push(`${header} -- and it goes catastrophically wrong: a clean miss, and everyone nearby heard it.`);
313
+ return { world: [...world, ...dryWarning], meta };
278
314
  }
279
315
  if (netHits <= 0) {
280
- lines.push(` ${defender.name} evades the attack.`);
281
- return [...lines, ...dryWarning];
316
+ world.push(`${header} -- ${defender.name} evades.`);
317
+ return { world: [...world, ...dryWarning], meta };
282
318
  }
283
319
  const incoming = strike.damageBase + netHits;
284
320
  const soak = rollPool(strike.soakPool);
285
321
  const dealt = Math.max(0, incoming - soak.hits);
286
- lines.push(` Hit! ${netHits} net -- ${incoming} damage incoming.`);
287
- lines.push(` Soak: ${formatRoll(soak)}`);
322
+ meta.push(` Hit! ${netHits} net -- ${incoming} damage incoming.`);
323
+ meta.push(` Soak: ${formatRoll(soak)}`);
288
324
  if (dealt === 0) {
289
- lines.push(` ${defender.name} shrugs it off -- no damage gets through.`);
290
- return [...lines, ...dryWarning];
325
+ world.push(`${header} -- ${defender.name} shrugs it off; nothing gets through.`);
326
+ return { world: [...world, ...dryWarning], meta };
327
+ }
328
+ world.push(`${header} -- HIT:`);
329
+ // A COMPANION SHELL routes damage into its persistent DEVICE: an
330
+ // autonomous drone's hull (Item.droneDamage), an agent's deck (RAW
331
+ // p.246: attacks on the agent damage the deck -- shared monitor).
332
+ // No biofeedback -- nobody's brain rides a dog-brain. When the
333
+ // device dies, the shell's own track is filled so the caller's
334
+ // normal death flow (resolveDeath -> removeActor -> onActorRemoved)
335
+ // fires without a parallel path.
336
+ if (defender instanceof NPC && defender.boundDevice) {
337
+ const device = defender.boundDevice;
338
+ if (device.isDrone()) {
339
+ device.takeDroneDamage(dealt);
340
+ this.logger.write(`${attacker.name} dealt ${dealt} HULL damage to companion ${defender.name} (${device.droneDamage}/${device.maxDroneBoxes}).`);
341
+ lines.push(` ${defender.name} takes ${dealt} box${dealt === 1 ? '' : 'es'} to the airframe: ${device.droneSummary()}`);
342
+ if (device.isWrecked) {
343
+ defender.takeDamage(defender.maxConditionBoxes);
344
+ lines.push(` The frame comes apart -- rotors, casing, sparks.`);
345
+ }
346
+ return { world: [...lines, ...dryWarning], meta };
347
+ }
348
+ if (device.isCyberdeck()) {
349
+ device.takeDeckDamage(dealt);
350
+ this.logger.write(`${attacker.name} dealt ${dealt} MATRIX damage to agent ${defender.name}'s host deck (${device.deckDamage}/${device.maxDeckBoxes}).`);
351
+ lines.push(` The hit lands on the deck the agent runs on: ${device.deckSummary()}`);
352
+ if (device.isBricked) {
353
+ defender.takeDamage(defender.maxConditionBoxes);
354
+ lines.push(` The deck BRICKS -- the agent derezzes mid-instruction.`);
355
+ }
356
+ return { world: [...lines, ...dryWarning], meta };
357
+ }
291
358
  }
292
359
  // A JUMPED-IN RIGGER's presence is the airframe: every box lands on
293
360
  // the drone's hull monitor, and HALF (rounded up) bleeds through the
@@ -315,7 +382,7 @@ export class CombatExchange {
315
382
  lines.push(` ${drone.name} comes apart mid-air -- rotors, casing, sparks --`);
316
383
  lines.push(...leaveDrone(this.scene, defender, { forced: true, reason: `The airframe dies around you.` }));
317
384
  }
318
- return [...lines, ...dryWarning];
385
+ return { world: [...lines, ...dryWarning], meta };
319
386
  }
320
387
  // A jacked-in visitor takes MATRIX damage (only visitors have a
321
388
  // simMode -- native ice's normal track IS its integrity, so it takes
@@ -344,7 +411,7 @@ export class CombatExchange {
344
411
  lines.push(` ${deck.name} BRICKS -- the persona collapses into static --`);
345
412
  lines.push(...leaveMatrix(this.scene, defender, { forced: true, reason: `The ice burns your deck out from under you.` }));
346
413
  }
347
- return [...lines, ...dryWarning];
414
+ return { world: [...lines, ...dryWarning], meta };
348
415
  }
349
416
  // Deckless: the living persona. Fall through to the stun branch.
350
417
  }
@@ -363,7 +430,7 @@ export class CombatExchange {
363
430
  this.logger.write(`${attacker.name} dealt ${dealt} boxes to ${defender.name} (${defender.damageTaken}/${defender.maxConditionBoxes}).`);
364
431
  lines.push(` ${defender.name} takes ${dealt} box${dealt === 1 ? '' : 'es'}: ${defender.conditionSummary()}`);
365
432
  }
366
- return [...lines, ...dryWarning];
433
+ return { world: [...lines, ...dryWarning], meta };
367
434
  }
368
435
  /**
369
436
  * Death/knockout narration is announced BEFORE the removal side effects
@@ -374,6 +441,23 @@ export class CombatExchange {
374
441
  * full STUN track -- see cast.ts drain) rather than a death: same
375
442
  * out-of-the-run consequences, honestly narrated as unconsciousness.
376
443
  */
444
+ /**
445
+ * The PLAYER dropped from a full STUN track: out of the fight, not out
446
+ * of the game (RAW p.379 "out of action for the rest of the fight";
447
+ * instant death is physical overflow only, p.170). No endGame here --
448
+ * Scene.beginPlayerKnockout defers judgment until the room settles:
449
+ * hostiles all down/surrendered/gone means the player comes to with a
450
+ * Body + Willpower recovery (p.207); a hostile left standing over them
451
+ * with nobody fighting for them means the street decides, as ever.
452
+ */
453
+ async resolvePlayerKnockout(killer) {
454
+ const p = this.scene.getPlayer();
455
+ this.logger.write(`CombatExchange: ${killer.name} knocked out ${p.name} (stun-full) -- judgment deferred to the room.`);
456
+ const lines = [`The world tilts and goes dark -- you're out cold on the floor, breathing shallow. The fight goes on without you.`];
457
+ await this.announce(lines);
458
+ this.scene.beginPlayerKnockout([killer.name]);
459
+ return ['', ...lines];
460
+ }
377
461
  async resolveDeath(fallen, killer, opts) {
378
462
  const room = fallen.currentLocation;
379
463
  const isPlayer = fallen === this.scene.getPlayer();
@@ -405,9 +489,22 @@ export class CombatExchange {
405
489
  });
406
490
  return ['', ...lines];
407
491
  }
408
- const npcFall = (opts?.fatal ?? true)
409
- ? `${fallen.name} goes down -- dead before they hit the floor. Their gear scatters across ${room.name}.`
410
- : `${fallen.name} collapses, out cold -- down for the count. Their gear scatters across ${room.name}.`;
492
+ // Companions don't die like meat. A spirit's condition track filling
493
+ // is a DISRUPTION -- the vessel ruptures and the spirit snaps back to
494
+ // its own plane (SR5e p.303). An agent derezzes with the deck it runs
495
+ // on, and a drone shell is just machinery coming apart -- none of
496
+ // them have a corpse or gear to scatter (a real session's ice kill
497
+ // read "gear scatters across Grid Vault" for a program).
498
+ const companionKind = fallen instanceof NPC ? fallen.companionKind : undefined;
499
+ const npcFall = companionKind === 'spirit'
500
+ ? `${fallen.name} ruptures -- a pressure-drop like the room exhaling -- DISRUPTED, snapped back to its own plane. The binding is over.`
501
+ : companionKind === 'agent'
502
+ ? `${fallen.name} DEREZZES -- its icon shears into static and rains out of the grid, gone with the deck that ran it.`
503
+ : companionKind === 'drone'
504
+ ? `${fallen.name} comes apart -- rotors, plating, and sparks skidding across ${room.name}. The frame is wrecked.`
505
+ : (opts?.fatal ?? true)
506
+ ? `${fallen.name} goes down -- dead before they hit the floor. Their gear scatters across ${room.name}.`
507
+ : `${fallen.name} collapses, out cold -- down for the count. Their gear scatters across ${room.name}.`;
411
508
  const lines = [opts?.fallLine ?? npcFall];
412
509
  await this.announce(lines);
413
510
  // scene.removeActor drops everything they carried AND wore where they
@@ -432,6 +529,9 @@ export class CombatExchange {
432
529
  // The fight autopilot's trigger: a settled exchange involving the
433
530
  // player schedules their next auto action (see utilities/auto-fight.ts).
434
531
  this.scene.notifyExchangeSettled(this._combatants);
532
+ // A spirit whose services ran out departs once the dust settles
533
+ // (RAW p.302: the bargain is spent).
534
+ this.scene.ownerGame?.settleSpiritServices?.();
435
535
  if (this.actor === this.scene.getPlayer()) {
436
536
  return '';
437
537
  }