@maka/maka-cli 5.21.0 → 5.23.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 (49) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/sideQuest/commands/breach.js +2 -0
  3. package/bundle/typescript/src/commands/game/sideQuest/commands/breach.js.map +1 -1
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +2 -0
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/compile.js +11 -0
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/compile.js.map +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +9 -0
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.d.ts +7 -0
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +28 -0
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +8 -41
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/overwatch.d.ts +14 -0
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/overwatch.js +57 -0
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/overwatch.js.map +1 -0
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js +9 -1
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js.map +1 -1
  20. package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
  21. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +5 -1
  22. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
  23. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +25 -0
  24. package/bundle/typescript/src/commands/game/sideQuest/game.js +135 -4
  25. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  26. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +2 -0
  27. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +17 -1
  28. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  29. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.d.ts +1 -1
  30. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js +6 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js.map +1 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +6 -0
  33. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  34. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.d.ts +16 -0
  35. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js +57 -0
  36. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js.map +1 -0
  37. package/bundle/typescript/src/commands/game/sideQuest/utilities/overwatch.d.ts +34 -0
  38. package/bundle/typescript/src/commands/game/sideQuest/utilities/overwatch.js +112 -0
  39. package/bundle/typescript/src/commands/game/sideQuest/utilities/overwatch.js.map +1 -0
  40. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +4 -0
  41. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  42. package/bundle/typescript/src/tools/mpi/mpi.class.js +3 -1
  43. package/bundle/typescript/src/tools/mpi/mpi.class.js.map +1 -1
  44. package/bundle/typescript/src/tools/mpi/sdk-runtime.d.ts +1 -0
  45. package/bundle/typescript/src/tools/mpi/sdk-runtime.js +30 -0
  46. package/bundle/typescript/src/tools/mpi/sdk-runtime.js.map +1 -1
  47. package/bundle/typescript/src/tools/scaffold/scaffold.class.js +2 -1
  48. package/bundle/typescript/src/tools/scaffold/scaffold.class.js.map +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,57 @@
