@maka/maka-cli 5.117.0 → 5.118.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 (88) 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/compile.js +3 -0
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/compile.js.map +1 -1
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/decompile.js +3 -6
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/decompile.js.map +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/download.js +4 -3
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/download.js.map +1 -1
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/enter-host.d.ts +1 -1
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/enter-host.js +10 -8
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/enter-host.js.map +1 -1
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +8 -3
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +14 -11
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/hop.d.ts +16 -0
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/hop.js +95 -0
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/hop.js.map +1 -0
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js +3 -1
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js.map +1 -1
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/jackpoint.js +5 -7
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/jackpoint.js.map +1 -1
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +24 -4
  25. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  26. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +1 -1
  27. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
  28. package/bundle/typescript/src/commands/game/sideQuest/commands/mark.js +8 -4
  29. package/bundle/typescript/src/commands/game/sideQuest/commands/mark.js.map +1 -1
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +2 -0
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +8 -5
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +2 -1
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +20 -2
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/thread.js +2 -5
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/thread.js.map +1 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
  41. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +1 -1
  42. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +1 -0
  43. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  44. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +2 -0
  45. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
  46. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +4 -0
  47. package/bundle/typescript/src/commands/game/sideQuest/game.js +40 -13
  48. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  49. package/bundle/typescript/src/commands/game/sideQuest/models/host.d.ts +80 -0
  50. package/bundle/typescript/src/commands/game/sideQuest/models/host.js +91 -0
  51. package/bundle/typescript/src/commands/game/sideQuest/models/host.js.map +1 -0
  52. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +45 -1
  53. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +79 -35
  54. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  55. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +40 -9
  56. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +73 -12
  57. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  58. package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +3 -0
  59. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +3 -0
  60. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
  61. package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +2 -0
  62. package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +1 -0
  63. package/bundle/typescript/src/commands/game/sideQuest/types/seed/scene-seed.d.ts +2 -0
  64. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +30 -10
  65. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  66. package/bundle/typescript/src/commands/game/sideQuest/utilities/condition-report.js +64 -11
  67. package/bundle/typescript/src/commands/game/sideQuest/utilities/condition-report.js.map +1 -1
  68. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-reach.js +2 -1
  69. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-reach.js.map +1 -1
  70. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.d.ts +11 -0
  71. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js +56 -8
  72. package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js.map +1 -1
  73. package/bundle/typescript/src/commands/game/sideQuest/utilities/grids.d.ts +72 -0
  74. package/bundle/typescript/src/commands/game/sideQuest/utilities/grids.js +100 -0
  75. package/bundle/typescript/src/commands/game/sideQuest/utilities/grids.js.map +1 -0
  76. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic-actors.d.ts +9 -0
  77. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic-actors.js +98 -0
  78. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic-actors.js.map +1 -0
  79. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.d.ts +3 -2
  80. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.js +26 -6
  81. package/bundle/typescript/src/commands/game/sideQuest/utilities/ic.js.map +1 -1
  82. package/bundle/typescript/src/commands/game/sideQuest/utilities/matrix-roster.d.ts +62 -0
  83. package/bundle/typescript/src/commands/game/sideQuest/utilities/matrix-roster.js +97 -0
  84. package/bundle/typescript/src/commands/game/sideQuest/utilities/matrix-roster.js.map +1 -0
  85. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +10 -0
  86. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +24 -19
  87. package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -1
  88. package/package.json +1 -1
