@maka/maka-cli 5.117.0 → 5.119.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 (159) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +6 -1
  3. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js.map +1 -1
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +9 -2
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/compile.js +3 -0
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/compile.js.map +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/deck.d.ts +14 -2
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/deck.js +73 -38
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/deck.js.map +1 -1
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/decompile.js +3 -6
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/decompile.js.map +1 -1
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/docwagon.js +1 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/docwagon.js.map +1 -1
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/download.js +4 -3
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/download.js.map +1 -1
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/enter-host.d.ts +1 -1
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/enter-host.js +10 -8
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/enter-host.js.map +1 -1
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +17 -7
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js.map +1 -1
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.d.ts +10 -0
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js +72 -20
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js.map +1 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +8 -3
  26. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  27. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +30 -14
  28. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  29. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js +14 -2
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js.map +1 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/hop.d.ts +16 -0
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/hop.js +95 -0
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/hop.js.map +1 -0
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +6 -0
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js.map +1 -1
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js +3 -1
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js.map +1 -1
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/jackpoint.js +5 -7
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/jackpoint.js.map +1 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +24 -4
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +1 -1
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/mark.js +20 -6
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/mark.js.map +1 -1
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js +7 -6
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js.map +1 -1
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/reload.js +6 -3
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/reload.js.map +1 -1
  50. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +3 -0
  51. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
  52. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +8 -5
  53. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
  54. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +4 -3
  55. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
  56. package/bundle/typescript/src/commands/game/sideQuest/commands/sin.js +11 -3
  57. package/bundle/typescript/src/commands/game/sideQuest/commands/sin.js.map +1 -1
  58. package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js +5 -3
  59. package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js.map +1 -1
  60. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +20 -2
  61. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
  62. package/bundle/typescript/src/commands/game/sideQuest/commands/tap.js +1 -0
  63. package/bundle/typescript/src/commands/game/sideQuest/commands/tap.js.map +1 -1
  64. package/bundle/typescript/src/commands/game/sideQuest/commands/thread.js +2 -5
  65. package/bundle/typescript/src/commands/game/sideQuest/commands/thread.js.map +1 -1
  66. package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
  67. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +17 -1
  68. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
  69. package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.d.ts +6 -0
  70. package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.js +17 -4
  71. package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.js.map +1 -1
  72. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +1 -0
  73. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  74. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +2 -0
  75. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
  76. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +22 -0
  77. package/bundle/typescript/src/commands/game/sideQuest/game.js +132 -29
  78. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  79. package/bundle/typescript/src/commands/game/sideQuest/models/host.d.ts +80 -0
  80. package/bundle/typescript/src/commands/game/sideQuest/models/host.js +98 -0
  81. package/bundle/typescript/src/commands/game/sideQuest/models/host.js.map +1 -0
  82. package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +50 -1
  83. package/bundle/typescript/src/commands/game/sideQuest/models/item.js +78 -2
  84. package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
  85. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +133 -3
  86. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +381 -85
  87. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  88. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +40 -9
  89. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +73 -12
  90. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  91. package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +3 -0
  92. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +3 -0
  93. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
  94. package/bundle/typescript/src/commands/game/sideQuest/qualities.d.ts +34 -1
  95. package/bundle/typescript/src/commands/game/sideQuest/qualities.js +58 -26
  96. package/bundle/typescript/src/commands/game/sideQuest/qualities.js.map +1 -1
  97. package/bundle/typescript/src/commands/game/sideQuest/types/player-equipment.d.ts +8 -12
  98. package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +14 -1
  99. package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +1 -0
  100. package/bundle/typescript/src/commands/game/sideQuest/types/seed/scene-seed.d.ts +2 -0
  101. package/bundle/typescript/src/commands/game/sideQuest/types/shared/equipment-enum.d.ts +22 -16
  102. package/bundle/typescript/src/commands/game/sideQuest/types/shared/equipment-enum.js +22 -16
  103. package/bundle/typescript/src/commands/game/sideQuest/types/shared/equipment-enum.js.map +1 -1
  104. package/bundle/typescript/src/commands/game/sideQuest/ui.js +2 -2
  105. package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
  106. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +13 -8
  107. package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js.map +1 -1
  108. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.d.ts +1 -1
  109. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js +8 -7
  110. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js.map +1 -1
  111. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +61 -16
  112. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  113. package/bundle/typescript/src/commands/game/sideQuest/utilities/condition-report.d.ts +23 -0
  114. package/bundle/typescript/src/commands/game/sideQuest/utilities/condition-report.js +111 -11
  115. package/bundle/typescript/src/commands/game/sideQuest/utilities/condition-report.js.map +1 -1
  116. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.d.ts +5 -1
  117. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js +5 -27
  118. package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js.map +1 -1
  119. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-reach.js +2 -1
  120. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-reach.js.map +1 -1
  121. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.d.ts +11 -0
  122. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js +56 -8
  123. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js.map +1 -1
  124. package/bundle/typescript/src/commands/game/sideQuest/utilities/grids.d.ts +72 -0
  125. package/bundle/typescript/src/commands/game/sideQuest/utilities/grids.js +100 -0
  126. package/bundle/typescript/src/commands/game/sideQuest/utilities/grids.js.map +1 -0
  127. package/bundle/typescript/src/commands/game/sideQuest/utilities/harm-note.d.ts +25 -0
  128. package/bundle/typescript/src/commands/game/sideQuest/utilities/harm-note.js +10 -0
  129. package/bundle/typescript/src/commands/game/sideQuest/utilities/harm-note.js.map +1 -0
  130. package/bundle/typescript/src/commands/game/sideQuest/utilities/harm-settlement.d.ts +29 -0
  131. package/bundle/typescript/src/commands/game/sideQuest/utilities/harm-settlement.js +64 -0
  132. package/bundle/typescript/src/commands/game/sideQuest/utilities/harm-settlement.js.map +1 -0
  133. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic-actors.d.ts +9 -0
  134. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic-actors.js +98 -0
  135. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic-actors.js.map +1 -0
  136. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.d.ts +3 -2
  137. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.js +34 -6
  138. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.js.map +1 -1
  139. package/bundle/typescript/src/commands/game/sideQuest/utilities/marks.d.ts +42 -2
  140. package/bundle/typescript/src/commands/game/sideQuest/utilities/marks.js +75 -4
  141. package/bundle/typescript/src/commands/game/sideQuest/utilities/marks.js.map +1 -1
  142. package/bundle/typescript/src/commands/game/sideQuest/utilities/matrix-roster.d.ts +62 -0
  143. package/bundle/typescript/src/commands/game/sideQuest/utilities/matrix-roster.js +97 -0
  144. package/bundle/typescript/src/commands/game/sideQuest/utilities/matrix-roster.js.map +1 -0
  145. package/bundle/typescript/src/commands/game/sideQuest/utilities/overwatch.js +3 -3
  146. package/bundle/typescript/src/commands/game/sideQuest/utilities/overwatch.js.map +1 -1
  147. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.d.ts +1 -3
  148. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +85 -7
  149. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js.map +1 -1
  150. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +10 -0
  151. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +39 -23
  152. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  153. package/bundle/typescript/src/commands/game/sideQuest/utilities/programs.d.ts +16 -0
  154. package/bundle/typescript/src/commands/game/sideQuest/utilities/programs.js +12 -0
  155. package/bundle/typescript/src/commands/game/sideQuest/utilities/programs.js.map +1 -1
  156. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.d.ts +2 -0
  157. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.js +23 -4
  158. package/bundle/typescript/src/commands/game/sideQuest/utilities/sin.js.map +1 -1
  159. package/package.json +1 -1