1
+ import { Command } from './command.js';
2
+ import { rollPool, formatRoll } from '../utilities/dice.js';
3
+ import { accrueOverwatch, settledScore, CONVERGENCE_AT } from '../utilities/overwatch.js';
4
+ import { leaveMatrix } from '../utilities/planes.js';
5
+ /**
6
+ * Check Overwatch Score (canon p.238): Electronic Warfare + Logic
7
+ * [Sleaze] vs GOD's flat 6 dice. Win the read and you see the exact
8
+ * number; lose it and you only learn that a count exists. Either way
9
+ * the check is ITSELF a Sleaze action -- GOD's counter-sweep hits add
10
+ * to the very score you're checking. The score is never on the HUD:
11
+ * this command is the only window, and it costs.
12
+ */
13
+ export class OverwatchCommand extends Command {
14
+ static verb = 'overwatch';
15
+ static description = 'Check your Overwatch Score -- GOD\'s evidence tally on your persona (Electronic Warfare + Logic vs GOD\'s sweep; a SLEAZE action, so asking adds to the count). Illegal hacks grow it; time grows it; at 40 GOD CONVERGES. "reboot" or jacking out resets it to zero.';
16
+ async execute(_args = []) {
17
+ const actor = this.actor;
18
+ if (actor.plane === 'astral') {
19
+ return `GOD doesn't watch the astral -- and the astral can't check.`;
20
+ }
21
+ const deck = actor.getCyberdeck();
22
+ if (actor.plane !== 'matrix' && !actor.isTechnomancer() && !deck) {
23
+ return `Checking your overwatch takes a live persona -- jack in, or put a working deck in hand.`;
24
+ }
25
+ if (actor.overwatchScore <= 0) {
26
+ return `You listen for the grid's counting... nothing. Fresh boot, clean slate -- GOD has no file on this persona yet.`;
27
+ }
28
+ const skill = actor.skillRating('electronic-warfare');
29
+ const sleazeEdge = actor.isTechnomancer()
30
+ ? Math.floor(actor.resonance / 2)
31
+ : (actor.activeDeck ?? deck)?.matrixEdge('sleaze') ?? 0;
32
+ const mine = rollPool(Math.max(1, actor.logic + (skill > 0 ? skill : -1) + sleazeEdge + actor.woundModifier - actor.sustainingPenalty));
33
+ const god = rollPool(6);
34
+ if (this.scene.isHumanControlled(actor)) {
35
+ this.logger.meta(`Check Overwatch (EW, sleaze): ${formatRoll(mine)}`);
36
+ this.logger.meta(` GOD's counter-sweep: ${formatRoll(god)}`);
37
+ }
38
+ // The number they glimpse is the score BEFORE this check's own bill
39
+ // lands -- you read the file as it stood when you looked.
40
+ const glimpsed = settledScore(actor);
41
+ const bill = accrueOverwatch(this.scene, actor, god.hits, 'checked overwatch (sleaze)');
42
+ const out = [];
43
+ if (mine.hits > god.hits) {
44
+ out.push(`You read your own file over GOD's shoulder: Overwatch Score ${glimpsed} of ${CONVERGENCE_AT}.${glimpsed >= 30 ? ` They are CLOSE.` : glimpsed >= 20 ? ` The count is climbing.` : ``}`);
45
+ }
46
+ else {
47
+ out.push(`The sweep slips past before you can read it -- no number, just the cold certainty that a count EXISTS, and that looking fed it.`);
48
+ }
49
+ out.push(...bill.lines);
50
+ if (bill.converged && actor.plane === 'matrix') {
51
+ out.push(...leaveMatrix(this.scene, actor, { forced: true, reason: `GOD force-reboots your persona --` }));
52
+ }
53
+ this.scene.updateStatus();
54
+ return out.join('\n');
55
+ }
56
+ }
57
+ //# sourceMappingURL=overwatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overwatch.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/overwatch.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { Command } from './command.js';
2
+ import { resetOverwatch } from '../utilities/overwatch.js';
2
3
  /**
3
4
  * The answer to a cracked PAN (see hack.ts): reboot your network. Clears
4
5
  * every jammed device you carry and brings a hacked-dark AR overlay back
@@ -29,7 +30,8 @@ export class RebootCommand extends Command {
29
30
  }
30
31
  }
31
32
  const hadArDown = actor.arOffline;
32
- if (jammedGear.length === 0 && !hadArDown) {
33
+ const hadOverwatch = actor.overwatchScore > 0;
34
+ if (jammedGear.length === 0 && !hadArDown && !hadOverwatch) {
33
35
  return `Your PAN is clean -- nothing needs a reboot.`;
34
36
  }
35
37
  for (const item of jammedGear) {
@@ -46,6 +48,12 @@ export class RebootCommand extends Command {
46
48
  if (hadArDown) {
47
49
  lines.push(` • AR overlay back up -- the world regrows its labels`);
48
50
  }
51
+ // A fresh boot has no history (canon p.231-232): GOD's tally dies
52
+ // with the old session.
53
+ if (hadOverwatch) {
54
+ resetOverwatch(actor, 'PAN reboot');
55
+ lines.push(` • Overwatch tally ZEROED -- whatever GOD was counting, it belonged to a persona that no longer exists`);
56
+ }
49
57
  return lines.join('\n');
50
58
  }
51
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"reboot.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/reboot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,cAAc,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,OAAO,aAAc,SAAQ,OAAO;IAC9B,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;IACvB,MAAM,CAAC,WAAW,GAAG,0GAA0G,CAAC;IAE1I,KAAK,CAAC,OAAO,CAAC,QAAkB,EAAE;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAEzB,IAAI,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;YAC5B,OAAO,oCAAoC,CAAC;QAC9C,CAAC;QACD,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC/G,OAAO,uFAAuF,CAAC;QACjG,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,oEAAoE,CAAC;QAC9E,CAAC;QAED,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACvE,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxC,IAAI,IAAI,IAAK,IAA6B,CAAC,MAAM;oBAAE,UAAU,CAAC,IAAI,CAAC,IAAiC,CAAC,CAAC;YACxG,CAAC;QACH,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAElC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC1C,OAAO,8CAA8C,CAAC;QACxD,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACtB,CAAC;QACD,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;QACxB,KAAK,CAAC,aAAa,CAAC,mBAAmB,EAAE,yBAAyB,CAAC,CAAC;QACpE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,wBAAwB,UAAU,CAAC,MAAM,0BAA0B,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;QACvI,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QAE1B,MAAM,KAAK,GAAG,CAAC,mFAAmF,CAAC,CAAC;QACpG,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,0BAA0B,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC"}
1
+ {"version":3,"file":"reboot.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/reboot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,OAAO,aAAc,SAAQ,OAAO;IAC9B,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;IACvB,MAAM,CAAC,WAAW,GAAG,0GAA0G,CAAC;IAE1I,KAAK,CAAC,OAAO,CAAC,QAAkB,EAAE;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAEzB,IAAI,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;YAC5B,OAAO,oCAAoC,CAAC;QAC9C,CAAC;QACD,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC/G,OAAO,uFAAuF,CAAC;QACjG,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,oEAAoE,CAAC;QAC9E,CAAC;QAED,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACvE,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxC,IAAI,IAAI,IAAK,IAA6B,CAAC,MAAM;oBAAE,UAAU,CAAC,IAAI,CAAC,IAAiC,CAAC,CAAC;YACxG,CAAC;QACH,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAClC,MAAM,YAAY,GAAG,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC;QAE9C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,YAAY,EAAE,CAAC;YAC3D,OAAO,8CAA8C,CAAC;QACxD,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACtB,CAAC;QACD,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;QACxB,KAAK,CAAC,aAAa,CAAC,mBAAmB,EAAE,yBAAyB,CAAC,CAAC;QACpE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,wBAAwB,UAAU,CAAC,MAAM,0BAA0B,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;QACvI,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QAE1B,MAAM,KAAK,GAAG,CAAC,mFAAmF,CAAC,CAAC;QACpG,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,0BAA0B,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;QACvE,CAAC;QACD,kEAAkE;QAClE,wBAAwB;QACxB,IAAI,YAAY,EAAE,CAAC;YACjB,cAAc,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,yGAAyG,CAAC,CAAC;QACxH,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC"}
@@ -16,4 +16,4 @@
16
16
  * No import.meta here (engine-graph law) -- this file is loaded by the
17
17
  * server.
18
18
  */