@@ -0,0 +1,80 @@
1
+ import type { Room } from './room.js';
2
+ import type { NPC } from './npc.js';
3
+ import type { Door } from './door.js';
4
+ import type { Item } from './item.js';
5
+ import type { Grid } from '../utilities/grids.js';
6
+ /**
7
+ * A HOST (SR5 p.246-247), as its own object. Increment 1 of the Matrix
8
+ * position model (plan 2026-09-02): the host state that used to live as
9
+ * loose fields on Room moves here, and Room keeps delegating accessors
10
+ * so every existing reader and writer compiles and behaves unchanged.
11
+ *
12
+ * `over` is the physical room the host stands over in this engine: the
13
+ * site systems it masters bind through it -- a crack releases that room's
14
+ * maglocks and unseals the matrix items in its inventory, and the room's
15
+ * cameras answer to it (utilities/surveillance.ts). Canon puts no host at
16
+ * a physical address (p.246, "hosts don't have a physical location"); the
17
+ * anchor is the engine's mapping of "the network that runs this place",
18
+ * not a claim about where the host is.
19
+ *
20
+ * `grid` is optional and unset by default: canon says hosts are reached
21
+ * from any grid and pay to be accessible from all of them (p.246, Data
22
+ * Trails p.15); a seed may declare one, and only then does the cross-grid
23
+ * penalty apply to actions against it (increment 3).
24
+ *
25
+ * What persists: `cracked` only (utilities/persistence.ts). Marks, alert,
26
+ * running IC and the patrol clock are session state by design.
27
+ */
28
+ export declare class Host {
29
+ static readonly DEFAULT_RATING = 3;
30
+ static readonly MIN_RATING = 1;
31
+ static readonly MAX_RATING = 12;
32
+ /** SR5 p.247: Host Rating 1-12; anything else is the default. */
33
+ static clampRating(rating: number | undefined): number;
34
+ readonly over: Room;
35
+ rating: number;
36
+ grid?: Grid;
37
+ /** The seed's word for the grid ('public' | 'local' | 'global:<corp>'); resolved against the scene's local grid by the factory. */
38
+ gridSeed?: string;
39
+ /** The one persisted field. A sanctioned host never remembers a crack. */
40
+ cracked: boolean;
41
+ /** player name -> marks the player holds on this host (max 3, p.236). */
42
+ marksBy: Map<string, number>;
43
+ /** player name -> marks the host holds on that player. */
44
+ marksOn: Map<string, number>;
45
+ /** Walls up: the host holds a mark on an intruder and its ice is hunting. */
46
+ alert: boolean;
47
+ /** IC running now, by kind name. Names, not actors -- until increment 5. */
48
+ runningIC: string[];
49
+ /** The running IC as actors, by kind (utilities/ic-actors.ts). Empty where no scene could host them. */
50
+ iceActors: Map<string, NPC>;
51
+ /** Kinds crashed this turn; the host relaunches them next turn (p.355-356). */
52
+ relaunchQueue: string[];
53
+ patrolCountdown?: number;
54
+ /** A haven (jackpoint.ts): never hunts, shows no rating, forgets cracks. */
55
+ sanctioned: boolean;
56
+ constructor(over: Room, opts?: {
57
+ rating?: number;
58
+ grid?: Grid;
59
+ sanctioned?: boolean;
60
+ });
61
+ /** The host is named for the place it stands over -- every alias the game prints derives from this. */
62
+ get name(): string;
63
+ /** So a Host can stand in for a Room wherever a reader only asks "is there a host here?". */
64
+ get hasNode(): true;
65
+ /**
66
+ * SR5 p.247: a host's four Matrix attributes are Host Rating, +1, +2, +3
67
+ * in some order. This engine's fixed order: Attack +1, Sleaze +0, Data
68
+ * Processing +3, Firewall +2.
69
+ */
70
+ attributes(): {
71
+ attack: number;
72
+ sleaze: number;
73
+ dataProcessing: number;
74
+ firewall: number;
75
+ };
76
+ /** The matrix-plane items the host keeps, from the room it stands over (until seeds carry host files). */
77
+ files(): Item[];
78
+ /** The locked doors of the room it stands over -- what a crack releases. */
79
+ maglocks(): Door[];
80
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * A HOST (SR5 p.246-247), as its own object. Increment 1 of the Matrix
3
+ * position model (plan 2026-09-02): the host state that used to live as
4
+ * loose fields on Room moves here, and Room keeps delegating accessors
5
+ * so every existing reader and writer compiles and behaves unchanged.
6
+ *
7
+ * `over` is the physical room the host stands over in this engine: the
8
+ * site systems it masters bind through it -- a crack releases that room's
9
+ * maglocks and unseals the matrix items in its inventory, and the room's
10
+ * cameras answer to it (utilities/surveillance.ts). Canon puts no host at
11
+ * a physical address (p.246, "hosts don't have a physical location"); the
12
+ * anchor is the engine's mapping of "the network that runs this place",
13
+ * not a claim about where the host is.
14
+ *
15
+ * `grid` is optional and unset by default: canon says hosts are reached
16
+ * from any grid and pay to be accessible from all of them (p.246, Data
17
+ * Trails p.15); a seed may declare one, and only then does the cross-grid
18
+ * penalty apply to actions against it (increment 3).
19
+ *
20
+ * What persists: `cracked` only (utilities/persistence.ts). Marks, alert,
21
+ * running IC and the patrol clock are session state by design.
22
+ */
23
+ export class Host {
24
+ static DEFAULT_RATING = 3;
25
+ static MIN_RATING = 1;
26
+ static MAX_RATING = 12;
27
+ /** SR5 p.247: Host Rating 1-12; anything else is the default. */
28
+ static clampRating(rating) {
29
+ if (typeof rating !== 'number' || !Number.isFinite(rating))
30
+ return Host.DEFAULT_RATING;
31
+ return Math.min(Host.MAX_RATING, Math.max(Host.MIN_RATING, Math.round(rating)));
32
+ }
33
+ over;
34
+ rating;
35
+ grid;
36
+ /** The seed's word for the grid ('public' | 'local' | 'global:<corp>'); resolved against the scene's local grid by the factory. */
37
+ gridSeed;
38
+ /** The one persisted field. A sanctioned host never remembers a crack. */
39
+ cracked = false;
40
+ /** player name -> marks the player holds on this host (max 3, p.236). */
41
+ marksBy = new Map();
42
+ /** player name -> marks the host holds on that player. */
43
+ marksOn = new Map();
44
+ /** Walls up: the host holds a mark on an intruder and its ice is hunting. */
45
+ alert = false;
46
+ /** IC running now, by kind name. Names, not actors -- until increment 5. */
47
+ runningIC = [];
48
+ /** The running IC as actors, by kind (utilities/ic-actors.ts). Empty where no scene could host them. */
49
+ iceActors = new Map();
50
+ /** Kinds crashed this turn; the host relaunches them next turn (p.355-356). */
51
+ relaunchQueue = [];
52
+ patrolCountdown;
53
+ /** A haven (jackpoint.ts): never hunts, shows no rating, forgets cracks. */
54
+ sanctioned = false;
55
+ constructor(over, opts = {}) {
56
+ this.over = over;
57
+ this.rating = Host.clampRating(opts.rating);
58
+ this.grid = opts.grid;
59
+ this.sanctioned = opts.sanctioned ?? false;
60
+ }
61
+ /** The host is named for the place it stands over -- every alias the game prints derives from this. */
62
+ get name() {
63
+ return this.over.name;
64
+ }
65
+ /** So a Host can stand in for a Room wherever a reader only asks "is there a host here?". */
66
+ get hasNode() {
67
+ return true;
68
+ }
69
+ /**
70
+ * SR5 p.247: a host's four Matrix attributes are Host Rating, +1, +2, +3
71
+ * in some order. This engine's fixed order: Attack +1, Sleaze +0, Data
72
+ * Processing +3, Firewall +2.
73
+ */
74
+ attributes() {
75
+ const hr = this.rating;
76
+ return { attack: hr + 1, sleaze: hr, dataProcessing: hr + 3, firewall: hr + 2 };
77
+ }
78
+ /** The matrix-plane items the host keeps, from the room it stands over (until seeds carry host files). */
79
+ files() {
80
+ return this.over.inventory.getAllItems().filter(i => i.plane === 'matrix');
81
+ }
82
+ /** The locked doors of the room it stands over -- what a crack releases. */
83
+ maglocks() {
84
+ return Array.from(this.over.exits.values())
85
+ .filter((exit) => isDoor(exit) && exit.checkIfLocked());
86
+ }
87
+ }
88
+ function isDoor(exit) {
89
+ return typeof exit === 'object' && exit !== null && typeof exit.checkIfLocked === 'function';
90
+ }
91
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/models/host.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,IAAI;IACf,MAAM,CAAU,cAAc,GAAG,CAAC,CAAC;IACnC,MAAM,CAAU,UAAU,GAAG,CAAC,CAAC;IAC/B,MAAM,CAAU,UAAU,GAAG,EAAE,CAAC;IAEhC,iEAAiE;IACjE,MAAM,CAAC,WAAW,CAAC,MAA0B;QAC3C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC,cAAc,CAAC;QACvF,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC;IAEQ,IAAI,CAAO;IACpB,MAAM,CAAS;IACf,IAAI,CAAQ;IACZ,mIAAmI;IACnI,QAAQ,CAAU;IAClB,0EAA0E;IAC1E,OAAO,GAAG,KAAK,CAAC;IAChB,yEAAyE;IACzE,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,0DAA0D;IAC1D,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,6EAA6E;IAC7E,KAAK,GAAG,KAAK,CAAC;IACd,4EAA4E;IAC5E,SAAS,GAAa,EAAE,CAAC;IACzB,wGAAwG;IACxG,SAAS,GAAG,IAAI,GAAG,EAAe,CAAC;IACnC,+EAA+E;IAC/E,aAAa,GAAa,EAAE,CAAC;IAC7B,eAAe,CAAU;IACzB,4EAA4E;IAC5E,UAAU,GAAG,KAAK,CAAC;IAEnB,YAAY,IAAU,EAAE,OAA+D,EAAE;QACvF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC;IAC7C,CAAC;IAED,uGAAuG;IACvG,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,6FAA6F;IAC7F,IAAI,OAAO;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,UAAU;QACR,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;IAClF,CAAC;IAED,0GAA0G;IAC1G,KAAK;QACH,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC;IAC7E,CAAC;IAED,4EAA4E;IAC5E,QAAQ;QACN,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;aACxC,MAAM,CAAC,CAAC,IAAI,EAAgB,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1E,CAAC;;AAGH,SAAS,MAAM,CAAC,IAAa;IAC3B,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,OAAQ,IAAa,CAAC,aAAa,KAAK,UAAU,CAAC;AACzG,CAAC"}
@@ -1,4 +1,6 @@
1
1
  import { PlayerEquipment } from '../types/player-equipment.js';
2
+ import type { Grid } from '../utilities/grids.js';
3
+ import type { Host } from './host.js';
2
4
  import { type Aspect } from '../aspects.js';
3
5
  import { LifestyleChoices } from '../types/seed/lifestyle-seed.js';
4
6
  import { Logger } from '../utilities/logger.js';
@@ -51,6 +53,19 @@ export declare function isPhysicalPlane(plane: Plane): boolean;
51
53
  * actors out of a Matrix target list; this function answers one question
52
54
  * and is not a second plane check.
53
55
  */
56
+ /**
57
+ * A persona's place in the Matrix (SR5 p.239, p.246): out on a grid, or
58
+ * inside a host it entered from one -- and never on a street. See
59
+ * Player.matrixPosition.
60
+ */
61
+ export type MatrixPosition = {
62
+ kind: 'grid';
63
+ grid?: Grid;
64
+ } | {
65
+ kind: 'host';
66
+ host: Host;
67
+ from?: Grid;
68
+ };
54
69
  export declare function sameHostSide(actor: Player, other: Player): boolean;
55
70
  export type SimMode = 'cold' | 'hot';
56
71
  /**
@@ -435,7 +450,32 @@ export declare class Player extends AbstractPlayer {
435
450
  * Session state like hostAlert and linkLockedBy: leaving the Matrix
436
451
  * ends it, so nothing here is persisted.
437
452
  */
438
- insideHost?: Room;
453
+ /**
454
+ * WHERE THE PERSONA IS (increment 2 of the Matrix position model): on a
455
+ * grid, or inside a host it entered from one. Session-only, like
456
+ * bodyRoom. The body stays in currentLocation on every plane -- the
457
+ * persona never writes it. The setter fires the live-sheet beat, which
458
+ * is the beat the currentLocation setter used to fire on enter/exit.
459
+ * `grid` may be unknown (fixtures, and increment 2 itself): unknown is
460
+ * not "another grid", so it costs nothing.
461
+ */
462
+ private _matrixPosition?;
463
+ get matrixPosition(): MatrixPosition | undefined;
464
+ set matrixPosition(v: MatrixPosition | undefined);
465
+ /** The grid the persona rides, when it is on one. */
466
+ get currentGrid(): Grid | undefined;
467
+ /** The host the persona is inside, when it is inside one. */
468
+ get hostInside(): Host | undefined;
469
+ /**
470
+ * @deprecated compat through increment 4 -- read hostInside / write
471
+ * matrixPosition. The room a host stands over, when inside one. The
472
+ * setter still builds the position so every old writer keeps working.
473
+ */
474
+ private _compatInsideRoom?;
475
+ get insideHost(): Room | undefined;
476
+ set insideHost(room: Room | undefined);
477
+ /** The grid last stood on, so an exit built through the old setter returns there. */
478
+ private get lastGrid();
439
479
  /**
440
480
  * MATRIX ATTRIBUTES BURNED OFF BY IC (p.248): Acid eats Firewall,
441
481
  * Binder eats Data Processing, Jammer eats Attack, Marker eats Sleaze.
@@ -571,6 +611,10 @@ export declare class Player extends AbstractPlayer {
571
611
  private static readonly ATTR_EATER;
572
612
  private static readonly ATTR_NAME;
573
613
  /** The undamaged array, before any IC reduction. */
614
+ /** Set on an IC actor (utilities/ic-actors.ts): "IC uses the Matrix attributes of its host" (p.247). */
615
+ icHost?: Host;
616
+ /** The IC kind an IC actor was launched as -- the name the host's running list carries. */
617
+ icKind?: string;
574
618
  baseMatrixAttribute(kind: 'attack' | 'sleaze' | 'dataProcessing' | 'firewall'): number;
575
619
  /**
576
620
  * THE LIVE LINK -- what makes this runner reachable and lit (p.223,
@@ -16,40 +16,11 @@ export const STANCE_CYCLE = ['manual', 'guarded', 'aggressive'];
16
16
  export function isPhysicalPlane(plane) {
17
17
  return plane === 'meat' || plane === 'drone';
18
18
  }
19
- /**
20
- * ARE THESE TWO STANDING ON THE SAME SIDE OF A HOST'S WALL?
21
- *
22
- * The companion to fileReach, and it exists for the same reason: the
23
- * boundary was being asked about in more than one place and the copies
24
- * disagreed. fileReach settled it for FILES; nothing settled it for
25
- * ICONS, so `look` honoured insideHost while every verb that picks a
26
- * target read the room's raw actor list and saw straight through the
27
- * wall. A technomancer standing inside a host was told "Empty
28
- * architecture -- turning geometry and nothing in it worth the trip" by
29
- * one surface and offered those same two IC as spike targets by the
30
- * next.
31
- *
32
- * CANON MAKES THE PANEL BINDING, not the target list. Inside a host you
33
- * see and interact with the icons inside it and not the ones outside,
34
- * and from the grid the inside is unreachable (p.246) -- and threading
35
- * adds its own half of the same rule: "You can only use complex forms
36
- * on icons you've spotted" (p.251). An icon the room refuses to name is
37
- * an icon nobody has spotted, so targeting it was never legal.
38
- *
39
- * BOTH IN THE SAME ROOM IS THE PRECONDITION, and every caller has it --
40
- * these are room-scoped target lists. Given that, comparing insideHost
41
- * directly is the whole rule: both undefined means both are out on the
42
- * open grid, both equal to the room means both are inside its
43
- * architecture. There is deliberately no third state.
44
- *
45
- * MEAT AND ASTRAL ACTORS PASS TRIVIALLY, which is correct and not an
46
- * oversight: insideHost is persona state, so a body in the room has
47
- * none. The plane filters the callers already apply are what keep those
48
- * actors out of a Matrix target list; this function answers one question
49
- * and is not a second plane check.
50
- */
51
19
  export function sameHostSide(actor, other) {
52
- return actor.insideHost === other.insideHost;
20
+ // A real Player answers with its Host; a bare fixture ({ insideHost })
21
+ // answers with whatever it carries. Both sides read the same way.
22
+ const sideOf = (p) => p.hostInside ?? p.insideHost;
23
+ return sideOf(actor) === sideOf(other);
53
24
  }
54
25
  export const SKILL_NAMES = [
55
26
  'blades', 'clubs', 'unarmed', 'archery', 'free-fall',
@@ -426,7 +397,68 @@ export class Player extends AbstractPlayer {
426
397
  * Session state like hostAlert and linkLockedBy: leaving the Matrix
427
398
  * ends it, so nothing here is persisted.
428
399
  */
429
- insideHost;
400
+ /**
401
+ * WHERE THE PERSONA IS (increment 2 of the Matrix position model): on a
402
+ * grid, or inside a host it entered from one. Session-only, like
403
+ * bodyRoom. The body stays in currentLocation on every plane -- the
404
+ * persona never writes it. The setter fires the live-sheet beat, which
405
+ * is the beat the currentLocation setter used to fire on enter/exit.
406
+ * `grid` may be unknown (fixtures, and increment 2 itself): unknown is
407
+ * not "another grid", so it costs nothing.
408
+ */
409
+ _matrixPosition;
410
+ get matrixPosition() { return this._matrixPosition; }
411
+ set matrixPosition(v) {
412
+ const same = v === this._matrixPosition
413
+ || (v !== undefined && this._matrixPosition !== undefined && v.kind === this._matrixPosition.kind
414
+ && (v.kind === 'host' ? v.host === this._matrixPosition.host
415
+ : v.grid?.key === this._matrixPosition.grid?.key));
416
+ if (same)
417
+ return;
418
+ this._matrixPosition = v;
419
+ this.noteConditionChanged();
420
+ }
421
+ /** The grid the persona rides, when it is on one. */
422
+ get currentGrid() {
423
+ return this._matrixPosition?.kind === 'grid' ? this._matrixPosition.grid : undefined;
424
+ }
425
+ /** The host the persona is inside, when it is inside one. */
426
+ get hostInside() {
427
+ return this._matrixPosition?.kind === 'host' ? this._matrixPosition.host : undefined;
428
+ }
429
+ /**
430
+ * @deprecated compat through increment 4 -- read hostInside / write
431
+ * matrixPosition. The room a host stands over, when inside one. The
432
+ * setter still builds the position so every old writer keeps working.
433
+ */
434
+ _compatInsideRoom;
435
+ get insideHost() { return this.hostInside?.over ?? this._compatInsideRoom; }
436
+ set insideHost(room) {
437
+ if (room) {
438
+ // A room you are inside of IS a host. Fixtures (and older callers)
439
+ // set insideHost on a plain Room; install the Host they meant.
440
+ if (!room.host)
441
+ room.hasNode = true;
442
+ const host = room.host;
443
+ if (!host) {
444
+ // Not a real Room (a fixture stub): remember it so the getter and
445
+ // sameHostSide still answer, without a position to stand on.
446
+ this._compatInsideRoom = room;
447
+ return;
448
+ }
449
+ this._compatInsideRoom = undefined;
450
+ this.matrixPosition = { kind: 'host', host, from: this.currentGrid ?? this.lastGrid };
451
+ }
452
+ else {
453
+ this._compatInsideRoom = undefined;
454
+ if (this._matrixPosition?.kind === 'host')
455
+ this.matrixPosition = { kind: 'grid', grid: this._matrixPosition.from };
456
+ }
457
+ }
458
+ /** The grid last stood on, so an exit built through the old setter returns there. */
459
+ get lastGrid() {
460
+ return this._matrixPosition?.kind === 'host' ? this._matrixPosition.from : this._matrixPosition?.grid;
461
+ }
430
462
  /**
431
463
  * MATRIX ATTRIBUTES BURNED OFF BY IC (p.248): Acid eats Firewall,
432
464
  * Binder eats Data Processing, Jammer eats Attack, Marker eats Sleaze.
@@ -647,7 +679,13 @@ export class Player extends AbstractPlayer {
647
679
  dataProcessing: 'Data Processing', firewall: 'Firewall',
648
680
  };
649
681
  /** The undamaged array, before any IC reduction. */
682
+ /** Set on an IC actor (utilities/ic-actors.ts): "IC uses the Matrix attributes of its host" (p.247). */
683
+ icHost;
684
+ /** The IC kind an IC actor was launched as -- the name the host's running list carries. */
685
+ icKind;
650
686
  baseMatrixAttribute(kind) {
687
+ if (this.icHost)
688
+ return this.icHost.attributes()[kind];
651
689
  if (this.isTechnomancer()) {
652
690
  switch (kind) {
653
691
  case 'attack': return this.charisma;
@@ -922,7 +960,13 @@ export class Player extends AbstractPlayer {
922
960
  /** Routes Matrix combat damage into the jacked-in deck's hardware track. */
923
961
  takeMatrixDamage(boxes) {
924
962
  const deck = this.getAnyCyberdeck();
925
- return deck ? deck.takeDeckDamage(boxes) : 0;
963
+ const taken = deck ? deck.takeDeckDamage(boxes) : 0;
964
+ // DECK DAMAGE BEATS (web report 2026-09-02: "the damage to the deck is not moving in realtime"):
965
+ // the Item has no path to its owner, so the owner fires here, and the
966
+ // exchange and rest fire on their own deck writes.
967
+ if (taken > 0)
968
+ this.noteConditionChanged();
969
+ return taken;
926
970
  }
927
971
  get matrixCrashed() {
928
972
  return this.getAnyCyberdeck()?.isBricked ?? false;