@@ -1,4 +1,10 @@
1
+ import { agentRatingFromName } from './utilities/programs.js';
1
2
  import blessed from 'blessed';
3
+ import { hostLabel } from './utilities/grid-names.js';
4
+ import { marksPlaced, marksOnYou } from './utilities/marks.js';
5
+ import { settleUnresolvedHarm } from './utilities/harm-settlement.js';
6
+ import { hostInteriorPanel } from './utilities/grid-view.js';
7
+ import { DEFAULT_GRID_PROVIDER, PUBLIC_GRID, globalGrid } from './utilities/grids.js';
2
8
  import { hostsInReach, hostDockLine, gridVicinity } from './utilities/grid-reach.js';
3
9
  import { nameThem, scrubHandles } from './utilities/identity.js';
4
10
  import { dirname } from 'path';
@@ -27,7 +33,7 @@ import { rollPool, formatRoll } from './utilities/dice.js';
27
33
  import { Direction } from './types/shared/direction-enum.js';
28
34
  import { Logger } from './utilities/logger.js';
29
35
  import { runHostTurn } from './utilities/ic.js';
30
- import { leaveMatrix } from './utilities/planes.js';
36
+ import { leaveMatrix, endPersona } from './utilities/planes.js';
31
37
  import { CommandQueue } from './utilities/command-queue.js';