19
- export declare const ENGINE_VERSION = "1.6.0";
19
+ export declare const ENGINE_VERSION = "1.8.0";
@@ -27,5 +27,9 @@
27
27
  // distance (go <room> jumps anywhere; map renders the grid).
28
28
  // 1.6.0 (2026-08-23): icon-first matrix look (PANs visible, no exits);
29
29
  // hack forks Attack/Sleaze (canon p.236/238/240).
30
- export const ENGINE_VERSION = '1.6.0';
30
+ // 1.7.0 (2026-08-23): GOD -- Overwatch Score on illegal actions,
31
+ // convergence at 40, overwatch check verb, reboot/jack-out resets.
32
+ // 1.8.0 (2026-08-24): SINs & getting caught -- run heat, the KE
33
+ // checkpoint on the ride home, fake SINs in the catalog (v4).
34
+ export const ENGINE_VERSION = '1.8.0';
31
35
  //# sourceMappingURL=engine-version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine-version.js","sourceRoot":"","sources":["../../../../../../src/commands/game/sideQuest/engine-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,sEAAsE;AACtE,mEAAmE;AACnE,oEAAoE;AACpE,sEAAsE;AACtE,6CAA6C;AAC7C,iEAAiE;AACjE,qCAAqC;AACrC,oEAAoE;AACpE,6DAA6D;AAC7D,uEAAuE;AACvE,kDAAkD;AAClD,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC"}
