@maka/maka-cli 5.182.0 → 5.184.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.
- package/bundle/typescript/package.json +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/ar.js +6 -6
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/delay.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/disable.js +331 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/download.js +3 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-turn.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/enter-host.js +11 -13
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +17 -17
- package/bundle/typescript/src/commands/game/sideQuest/commands/hop.js +2 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +5 -5
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/mark.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +78 -31
- package/bundle/typescript/src/commands/game/sideQuest/commands/snoop.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sprites.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/tap.js +7 -7
- package/bundle/typescript/src/commands/game/sideQuest/commands/thread.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/game.js +7 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/device.js +40 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/host.js +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/ar.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-turn.js +2 -2
- package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-reach.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js +44 -18
- package/bundle/typescript/src/commands/game/sideQuest/utilities/matrix-style.js +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/overwatch.js +2 -2
- package/bundle/typescript/src/commands/game/sideQuest/utilities/perception.js +50 -21
- package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +6 -2
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +3 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/shared-run.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/surveillance.js +4 -0
- package/package.json +1 -1
|
@@ -16,6 +16,25 @@ import { isWatched, camerasLive, canSnoopFeeds } from './surveillance.js';
|
|
|
16
16
|
* ▣ PAN, ▲ dangerous device, ◇ data. (There is no air-gapped-host glyph
|
|
17
17
|
* any more -- see hhSWzLSXECFtfoAGa and canReachHost.)
|
|
18
18
|
*/
|
|
19
|
+
/**
|
|
20
|
+
* A DEVICE ICON, AND WHAT WORKS IT (commands/disable.ts, 2026-09-13).
|
|
21
|
+
* Control Device (p.238) needs 2 marks on the device -- "disable" -- and
|
|
22
|
+
* a Data Spike (p.239) needs none -- "brick". A bricked lock stays
|
|
23
|
+
* locked (p.228), so its line says so rather than offering a way in.
|
|
24
|
+
*/
|
|
25
|
+
export function deviceIconLine(device, actor) {
|
|
26
|
+
const dr = `{bold}[DR ${device.rating}]{/bold}`;
|
|
27
|
+
if (device.isOpen())
|
|
28
|
+
return `▤ ${device.name} ${dr} -- its icon standing open, nothing left to hold.`;
|
|
29
|
+
if (device.isBricked) {
|
|
30
|
+
return `▤ ${device.name} ${dr} -- BRICKED, dead electronics${device.kind === 'lock' ? '; the lock stays locked (p.228)' : ''}.`;
|
|
31
|
+
}
|
|
32
|
+
const marks = device.marksBy.get(actor.name) ?? 0;
|
|
33
|
+
const way = marks >= 2
|
|
34
|
+
? `"disable ${device.name}" (Control Device, ${marks} marks held)`
|
|
35
|
+
: `"hack ${device.name}" for marks (${marks}/2 to command it), or "brick ${device.name}"`;
|
|
36
|
+
return `▤ ${device.name} ${dr} -- a device icon shaped like the thing itself, shut. (${way})`;
|
|
37
|
+
}
|
|
19
38
|
export function gridIcons(scene, actor, room) {
|
|
20
39
|
// INSIDE A HOST IS A DIFFERENT PLACE (SR5 p.246, and Deditri's
|
|
21
40
|
// eJANPpm7qCaCAJBJw). "The virtual space inside a host is separate
|
|
@@ -202,9 +221,7 @@ export function gridIcons(scene, actor, room) {
|
|
|
202
221
|
for (const device of room.devices) {
|
|
203
222
|
if (!device.broadcastsAro())
|
|
204
223
|
continue;
|
|
205
|
-
icons.push(device
|
|
206
|
-
? `▤ ${device.name} {bold}[DR ${device.rating}]{/bold} -- its icon standing open, nothing left to hold.`
|
|
207
|
-
: `▤ ${device.name} {bold}[DR ${device.rating}]{/bold} -- a device icon shaped like the thing itself, shut. ("hack ${device.name}")`);
|
|
224
|
+
icons.push(deviceIconLine(device, actor));
|
|
208
225
|
}
|
|
209
226
|
// The cameras, as a THING (surveillance.ts): live for security, looped,
|
|
210
227
|
// or yours to ride.
|
|
@@ -306,7 +323,16 @@ function hostInteriorIcons(scene, actor, room) {
|
|
|
306
323
|
const data = room.offlineServer
|
|
307
324
|
? []
|
|
308
325
|
: room.inventory.getAllItems().filter(i => i.plane === 'matrix');
|
|
309
|
-
|
|
326
|
+
// THE ARCHIVE IS NOT A GLANCE (p.241, player ruling 2026-09-13): a
|
|
327
|
+
// persona sees the host's files after a Matrix Search turns them up
|
|
328
|
+
// (commands/search.ts, Host.searchedBy), or when the host is cracked
|
|
329
|
+
// and they spill open. Until then a look says only that there is an
|
|
330
|
+
// archive to search.
|
|
331
|
+
const found = room.hostCracked || (room.host?.searchedBy.has(actor.name) ?? false);
|
|
332
|
+
if (data.length > 0 && !found) {
|
|
333
|
+
icons.push(`◇ The host's archive -- files in here somewhere, and a glance does not read an archive. ("search" runs a Matrix Search, p.241)`);
|
|
334
|
+
}
|
|
335
|
+
for (const item of found ? data : []) {
|
|
310
336
|
icons.push(room.hostCracked
|
|
311
337
|
? `◇ ${item.name} [data] -- unsealed, sitting open in the host's archive. ("take" it)`
|
|
312
338
|
: `◇ ${item.name} [data] -- SEALED in the host's archive. ("hack" the host to break the seal)`);
|
|
@@ -414,20 +440,20 @@ export function gridSculpt(actor, room) {
|
|
|
414
440
|
const purpose = room.host?.purpose;
|
|
415
441
|
const sculpt = room.host?.sculpt;
|
|
416
442
|
return [
|
|
417
|
-
`{
|
|
418
|
-
`{
|
|
443
|
+
`{lightblue-fg}INSIDE :: ${hostLabel(room, { capital: true }).toUpperCase()} [HR ${room.hostRating}]${purpose ? ` -- ${purpose}` : ''}{/lightblue-fg}`,
|
|
444
|
+
`{lightblue-fg}${sculpt ?? 'Architecture all the way around -- the outside grid is a wall away and might as well be weather.'}{/lightblue-fg}`,
|
|
419
445
|
].join('\n');
|
|
420
446
|
}
|
|
421
447
|
// ON THE GRID (SR5 pp.217-218): no street under the persona -- a black
|
|
422
448
|
// flatland, the body's room named only because that is where the
|
|
423
449
|
// signal comes from. A host directly overhead gets a line of its own.
|
|
424
450
|
const body = actor.bodyRoom ?? room;
|
|
425
|
-
const header = `{
|
|
451
|
+
const header = `{lightblue-fg}THE GRID :: jacked in from ${body.name.toUpperCase()}{/lightblue-fg}`;
|
|
426
452
|
const grid = actor.currentGrid;
|
|
427
|
-
const flat = `{
|
|
453
|
+
const flat = `{lightblue-fg}Black flatland under a black sky -- the icons are the only things out here.${grid ? ` Riding ${grid.name}${grid.userPenalty ? ' (-2 on everything you do, p.233)' : ''}.` : ''}{/lightblue-fg}`;
|
|
428
454
|
if (!room.hasNode)
|
|
429
455
|
return `${header}\n${flat}`;
|
|
430
|
-
return `${header}\n${flat}\n{
|
|
456
|
+
return `${header}\n${flat}\n{lightblue-fg}${hostLabel(room, { capital: true })} hangs directly overhead, sealed. What it holds is inside it.{/lightblue-fg}`;
|
|
431
457
|
}
|
|
432
458
|
/**
|
|
433
459
|
* The icons rendered as the standard cyan block, or the thin-grid line.
|
|
@@ -456,19 +482,19 @@ export function gridIconBlock(scene, actor, room, rooms) {
|
|
|
456
482
|
if (actor.insideHost || !rooms) {
|
|
457
483
|
const icons = gridIcons(scene, actor, room);
|
|
458
484
|
if (icons.length === 0) {
|
|
459
|
-
return `{
|
|
485
|
+
return `{lightblue-fg}Thin grid out here -- background noise, nothing worth cracking.{/lightblue-fg}`;
|
|
460
486
|
}
|
|
461
|
-
return `{
|
|
487
|
+
return `{lightblue-fg}ICONS IN REACH:{/lightblue-fg}\n${icons.map(i => ` {lightblue-fg}${i}{/lightblue-fg}`).join('\n')}`;
|
|
462
488
|
}
|
|
463
489
|
const out = [];
|
|
464
490
|
const hosts = hostsInReach(rooms, actor);
|
|
465
491
|
out.push(hosts.length > 0
|
|
466
|
-
? `{
|
|
467
|
-
: `{
|
|
492
|
+
? `{lightblue-fg}HOSTS OVERHEAD:{/lightblue-fg}\n${hosts.map(h => ` {lightblue-fg}${hostLine(h, actor)}{/lightblue-fg}`).join('\n')}`
|
|
493
|
+
: `{lightblue-fg}Nothing overhead -- no host stands over this district.{/lightblue-fg}`);
|
|
468
494
|
const near = gridIcons(scene, actor, gridVicinity(actor)).filter(i => !isHostLine(i));
|
|
469
495
|
out.push(near.length > 0
|
|
470
|
-
? `{
|
|
471
|
-
: `{
|
|
496
|
+
? `{lightblue-fg}ICONS IN REACH:{/lightblue-fg}\n${near.map(i => ` {lightblue-fg}${i}{/lightblue-fg}`).join('\n')}`
|
|
497
|
+
: `{lightblue-fg}Nothing near -- background noise around your signal.{/lightblue-fg}`);
|
|
472
498
|
// FARTHER OUT, WITHOUT AN ADDRESS (SR5 p.235, p.243): Matrix Perception
|
|
473
499
|
// never tells you where a device physically sits -- that is Trace Icon,
|
|
474
500
|
// two marks and an opposed test. So far icons are listed by the noise
|
|
@@ -493,9 +519,9 @@ export function gridIconBlock(scene, actor, room, rooms) {
|
|
|
493
519
|
byBand.set(noise, list);
|
|
494
520
|
}
|
|
495
521
|
const far = [...byBand.entries()].sort((a, b) => a[0] - b[0])
|
|
496
|
-
.map(([noise, names]) => ` {
|
|
522
|
+
.map(([noise, names]) => ` {lightblue-fg}· dim, noise -${noise}: ${names.join(', ')}{/lightblue-fg}`);
|
|
497
523
|
if (far.length > 0)
|
|
498
|
-
out.push(`{
|
|
524
|
+
out.push(`{lightblue-fg}FARTHER OUT -- dimmer with distance:{/lightblue-fg}\n${far.join('\n')}`);
|
|
499
525
|
// A blank line between the distance bands (user, 2026-09-02): three
|
|
500
526
|
// sections read as three when they do not touch.
|
|
501
527
|
return out.join('\n\n');
|
|
@@ -510,7 +536,7 @@ export function gridIconBlock(scene, actor, room, rooms) {
|
|
|
510
536
|
* the other personas, and the files. Cut to the dock's rows and columns.
|
|
511
537
|
*/
|
|
512
538
|
export function hostInteriorPanel(scene, actor, host, rows, cols) {
|
|
513
|
-
const cut = (raw) => `{
|
|
539
|
+
const cut = (raw) => `{lightblue-fg}${[...raw].slice(0, Math.max(4, cols)).join('')}{/lightblue-fg}`;
|
|
514
540
|
const lines = [];
|
|
515
541
|
lines.push(cut(`INSIDE ${host.name}`));
|
|
516
542
|
if (host.purpose)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const MATRIX_COLOR = 'lightblue';
|
|
2
|
+
export const FEED_COLOR = 'white';
|
|
3
|
+
export const TURN_COLOR = 'yellow';
|
|
4
|
+
/** Matrix-plane prose, tagged. */
|
|
5
|
+
export function mx(text) {
|
|
6
|
+
return `{${MATRIX_COLOR}-fg}${text}{/${MATRIX_COLOR}-fg}`;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=matrix-style.js.map
|
|
@@ -58,10 +58,10 @@ export function accrueOverwatch(scene, actor, defenseHits, why) {
|
|
|
58
58
|
// The grid's built-in warning system (p.221): subtle ripples, once
|
|
59
59
|
// per band -- the only free hint the player ever gets.
|
|
60
60
|
if (before < 30 && actor.overwatchScore >= 30) {
|
|
61
|
-
return { lines: [`{
|
|
61
|
+
return { lines: [`{lightblue-fg}The grid RIPPLES -- hard, close, and looking. Something vast has nearly found the thread you're hanging by. ("reboot" is a clean slate.){/lightblue-fg}`], converged: false };
|
|
62
62
|
}
|
|
63
63
|
if (before < 20 && actor.overwatchScore >= 20) {
|
|
64
|
-
return { lines: [`{
|
|
64
|
+
return { lines: [`{lightblue-fg}A ripple crosses the grid -- subtle, wrong, deliberate. Somewhere above, an eye has started counting your fingerprints.{/lightblue-fg}`], converged: false };
|
|
65
65
|
}
|
|
66
66
|
return { lines: [], converged: false };
|
|
67
67
|
}
|
|
@@ -39,36 +39,65 @@ import { mentalLimit } from './limits.js';
|
|
|
39
39
|
* an active search mechanically different from a passive glance. */
|
|
40
40
|
export const ACTIVELY_LOOKING = 3;
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
42
|
+
* THE MEAT PERCEPTION TEST. The Matrix used to ride through here as a
|
|
43
|
+
* "different test with a different bracket" -- Logic + the better of
|
|
44
|
+
* Perception and Computer [Data Processing] -- which was neither Matrix
|
|
45
|
+
* Perception nor Matrix Search. It is matrixSearchTest now, below; the
|
|
46
|
+
* grid never comes through this door.
|
|
47
|
+
*/
|
|
48
|
+
export function perceptionTest(actor, opts = {}) {
|
|
49
|
+
const skill = actor.skillRating('perception');
|
|
50
|
+
// Unskilled defaults at -1 (p.130), exactly as both callers already did.
|
|
51
|
+
const pool = Math.max(1, actor.intuition
|
|
52
|
+
+ (skill > 0 ? skill : -1)
|
|
53
|
+
+ (opts.active ? ACTIVELY_LOOKING : 0)
|
|
54
|
+
+ actor.bonus('perception')
|
|
55
|
+
+ actor.woundModifier
|
|
56
|
+
- actor.sustainingPenalty);
|
|
57
|
+
return { pool, limit: mentalLimit(actor) };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* MATRIX SEARCH (SR5 p.241, RAG-checked 2026-09-13): a Special action,
|
|
61
|
+
* "Computer + Intuition [Data Processing]" -- INTUITION, not Logic; the
|
|
62
|
+
* old grid branch above rolled Logic and called it canon. The threshold
|
|
63
|
+
* and the base time come from what is being looked for (the Matrix
|
|
64
|
+
* Search Table: public 1 / 1 minute, limited 3 / 30 minutes; inside a
|
|
65
|
+
* host the base time is one minute whatever the information), net hits
|
|
66
|
+
* over the threshold divide the time, and a failure still spends all
|
|
67
|
+
* of it. A Browse program "cuts the base time in half" (Splintered
|
|
68
|
+
* State p.54) -- time, not dice. Running silent costs its -2 like every
|
|
69
|
+
* Matrix action (p.235-236).
|
|
48
70
|
*
|
|
49
71
|
* Data Processing reads 0 for an actor with no deck at all; that is a
|
|
50
72
|
* caller's missing gate rather than a real limit, so it degrades to
|
|
51
73
|
* unlimited here instead of handing rollPool a zero it would reject.
|
|
52
74
|
*/
|
|
53
|
-
export function
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
? Math.max(actor.skillRating('perception'), actor.skillRating('computer'))
|
|
57
|
-
: actor.skillRating('perception');
|
|
58
|
-
const attribute = onGrid ? actor.logic : actor.intuition;
|
|
59
|
-
// Unskilled defaults at -1 (p.130), exactly as both callers already did.
|
|
60
|
-
const pool = Math.max(1, attribute
|
|
75
|
+
export function matrixSearchTest(actor) {
|
|
76
|
+
const skill = actor.skillRating('computer');
|
|
77
|
+
const pool = Math.max(1, actor.intuition
|
|
61
78
|
+ (skill > 0 ? skill : -1)
|
|
62
|
-
+ (opts.browse ?? 0)
|
|
63
|
-
+ (opts.active ? ACTIVELY_LOOKING : 0)
|
|
64
79
|
+ actor.bonus('perception')
|
|
80
|
+
+ (typeof actor.matrixActionPenalty === 'number' ? actor.matrixActionPenalty : 0)
|
|
65
81
|
+ actor.woundModifier
|
|
66
82
|
- actor.sustainingPenalty);
|
|
67
|
-
const dataProcessing =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
const dataProcessing = actor.matrixAttribute('dataProcessing');
|
|
84
|
+
return { pool, limit: dataProcessing > 0 ? dataProcessing : undefined };
|
|
85
|
+
}
|
|
86
|
+
/** The Matrix Search Table (p.241-242): threshold and base time in
|
|
87
|
+
* seconds. Inside a host the base time is one minute regardless. */
|
|
88
|
+
export const MATRIX_SEARCH = {
|
|
89
|
+
public: { threshold: 1, seconds: 60 },
|
|
90
|
+
inHost: { threshold: 3, seconds: 60 },
|
|
91
|
+
};
|
|
92
|
+
/** How long a search takes once rolled: the base time divided by the
|
|
93
|
+
* net hits over the threshold, never under one Combat Turn (3s); a
|
|
94
|
+
* failure spends the whole base time. Browse halves the base. */
|
|
95
|
+
export function matrixSearchSeconds(baseSeconds, hits, threshold, browse) {
|
|
96
|
+
const base = browse ? Math.ceil(baseSeconds / 2) : baseSeconds;
|
|
97
|
+
if (hits < threshold)
|
|
98
|
+
return base;
|
|
99
|
+
const net = hits - threshold;
|
|
100
|
+
return net > 0 ? Math.max(3, Math.ceil(base / net)) : base;
|
|
72
101
|
}
|
|
73
102
|
/**
|
|
74
103
|
* PERCEPTION THRESHOLDS (p.136). Named because the numbers appear in
|
|
@@ -164,6 +164,8 @@ export function serializeItem(item) {
|
|
|
164
164
|
out.loadedAmmo = item.loadedAmmo;
|
|
165
165
|
if (item.jammed)
|
|
166
166
|
out.jammed = true;
|
|
167
|
+
if (item.bricked)
|
|
168
|
+
out.bricked = true;
|
|
167
169
|
if (item.deckDamage > 0)
|
|
168
170
|
out.deckDamage = item.deckDamage;
|
|
169
171
|
if (item.droneDamage > 0)
|
|
@@ -219,6 +221,8 @@ export function restoreItem(json) {
|
|
|
219
221
|
item.loadedAmmo = json.loadedAmmo;
|
|
220
222
|
if (json.jammed)
|
|
221
223
|
item.jammed = true;
|
|
224
|
+
if (json.bricked)
|
|
225
|
+
item.bricked = true;
|
|
222
226
|
if (json.deckDamage)
|
|
223
227
|
item.takeDeckDamage(json.deckDamage);
|
|
224
228
|
if (json.droneDamage)
|
|
@@ -663,7 +667,7 @@ export function captureHubOverlay(scene, hubSeed, player, homeRoom) {
|
|
|
663
667
|
.map(n => n.player.name)
|
|
664
668
|
.filter(name => !scene.getActor(name));
|
|
665
669
|
const devices = [...collectDevices(scene).entries()]
|
|
666
|
-
.map(([name, d]) => ({ name, open: d.isOpen(), ...(d.barrierDamage > 0 ? { damage: d.barrierDamage } : {}) }));
|
|
670
|
+
.map(([name, d]) => ({ name, open: d.isOpen(), ...(d.barrierDamage > 0 ? { damage: d.barrierDamage } : {}), ...(d.isBricked ? { bricked: true } : {}) }));
|
|
667
671
|
// THE TOMBSTONE (see ISavedHub.puzzles): always [], never read here.
|
|
668
672
|
// Omitting it crashes v5.64.0's unguarded for-of on resume, and
|
|
669
673
|
// saves move between builds via the cloud mirror.
|
|
@@ -824,7 +828,7 @@ export function applyHubOverlay(scene, saved, player, homeRoom) {
|
|
|
824
828
|
for (const d of saved.devices ?? []) {
|
|
825
829
|
const live = liveDevices.get(d.name);
|
|
826
830
|
if (live)
|
|
827
|
-
live.restoreState({ open: d.open, damage: d.damage });
|
|
831
|
+
live.restoreState({ open: d.open, damage: d.damage, bricked: d.bricked });
|
|
828
832
|
}
|
|
829
833
|
// A SAVE WRITTEN BEFORE DEVICES EXISTED has no `devices` key at all,
|
|
830
834
|
// so every device would read shut -- while the door it holds stays
|
|
@@ -107,7 +107,7 @@ export function enterMatrix(scene, actor, mode) {
|
|
|
107
107
|
// half-done, which would be a fresh canon violation wearing a
|
|
108
108
|
// feature's clothes. See the PR for CgJ6uTfEvTd8JXdDN.
|
|
109
109
|
actor.activeDeck = actor.isTechnomancer() ? undefined : actor.getCyberdeck();
|
|
110
|
-
actor.performAction('jacks in', `slumping where they stand, eyes flickering (${mode}-sim)
|
|
110
|
+
actor.performAction('jacks in', `slumping where they stand, eyes flickering (${mode}-sim)`, { quiet: scene.isHumanControlled?.(actor) ?? false });
|
|
111
111
|
scene.addWorldEvent(`${actor.name} jacked into the Matrix in ${actor.bodyRoom.name}.`);
|
|
112
112
|
Logger.getInstance().write(`${actor.name} entered the Matrix (${mode}-sim, ${actor.activeDeck?.name ?? (actor.isTechnomancer() ? 'living persona' : 'no deck?!')}) from ${actor.bodyRoom.name}.`);
|
|
113
113
|
const hotWarning = mode === 'hot'
|
|
@@ -267,7 +267,7 @@ export function leaveMatrix(scene, actor, opts) {
|
|
|
267
267
|
actor.bodyRoom = undefined;
|
|
268
268
|
lines.push(`You're back in your body in ${body.name}.`);
|
|
269
269
|
}
|
|
270
|
-
actor.performAction('jacks out', 'stirring awake');
|
|
270
|
+
actor.performAction('jacks out', 'stirring awake', { quiet: scene.isHumanControlled?.(actor) ?? false });
|
|
271
271
|
scene.addWorldEvent(`${actor.name} jacked out of the Matrix.`);
|
|
272
272
|
scene.updateStatus();
|
|
273
273
|
return lines;
|
|
@@ -449,7 +449,7 @@ export function fileReach(actor, room) {
|
|
|
449
449
|
*/
|
|
450
450
|
export function planeTintItemName(item, viewerPlane) {
|
|
451
451
|
if (item.plane === 'matrix') {
|
|
452
|
-
return `{
|
|
452
|
+
return `{lightblue-fg}${item.name} [data]{/lightblue-fg}`;
|
|
453
453
|
}
|
|
454
454
|
if (item.plane === 'astral') {
|
|
455
455
|
return `{magenta-fg}${item.name} [astral]{/magenta-fg}`;
|
|
@@ -24,6 +24,7 @@ import { join, dirname } from 'node:path';
|
|
|
24
24
|
import { Logger } from './logger.js';
|
|
25
25
|
import { hint } from './hints.js';
|
|
26
26
|
import { CALL_ICON, CALL_COLOR, END_CALL_SENTINEL } from './comm-style.js';
|
|
27
|
+
import { FEED_COLOR } from './matrix-style.js';
|
|
27
28
|
import { ENGINE_VERSION } from '../engine-version.js';
|
|
28
29
|
import { authToken, fetchSave } from './cloud-saves.js';
|
|
29
30
|
import { saveSlug, restorePlayer, writeSaveAtomic } from './persistence.js';
|
|
@@ -1143,7 +1144,7 @@ export class SharedRunSession {
|
|
|
1143
1144
|
break;
|
|
1144
1145
|
}
|
|
1145
1146
|
for (const line of ev.text.split('\n'))
|
|
1146
|
-
g.logBox?.pushLine(`{
|
|
1147
|
+
g.logBox?.pushLine(`{${FEED_COLOR}-fg}${line}{/${FEED_COLOR}-fg}`);
|
|
1147
1148
|
Logger.getInstance().transcript(ev.text);
|
|
1148
1149
|
g.followLog?.();
|
|
1149
1150
|
if (ev.kind === 'death' && ev.data?.player === g.player.name) {
|
|
@@ -85,6 +85,10 @@ export function directConnectionBlocker(actor, room) {
|
|
|
85
85
|
export function camerasLive(rooms, room) {
|
|
86
86
|
if (!isWatched(room))
|
|
87
87
|
return false;
|
|
88
|
+
// Looped by Control Device or bricked by a Data Spike (commands/
|
|
89
|
+
// disable.ts): the house sees nothing from this cluster either way.
|
|
90
|
+
if (room.camerasLooped || room.camerasBricked)
|
|
91
|
+
return false;
|
|
88
92
|
if (room.hasNode)
|
|
89
93
|
return !room.hostCracked;
|
|
90
94
|
const hosts = Object.values(rooms).filter(r => r.hasNode);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.184.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|