32
38
  import { currentSession } from './utilities/session-context.js';
33
39
  import { PlayersCommand } from './commands/players.js';
@@ -66,6 +72,7 @@ import { EquipmentCommand } from './commands/equipment.js';
66
72
  import { MapCommand } from './commands/map.js';
67
73
  import { SilentCommand } from './commands/silent.js';
68
74
  import { EnterHostCommand, ExitHostCommand } from './commands/enter-host.js';
75
+ import { HopCommand } from './commands/hop.js';
69
76
  import { RestCommand } from './commands/rest.js';
70
77
  import { ReloadCommand } from './commands/reload.js';
71
78
  import { SubdueCommand } from './commands/subdue.js';
@@ -1295,6 +1302,55 @@ export default class Game {
1295
1302
  return npc;
1296
1303
  }
1297
1304
  _remoteFixer;
1305
+ /**
1306
+ * The live-sheet read of everything this runner has out (contract
1307
+ * 2026-09-02): the ledger's companions plus the drone they are jumped
1308
+ * into. `where` is the room, or the host/grid for a construct;
1309
+ * `damage` is the thing's OWN track -- a drone's hull, an agent's deck
1310
+ * (shared monitor, p.246), a spirit's or sprite's shell.
1311
+ */
1312
+ companionsForBeat(player) {
1313
+ const whereOf = (a) => a.plane === 'matrix'
1314
+ ? (a.hostInside ? hostLabel(a.hostInside.over, { capital: true }) : (a.currentGrid?.name ?? 'the grid'))
1315
+ : a.currentLocation?.name;
1316
+ const out = [];
1317
+ for (const c of this.companions) {
1318
+ const dev = c.boundDevice;
1319
+ const services = typeof c.services === 'number' ? `, ${c.services} service${c.services === 1 ? '' : 's'}` : '';
1320
+ const line = c.kind === 'drone' && dev ? `Pilot ${2 + dev.qualityBonus}, hull ${dev.droneSummary()}`
1321
+ : c.kind === 'agent' && dev ? `rating ${c.force} on ${dev.name} ${dev.deckSummary()}`
1322
+ : c.kind === 'sprite' ? `${c.spriteType ?? 'sprite'} level ${c.force}${services}${c.registered ? ', registered' : ''}`
1323
+ : c.kind === 'spirit' ? `Force ${c.force}${services}`
1324
+ : c.npc.archetypeName ?? undefined;
1325
+ const damage = c.kind === 'drone' && dev ? { taken: dev.droneDamage, max: dev.maxDroneBoxes }
1326
+ : c.kind === 'agent' && dev ? { taken: dev.deckDamage, max: dev.maxDeckBoxes }
1327
+ : { taken: c.npc.damageTaken, max: c.npc.maxConditionBoxes };
1328
+ out.push({ name: dev?.name ?? c.npc.name, kind: c.kind, deployed: true, where: whereOf(c.npc), line, damage });
1329
+ }
1330
+ if (player.riggedDrone) {
1331
+ const d = player.riggedDrone;
1332
+ out.push({ name: d.name, kind: 'drone', deployed: true, where: `jumped in${player.bodyRoom ? ` from ${player.bodyRoom.name}` : ''}`, line: `hull ${d.droneSummary()}`, damage: { taken: d.droneDamage, max: d.maxDroneBoxes } });
1333
+ }
1334
+ // STOWED (addendum 2026-09-02: "and when they are deployed or not"): the
1335
+ // roster the runner OWNS and has not sent out -- drones in the bag, the
1336
+ // deck's agent, registered sprites waiting in the Resonance. Spirits
1337
+ // exist only while summoned, so none is ever stowed.
1338
+ for (const d of player.inventory.getAllItems().filter(i => i.isDrone())) {
1339
+ if (out.some(o => o.name === d.name))
1340
+ continue;
1341
+ out.push({ name: d.name, kind: 'drone', deployed: false, line: `Pilot ${2 + d.qualityBonus}, hull ${d.droneSummary()}${d.isWrecked ? ' -- WRECKED' : ''}`, damage: { taken: d.droneDamage, max: d.maxDroneBoxes } });
1342
+ }
1343
+ const deck = player.getAnyCyberdeck();
1344
+ if (deck && !this.companions.some(c => c.kind === 'agent')) {
1345
+ const chips = player.inventory.getAllItems().map(i => agentRatingFromName(i.name)).filter((r) => r !== undefined);
1346
+ const rating = Math.max(3 + deck.qualityBonus, ...(chips.length ? chips : [0]));
1347
+ out.push({ name: `Agent (${deck.name})`, kind: 'agent', deployed: false, line: `rating ${rating} on ${deck.name} ${deck.deckSummary()}`, damage: { taken: deck.deckDamage, max: deck.maxDeckBoxes } });
1348
+ }
1349
+ for (const sp of this.registeredSpritesWaiting()) {
1350
+ out.push({ name: sp.name, kind: 'sprite', deployed: false, line: `${sp.spriteType} level ${sp.level}, ${sp.tasks} task${sp.tasks === 1 ? '' : 's'}, registered` });
1351
+ }
1352
+ return out;
1353
+ }
1298
1354
  spawnCompanionShell(opts) {
1299
1355
  if (this.scene.getActor(opts.name)) {
1300
1356
  Logger.getInstance().write(`spawnCompanionShell: name "${opts.name}" already in scene -- refusing.`);
@@ -1312,6 +1368,7 @@ export default class Game {
1312
1368
  this.scene.addActor(npc);
1313
1369
  this.scene.setCharacterLocation(npc, opts.room.name);
1314
1370
  npc.scene = this.scene;
1371
+ this.player.noteConditionChanged(); // the crew rides the beat (companions)
1315
1372
  this.companions.push({ npc, kind: opts.kind, services: opts.services, force: opts.force, boundDevice: opts.boundDevice });
1316
1373
  Logger.getInstance().write(`Companion spawned: ${opts.name} (${opts.kind}, force ${opts.force}) in ${opts.room.name}.`);
1317
1374
  return npc;
@@ -1364,6 +1421,7 @@ export default class Game {
1364
1421
  const idx = this.companions.indexOf(entry);
1365
1422
  if (idx >= 0)
1366
1423
  this.companions.splice(idx, 1);
1424
+ this.player.noteConditionChanged(); // the crew rides the beat (companions)
1367
1425
  Logger.getInstance().write(`Companion dismissed: ${entry.npc.name} -- ${reason}`);
1368
1426
  }
1369
1427
  /**
@@ -1425,6 +1483,7 @@ export default class Game {
1425
1483
  const idx = this.companions.indexOf(entry);
1426
1484
  if (idx >= 0)
1427
1485
  this.companions.splice(idx, 1);
1486
+ this.player.noteConditionChanged(); // the crew rides the beat (companions)
1428
1487
  if (entry.kind === 'drone' && entry.boundDevice && !entry.npc.deathClaimed) {
1429
1488
  entry.boundDevice.takeDroneDamage(entry.boundDevice.maxDroneBoxes);
1430
1489
  Logger.getInstance().logWithColor(`👥 The ${entry.boundDevice.name} is WRECKED where it fell -- rest to repair the frame.`, 'yellow');
@@ -1659,6 +1718,8 @@ export default class Game {
1659
1718
  // and while the tab stands, rest at the hideout recovers half (rest.ts;
1660
1719
  // "pay" squares it, pay.ts; "lifestyle" views and moves tiers).
1661
1720
  lifestyleTier = 'Squatter';
1721
+ /** The Big Ten grid a High lifestyle rides (SR5 p.222). A placeholder default until chargen offers the pick (grids.ts). */
1722
+ gridProvider = DEFAULT_GRID_PROVIDER;
1662
1723
  rentDebt = 0;
1663
1724
  /** The active lifestyle tier's row (falls back to Squatter). */
1664
1725
  get lifestyle() {
@@ -2213,19 +2274,26 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2213
2274
  // The one-word role on the live sheet's actor entries: the contact
2214
2275
  // ledger's word, else "fence" when the room's fence stands here,
2215
2276
  // else nothing (utilities/condition-report.ts setActorRoleResolver).
2216
- void import('./utilities/condition-report.js').then(m => m.setActorRoleResolver(a => {
2217
- const contact = this.contacts.get(a.name)?.role;
2218
- if (contact)
2219
- return contact;
2220
- // The same classifier the ledger uses, on anyone -- minus its
2221
- // "Street Contact" default, which is a ledger word, not a role: an
2222
- // extra it cannot place carries nothing rather than a wrong word.
2223
- const classified = Game.roleOf(a);
2224
- if (classified !== 'Street Contact')
2225
- return classified;
2226
- const room = a.currentLocation;
2227
- return room && isFence(a, room) ? 'Fence' : undefined;
2228
- })).catch(() => undefined);
2277
+ void import('./utilities/condition-report.js').then(m => {
2278
+ // The marks this persona HOLDS (contract 2026-09-02, web accepted first):
2279
+ // the same names the "marks" listing prints.
2280
+ m.setMarksPlacedResolver(p => marksPlaced(this.scene, p.name));
2281
+ m.setMarksOnYouResolver(p => marksOnYou(this.scene, p));
2282
+ m.setCompanionsResolver(p => this.companionsForBeat(p));
2283
+ return m.setActorRoleResolver(a => {
2284
+ const contact = this.contacts.get(a.name)?.role;
2285
+ if (contact)
2286
+ return contact;
2287
+ // The same classifier the ledger uses, on anyone -- minus its
2288
+ // "Street Contact" default, which is a ledger word, not a role: an
2289
+ // extra it cannot place carries nothing rather than a wrong word.
2290
+ const classified = Game.roleOf(a);
2291
+ if (classified !== 'Street Contact')
2292
+ return classified;
2293
+ const room = a.currentLocation;
2294
+ return room && isFence(a, room) ? 'Fence' : undefined;
2295
+ });
2296
+ }).catch(() => undefined);
2229
2297
  }
2230
2298
  registerCommands() {
2231
2299
  CommandFactory.registerCommand('look', LookCommand);
@@ -2287,6 +2355,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2287
2355
  // branch ROUTES a bare "exit" to this command while the persona is
2288
2356
  // inside a host, and 'exit-host' is the always-reachable name.
2289
2357
  CommandFactory.registerCommand('enter', EnterHostCommand);
2358
+ CommandFactory.registerCommand('hop', HopCommand);
2290
2359
  CommandFactory.registerCommand('exit-host', ExitHostCommand);
2291
2360
  CommandFactory.registerCommand('rest', RestCommand);
2292
2361
  CommandFactory.registerCommand('reload', ReloadCommand);
@@ -2662,6 +2731,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2662
2731
  throw new Error(`Scene generation failed: ${err.message}`);
2663
2732
  }
2664
2733
  this.scene.initialize(this);
2734
+ this.resolveHostGrids();
2665
2735
  this.player.currentLocation = this.determineStartRoom();
2666
2736
  this.applySeedStartSpot(this.sceneSeed);
2667
2737
  this.registerCommands();
@@ -2862,6 +2932,16 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2862
2932
  this._resumedAt = room;
2863
2933
  }
2864
2934
  /** The location a save carries: the current hub room and spot. */
2935
+ /** Each host's seeded grid word ('public' | 'local' | 'global:<corp>') against this scene's local grid. */
2936
+ resolveHostGrids() {
2937
+ for (const room of Object.values(this.scene.getRooms())) {
2938
+ const host = room.host;
2939
+ if (!host?.gridSeed)
2940
+ continue;
2941
+ const w = host.gridSeed.trim().toLowerCase();
2942
+ host.grid = w === 'public' ? PUBLIC_GRID : w === 'local' ? this.scene.localGrid : w.startsWith('global:') ? globalGrid(host.gridSeed.slice('global:'.length)) : undefined;
2943
+ }
2944
+ }
2865
2945
  hubLocationSnapshot() {
2866
2946
  // The BODY's room, never the persona's: jacked in, currentLocation is
2867
2947
  // wherever the persona stands (a host's room inside one), and a resume
@@ -2903,6 +2983,8 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2903
2983
  // local file never knew -- that is the cloud repair's job, and the
2904
2984
  // sync protocol's, see the repair-visibility ruling in cloud-saves.)
2905
2985
  this.lifestyleTier = canonicalLifestyleTier(save.game.lifestyleTier) ?? 'Street';
2986
+ if (typeof save.game.gridProvider === 'string' && save.game.gridProvider.trim())
2987
+ this.gridProvider = save.game.gridProvider;
2906
2988
  this.rentDebt = save.game.rentDebt;
2907
2989
  this.syncHomeToLifestyle();
2908
2990
  this.player.lifestyleEdgeBonus = this.lifestyle.edgeBonus;
@@ -3169,6 +3251,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
3169
3251
  player: serializePlayer(this.player),
3170
3252
  game: {
3171
3253
  location: this.hubLocationSnapshot(),
3254
+ gridProvider: this.gridProvider,
3172
3255
  lifestyleTier: this.lifestyleTier,
3173
3256
  rentDebt: this.rentDebt,
3174
3257
  tier: this._tier,
@@ -3417,7 +3500,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
3417
3500
  this.player.linkLockedBy.clear();
3418
3501
  // Same enumeration, same reason: insideHost is persona state and these
3419
3502
  // paths end the persona without passing through planes.leaveMatrix.
3420
- this.player.insideHost = undefined;
3503
+ endPersona(this.player);
3421
3504
  this.player.astralTicks = 0;
3422
3505
  this.player.activeDeck = undefined;
3423
3506
  this.player.riggedDrone = undefined;
@@ -3521,7 +3604,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
3521
3604
  this.player.linkLockedBy.clear();
3522
3605
  // Same enumeration, same reason: insideHost is persona state and these
3523
3606
  // paths end the persona without passing through planes.leaveMatrix.
3524
- this.player.insideHost = undefined;
3607
+ endPersona(this.player);
3525
3608
  this.player.astralTicks = 0;
3526
3609
  this.player.activeDeck = undefined;
3527
3610
  this.player.riggedDrone = undefined;
@@ -3973,7 +4056,11 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
3973
4056
  // forward), and no blocking AI-redressing pass either -- the chunked
3974
4057
  // rooms pass already wrote this scene's prose.
3975
4058
  this.scene = await SceneSynthesizer.synthesizeFromJson(seed, this.player, false, false);
4059
+ // A run happens in the same metroplex: it rides the hub's local grid.
4060
+ if (this._hubScene)
4061
+ this.scene.localGrid = this._hubScene.localGrid;
3976
4062
  this.scene.initialize(this);
4063
+ this.resolveHostGrids();
3977
4064
  this.player.currentLocation = this.determineStartRoom();
3978
4065
  this.applySeedStartSpot(seed);
3979
4066
  // New job -- and the needs come due at the door (see
@@ -4842,7 +4929,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
4842
4929
  // each under the picture it belongs to, so the HUD stops repeating
4843
4930
  // what two panels already show. The breadcrumb went with it -- the
4844
4931
  // district map draws the way you came far better than a text trail.
4845
- const stanceColor = player.autoStance === 'aggressive' ? 'red' : player.autoStance === 'guarded' ? 'yellow' : 'white';
4932
+ const stanceColor = player.autoStance === 'lethal' ? 'red' : player.autoStance === 'non-lethal' ? 'yellow' : 'white';
4846
4933
  // WHAT'S IN YOUR HANDS rides alongside the stance (player ruling
4847
4934
  // 2026-08-25). The two answer the same question -- how ready are you
4848
4935
  // -- and the stance line was left short and lonely once the location
@@ -5128,12 +5215,19 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
5128
5215
  this.setRoomOverlay(logoPanelLines(Game.MAP_DOCK_ROWS - 2, boxWidth ?? 16));
5129
5216
  return;
5130
5217
  }
5218
+ // INSIDE A HOST there is no floor to draw (p.219, p.246): the dock
5219
+ // shows the interior's icons, not the room the body sits in.
5220
+ const inside = this.player.hostInside;
5221
+ if (inside) {
5222
+ this.setRoomOverlay(hostInteriorPanel(this.scene, this.player, inside, Game.MAP_DOCK_ROWS - 2, boxWidth ?? 16));
5223
+ return;
5224
+ }
5131
5225
  }
5132
5226
  else {
5133
5227
  const mapInner = mapWidth ?? 15;
5134
5228
  const mapView = Math.max(7, mapInner % 2 === 0 ? mapInner - 1 : mapInner);
5135
5229
  const mapLines = renderMapViewport(this.scene.getRooms(), here, (r, d) => this.scene.barrierSealing(r, d), {
5136
- visited: this.player.visitedRooms,
5230
+ visited: this.player.hasSoft('mapsoft') ? undefined /* Mapsoft (p.442): the district drawn and named */ : this.player.visitedRooms,
5137
5231
  arEyes: this.player.hasMatrixEyes(),
5138
5232
  }, mapView, tallestRoomRows());
5139
5233
  // The captions the HUD used to carry: where you are under the
@@ -5316,6 +5410,13 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
5316
5410
  if (actor.isUnconscious() && this.scene.getKnockout(actor.name)) {
5317
5411
  return out(`You're out cold -- the world moves without you.`);
5318
5412
  }
5413
+ // HARM NOBODY SETTLED (user report 2026-09-02: killed by ice, still
5414
+ // walking): a track that filled outside an exchange -- IC biofeedback,
5415
+ // dumpshock, a blow to the empty body -- is judged before the next
5416
+ // command runs, and again after it (utilities/harm-settlement.ts).
5417
+ const settledBefore = await settleUnresolvedHarm(this.scene, actor);
5418
+ if (settledBefore.length > 0)
5419
+ return out(settledBefore.join('\n'));
5319
5420
  if (verb === 'maka') {
5320
5421
  return out('You are still in the game, type "quit" to exit');
5321
5422
  }
@@ -5341,7 +5442,13 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
5341
5442
  const command = CommandFactory.createCommand(verb, context);
5342
5443
  if (command) {
5343
5444
  const result = await command.execute(args);
5344
- return out(this.withEphemeralTurn(actor, this.withAlarmTurn(actor, this.withICTurn(actor, result))));
5445
+ const wrapped = this.withEphemeralTurn(actor, this.withAlarmTurn(actor, this.withICTurn(actor, result)));
5446
+ // The IC turn above may have filled a track -- settle it now, not
5447
+ // on the next keystroke.
5448
+ const settled = await settleUnresolvedHarm(this.scene, actor);
5449
+ if (settled.length === 0)
5450
+ return out(wrapped);
5451
+ return out([wrapped ?? '', ...settled].filter((l, i) => i > 0 || l.length > 0).join('\n'));
5345
5452
  }
5346
5453
  // Typo tolerance: two straight sessions bounced on slips like "searh"
5347
5454
  // and "maop". Reuse the same fuzzy matcher item/NPC names go through
@@ -5455,16 +5562,12 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
5455
5562
  withICTurn(actor, result) {
5456
5563
  if (result === null)
5457
5564
  return null;
5458
- const room = actor.currentLocation;
5459
- // Phase 1b widened this gate. It used to ask hostIsHunting, which is
5460
- // only true AFTER the host had made someone -- so the host could
5461
- // never notice you in the first place, and measurement said so:
5462
- // twelve runs, twelve cracked hosts, zero sightings. A host with a
5463
- // node gets its turn whenever a persona is standing in it, and
5464
- // ic.ts decides whether that turn is eyes or ice.
5465
- if (!room || actor.plane !== 'matrix' || !room.hasNode)
5565
+ // The host's turn happens INSIDE it (p.246-247): the ice acts on what
5566
+ // is in the host, and the persona's position says whether it is.
5567
+ const host = actor.hostInside;
5568
+ if (!host || actor.plane !== 'matrix')
5466
5569
  return result;
5467
- const turn = runHostTurn(room, actor);
5570
+ const turn = runHostTurn(host.over, actor, this.scene);
5468
5571
  if (turn.lines.length === 0 && turn.meta.length === 0)
5469
5572
  return result;
5470
5573
  // Dice to the Mechanics pane, and only for a human -- the same gate
@@ -5545,7 +5648,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
5545
5648
  // players" / "crew street"); the bare verb still answers quietly.
5546
5649
  { title: 'party', entries: [['crew', 'party'], ['hire'], ['dismiss'], ['train'], ['order'], ['lead'], ['command'], ['deploy'], ['recall']] },
5547
5650
  { title: 'magic', entries: [['spells'], ['cast'], ['summon', 'conjure'], ['project', 'astral'], ['return'], ['assense']] },
5548
- { title: 'matrix', entries: [['jack'], ['jackpoint'], ['deck'], ['load'], ['unload'], ['hack'], ['mark'], ['download'], ['enter'], ['exit-host'], ['tap', 'splice'], ['snoop'], ['overwatch', 'os'], ['pan'], ['ar'], ['aros'], ['silent'], ['reboot'], ['agent'], ['drone'], ['jump', 'rig']] },
5651
+ { title: 'matrix', entries: [['jack'], ['jackpoint'], ['deck'], ['load'], ['unload'], ['hack'], ['mark'], ['download'], ['enter'], ['exit-host'], ['hop'], ['tap', 'splice'], ['snoop'], ['overwatch', 'os'], ['pan'], ['ar'], ['aros'], ['silent'], ['reboot'], ['agent'], ['drone'], ['jump', 'rig']] },
5549
5652
  // The Emerged get their own shelf (player request: "there MUST be
5550
5653
  // resonance" -- matrix is the place, Resonance is the talent).
5551
5654
  // `sustain` shelves here and not under magic: the command is