1
+ {"version":3,"file":"engine-version.js","sourceRoot":"","sources":["../../../../../../src/commands/game/sideQuest/engine-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,sEAAsE;AACtE,mEAAmE;AACnE,oEAAoE;AACpE,sEAAsE;AACtE,6CAA6C;AAC7C,iEAAiE;AACjE,qCAAqC;AACrC,oEAAoE;AACpE,6DAA6D;AAC7D,uEAAuE;AACvE,kDAAkD;AAClD,iEAAiE;AACjE,mEAAmE;AACnE,gEAAgE;AAChE,8DAA8D;AAC9D,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC"}
@@ -95,6 +95,13 @@ export default class Game {
95
95
  * standing quote. Returns the line to show, or ''. */
96
96
  scrapPendingRunForCrewChange(): string;
97
97
  performedThisCycle: boolean;
98
+ private _runHeat;
99
+ private static readonly CHECKPOINT_HEAT;
100
+ /** Bill the district's attention. No-op at the hub or in classic
101
+ * (hubless) scenes -- only runs have a ride home to be caught on. */
102
+ addHeat(amount: number, why: string): void;
103
+ /** The HUD's read on the district (bands, never the number). */
104
+ private heatBand;
98
105
  crewForHire: ICrewMember[];
99
106
  private _presenceTimer?;
100
107
  private static readonly PRESENCE_TICK_MS;
@@ -572,6 +579,24 @@ export default class Game {
572
579
  * open combat bookkeeping) is settled on the way home.
573
580
  */
574
581
  private returnToHub;
582
+ /**
583
+ * KNIGHT ERRANT CHECKPOINT (SINs & getting caught, ruling 2026-08-24;
584
+ * canon p.363-368, verification values engine-adapted -- the RAG was
585
+ * down): past CHECKPOINT_HEAT, the ride home hits the wire and the
586
+ * SIN mechanic resolves. A CRIMINAL SIN broadcasts before the wand
587
+ * lifts -- shakedown, citation, notoriety. Otherwise your best FAKE
588
+ * SIN is tested: the scanner rolls its dice (R3 wand, R4 on the
589
+ * hottest nights) and BEATS the fake's rating to burn it -- burned
590
+ * means gone from every database at once, then processing. No SIN at
591
+ * all: fast-talk (Charisma + con) or zip ties. Heat always resets --
592
+ * the night ends either way.
593
+ */
594
+ private checkpointOnRideHome;
595
+ /** Processing: prints, retinas, a fee -- and for the SINless, the
596
+ * street's cruelest joke: they ISSUE you a SIN. A criminal one.
597
+ * Catch-and-release always (arrest never ends a career by fiat --
598
+ * the record it leaves does the long-term damage). */
599
+ private processArrest;
575
600
  /**
576
601
  * The actual drafting work, split out from continueToNextScene() purely
577
602
  * so that method can wrap it in the try/finally that clears
@@ -105,6 +105,7 @@ import { BluffCommand } from './commands/bluff.js';
105
105
  import { DispelCommand } from './commands/dispel.js';
106
106
  import { BreachCommand } from './commands/breach.js';
107
107
  import { EmoteCommand } from './commands/emote.js';
108
+ import { OverwatchCommand } from './commands/overwatch.js';
108
109
  import { AutoFight } from './utilities/auto-fight.js';
109
110
  import { CommandFactory } from './factories/command-factory.js';
110
111
  import { SceneSynthesizer } from './factories/scene-factory.js';
@@ -236,6 +237,35 @@ export default class Game {
236
237
  // cycle, reset at homecoming like a contact's legwork. Deliberately
237
238
  // NOT saved -- a relaunch is as good as a homecoming for new material.
238
239
  performedThisCycle = false;
240
+ // HEAT (SINs & getting caught, ruling 2026-08-24): the meatspace
241
+ // Overwatch Score. Loud acts during a RUN bill it -- alarms, breach
242
+ // crashes, bodies dropped, GOD burning your location -- and the ride
243
+ // home is where the night catches up: past CHECKPOINT_HEAT the taxi
244
+ // hits a Knight Errant checkpoint and the SIN mechanic resolves
245
+ // (checkpointOnRideHome). Hub life never accrues heat (home turf),
246
+ // and a fresh run starts cold. Not saved: heat is a per-run debt.
247
+ _runHeat = 0;
248
+ static CHECKPOINT_HEAT = 6;
249
+ /** Bill the district's attention. No-op at the hub or in classic
250
+ * (hubless) scenes -- only runs have a ride home to be caught on. */
251
+ addHeat(amount, why) {
252
+ if (!this.hasHub || this.scene === this._hubScene)
253
+ return;
254
+ this._runHeat += amount;
255
+ Logger.getInstance().write(`Heat: +${amount} (${why}) -> ${this._runHeat}.`);
256
+ }
257
+ /** The HUD's read on the district (bands, never the number). */
258
+ heatBand() {
259
+ if (this._runHeat <= 0)
260
+ return undefined;
261
+ if (this._runHeat >= 12)
262
+ return `{red-fg}🔥 Heat: HTR assembling somewhere close{/red-fg}`;
263
+ if (this._runHeat >= Game.CHECKPOINT_HEAT)
264
+ return `{red-fg}🔥 Heat: sirens circling closer{/red-fg}`;
265
+ if (this._runHeat >= 3)
266
+ return `{yellow-fg}🔥 Heat: ears up all over the block{/yellow-fg}`;
267
+ return `{yellow-fg}🔥 Heat: somebody heard something{/yellow-fg}`;
268
+ }
239
269
  // THE FOR-HIRE BOARD: archetype-built candidates, fresh faces each
240
270
  // homecoming (the gig-pool rotation school). Not saved -- the street's
241
271
  // talent pool churns; whoever you HIRED is the persistent part.
@@ -1664,6 +1694,9 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
1664
1694
  // Free-form roleplay ("me" is the classic MUD spelling).
1665
1695
  CommandFactory.registerCommand('emote', EmoteCommand);
1666
1696
  CommandFactory.registerCommand('me', EmoteCommand);
1697
+ // GOD's tally ("os" for the tabletop shorthand).
1698
+ CommandFactory.registerCommand('overwatch', OverwatchCommand);
1699
+ CommandFactory.registerCommand('os', OverwatchCommand);
1667
1700
  }
1668
1701
  determineStartRoom() {
1669
1702
  return this.scene.determineStartRoom();
@@ -2299,14 +2332,104 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2299
2332
  deliveryLine = `\nWaiting with your gear: ${delivered.join(', ')}.`;
2300
2333
  }
2301
2334
  }
2335
+ // The night catches up on the ride (SINs & getting caught): a hot
2336
+ // run means the wire between there and home. Settled AFTER the
2337
+ // payout (the fee comes out of tonight's take) and BEFORE the save
2338
+ // below -- a burned fake or an issued SIN must survive a quit.
2339
+ const checkpointBlock = this.checkpointOnRideHome();
2302
2340
  // The homecoming save: everything above (payout, rent, restock,
2303
- // contacts reset, deliveries) settled first, so the save reflects
2304
- // the hub as the player will find it.
2341
+ // contacts reset, deliveries, the checkpoint) settled first, so the
2342
+ // save reflects the hub as the player will find it.
2305
2343
  this.requestSave('homecoming');
2306
2344
  const fateLine = wrappedRun
2307
2345
  ? `The run's wrapped and the street swallows you back up.`
2308
2346
  : `The run's behind you -- unfinished, unpaid, already someone else's problem. The street doesn't ask.`;
2309
- return `\n=== ${this._hubName.toUpperCase()} ===\n${fateLine} You're home -- your hideout, one door off ${hub.determineStartRoom().name}.${hint(` Call ${FIXER_NAME} when you're hungry for the next job.`)}${clientLine}\n${rentLine}${comfortLine}${restockLine}${deliveryLine}${docwagonLine}${allyLine}${companionBlock.length > 0 ? `\n${companionBlock.join('\n')}` : ''}`;
2347
+ return `${checkpointBlock ? `\n${checkpointBlock}\n` : ''}\n=== ${this._hubName.toUpperCase()} ===\n${fateLine} You're home -- your hideout, one door off ${hub.determineStartRoom().name}.${hint(` Call ${FIXER_NAME} when you're hungry for the next job.`)}${clientLine}\n${rentLine}${comfortLine}${restockLine}${deliveryLine}${docwagonLine}${allyLine}${companionBlock.length > 0 ? `\n${companionBlock.join('\n')}` : ''}`;
2348
+ }
2349
+ /**
2350
+ * KNIGHT ERRANT CHECKPOINT (SINs & getting caught, ruling 2026-08-24;
2351
+ * canon p.363-368, verification values engine-adapted -- the RAG was
2352
+ * down): past CHECKPOINT_HEAT, the ride home hits the wire and the
2353
+ * SIN mechanic resolves. A CRIMINAL SIN broadcasts before the wand
2354
+ * lifts -- shakedown, citation, notoriety. Otherwise your best FAKE
2355
+ * SIN is tested: the scanner rolls its dice (R3 wand, R4 on the
2356
+ * hottest nights) and BEATS the fake's rating to burn it -- burned
2357
+ * means gone from every database at once, then processing. No SIN at
2358
+ * all: fast-talk (Charisma + con) or zip ties. Heat always resets --
2359
+ * the night ends either way.
2360
+ */
2361
+ checkpointOnRideHome() {
2362
+ const heat = this._runHeat;
2363
+ this._runHeat = 0;
2364
+ if (heat < Game.CHECKPOINT_HEAT)
2365
+ return '';
2366
+ const p = this.player;
2367
+ const lines = [
2368
+ ``,
2369
+ `{red-fg}Halfway home, the traffic folds around a KNIGHT ERRANT CHECKPOINT -- light bars, a drone hovering, a scanner wand. The night you just had is all over the district feeds.{/red-fg}`,
2370
+ ];
2371
+ if (p.qualities.has('sinner-criminal')) {
2372
+ const fine = Math.min(p.currency, 500 + heat * 100);
2373
+ if (fine > 0)
2374
+ p.adjustCurrency(-fine);
2375
+ p.addNotoriety('swept up at a KE checkpoint on a hot night');
2376
+ lines.push(`Your CRIMINAL SIN broadcasts before the wand even lifts -- they know the face, the file, the record. On a night this hot, that means the full treatment.`);
2377
+ lines.push(` Cited and released: ${fine > 0 ? `${fine} nuyen lighter` : `your sticks came up empty, so the citation rides your file instead`}, and the record grows. (Notoriety.)`);
2378
+ return lines.join('\n');
2379
+ }
2380
+ const verifierDice = heat >= 12 ? 8 : 6;
2381
+ const fakes = p.inventory.getAllItems()
2382
+ .map(i => ({ item: i, m: /^Fake SIN \(Rating (\d)\)$/.exec(i.name) }))
2383
+ .filter(x => x.m !== null)
2384
+ .map(x => ({ item: x.item, rating: parseInt(x.m[1], 10) }))
2385
+ .sort((a, b) => b.rating - a.rating);
2386
+ if (fakes.length > 0) {
2387
+ const fake = fakes[0];
2388
+ const scan = rollPool(verifierDice);
2389
+ Logger.getInstance().meta(`SIN verification (KE scanner, ${verifierDice}d6): ${formatRoll(scan)} (beats Rating ${fake.rating} to burn the fake)`);
2390
+ if (scan.hits <= fake.rating) {
2391
+ lines.push(`You hand over the Rating ${fake.rating} fake and hold a bored face. The wand chews on it... green light. "Move along."`);
2392
+ return lines.join('\n');
2393
+ }
2394
+ p.inventory.removeItem(fake.item.name);
2395
+ lines.push(`You hand over the Rating ${fake.rating} fake. The wand chews... and SPITS. "This SIN is flagged as of now." The fake is BURNED -- worthless in every database at once.`);
2396
+ lines.push(this.processArrest(heat));
2397
+ return lines.join('\n');
2398
+ }
2399
+ const con = p.skillRating('con');
2400
+ const talk = rollPool(Math.max(1, p.charisma + (con > 0 ? con : -1) + p.woundModifier));
2401
+ const need = heat >= 12 ? 4 : 3;
2402
+ Logger.getInstance().meta(`Fast talk at the wire (Charisma + con): ${formatRoll(talk)} (${need}+ slips through)`);
2403
+ if (talk.hits >= need) {
2404
+ lines.push(`No SIN at all -- so you become somebody's cousin, somebody's designated driver, a story so boring the officer waves you through mid-sentence.`);
2405
+ return lines.join('\n');
2406
+ }
2407
+ lines.push(`No SIN, and no story that holds. Zip ties.`);
2408
+ lines.push(this.processArrest(heat));
2409
+ return lines.join('\n');
2410
+ }
2411
+ /** Processing: prints, retinas, a fee -- and for the SINless, the
2412
+ * street's cruelest joke: they ISSUE you a SIN. A criminal one.
2413
+ * Catch-and-release always (arrest never ends a career by fiat --
2414
+ * the record it leaves does the long-term damage). */
2415
+ processArrest(heat) {
2416
+ const p = this.player;
2417
+ const lines = [];
2418
+ const fine = Math.min(p.currency, 1000 + heat * 100);
2419
+ if (fine > 0)
2420
+ p.adjustCurrency(-fine);
2421
+ lines.push(`Processing: prints, retinas, a genome swab, every camera in the room.${fine > 0 ? ` The "administrative fee" is ${fine} nuyen.` : ` Your sticks come up empty; the fee rides your new file.`}`);
2422
+ if (!p.qualities.has('sinner-criminal') && !p.qualities.has('sinner-national') && !p.qualities.has('sinner-corp')) {
2423
+ p.addQuality('sinner-criminal');
2424
+ lines.push(`{red-fg}And the worst of it: they ISSUE you a SIN. A CRIMINAL SIN -- broadcast always, 15% of every payout withheld, forever in the system. Get caught being nobody, become somebody with a record.{/red-fg}`);
2425
+ }
2426
+ else {
2427
+ lines.push(`The record deepens. (Notoriety.)`);
2428
+ }
2429
+ p.addNotoriety('arrested on the ride home');
2430
+ lines.push(`They cut you loose before dawn -- catch-and-release. Holding a runner costs more than the paperwork is worth.`);
2431
+ Logger.getInstance().write(`Checkpoint arrest: fine ${fine}, heat ${heat}, criminal SIN ${p.qualities.has('sinner-criminal')}.`);
2432
+ return lines.join('\n');
2310
2433
  }
2311
2434
  /**
2312
2435
  * The actual drafting work, split out from continueToNextScene() purely
@@ -2429,6 +2552,8 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2429
2552
  */
2430
2553
  async activateRunScene(seed, tier, opts) {
2431
2554
  this._tier = tier;
2555
+ // A fresh run starts COLD -- heat is a per-night debt.
2556
+ this._runHeat = 0;
2432
2557
  this.sceneSeed = seed;
2433
2558
  // A call partner from the old scene wouldn't exist in the new one --
2434
2559
  // hang up before moving on, rather than leave a dangling reference.
@@ -2973,6 +3098,12 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2973
3098
  const stunColor = trackColor(player.stunTaken, player.maxStunBoxes);
2974
3099
  lines.push(`{${physColor}-fg}Physical ${player.conditionSummary()}{/${physColor}-fg} ` +
2975
3100
  `{${stunColor}-fg}Stun ${player.stunSummary()}{/${stunColor}-fg}${woundNote}`);
3101
+ // HEAT (SINs & getting caught): the district's attention, in bands
3102
+ // -- the number stays the GM's. Absent entirely while cold, so hub
3103
+ // life never shows it.
3104
+ const heat = this.heatBand();
3105
+ if (heat && player === this.player)
3106
+ lines.push(heat);
2976
3107
  // Line 3: readiness -- armed state, armor, and whether the commlink is
2977
3108
  // actually live (equipped), the recurring "why can't I call anyone"
2978
3109
  // friction surfaced at a glance.
@@ -3275,7 +3406,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
3275
3406
  // players" / "crew street"); the bare verb still answers quietly.
3276
3407
  { title: 'party', entries: [['crew', 'party'], ['hire'], ['dismiss'], ['train'], ['order'], ['lead'], ['command'], ['deploy'], ['recall']] },
3277
3408
  { title: 'magic', entries: [['spells'], ['cast'], ['summon', 'conjure'], ['project'], ['return'], ['assense']] },
3278
- { title: 'matrix', entries: [['jack'], ['deck'], ['load'], ['unload'], ['hack'], ['pan'], ['silent'], ['reboot'], ['agent'], ['drone'], ['jump', 'rig']] },
3409
+ { title: 'matrix', entries: [['jack'], ['deck'], ['load'], ['unload'], ['hack'], ['overwatch', 'os'], ['pan'], ['silent'], ['reboot'], ['agent'], ['drone'], ['jump', 'rig']] },
3279
3410
  // The Emerged get their own shelf (player request: "there MUST be
3280
3411
  // resonance" -- matrix is the place, Resonance is the talent).
3281
3412
  { title: 'resonance', entries: [['compile', 'decompile'], ['thread'], ['register']] },