@maka/maka-cli 5.219.0 → 5.220.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.219.0",
3
+ "version": "5.220.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.",
@@ -313,10 +313,28 @@ export class SayCommand extends Command {
313
313
  // presence on the grid and is not -- "call" is still the verb that
314
314
  // reaches a body.
315
315
  if (currentPlayer.plane === 'matrix' && !(this.actor instanceof NPC)) {
316
- const heard = speakOnTheGrid(this.scene, currentPlayer, '').map(a => a.name);
317
- return heard.length > 0
318
- ? `Your persona speaks and the grid carries it: ${heard.join(', ')} ${heard.length === 1 ? 'is' : 'are'} on this side of the architecture and hears you. Bodies in the room hear nothing -- you have no voice out there.`
319
- : `Your words go out over the grid and find no persona to land on. Anyone here in the FLESH hears nothing -- a PAN icon is a commlink in somebody's pocket, not a person you can talk to. ("call <name>" reaches a body.)`;
316
+ const { personas, links } = speakOnTheGrid(this.scene, currentPlayer, '');
317
+ const iconNames = personas.map(a => a.name);
318
+ const linkNames = links.map(a => a.name);
319
+ const lines = [];
320
+ if (iconNames.length > 0) {
321
+ lines.push(`Your persona speaks and the grid carries it: ${iconNames.join(', ')} ${iconNames.length === 1 ? 'is' : 'are'} on this side of the architecture and hears you.`);
322
+ }
323
+ // A COMMLINK IS FOR TALKING (FhXadizkMCxAdMaJx). This used to be
324
+ // the refusal -- "a PAN icon is a commlink in somebody's pocket,
325
+ // not a person you can talk to" -- which had the fact right and
326
+ // drew the wrong conclusion. It IS a commlink in their pocket, and
327
+ // that is precisely a thing you can speak out of.
328
+ if (linkNames.length > 0) {
329
+ lines.push(`It comes out of ${linkNames.join(', ')}'s commlink where ${linkNames.length === 1 ? 'they stand' : 'they stand'} in the flesh -- your voice out of ${linkNames.length === 1 ? 'their' : 'their'} pocket, not out of the air.`);
330
+ }
331
+ if (lines.length === 0) {
332
+ return `Your words go out over the grid and land on nothing. No persona on this side of the architecture, and nobody near your body is carrying a live commlink for them to come out of. ("call <name>" reaches somebody further off.)`;
333
+ }
334
+ if (linkNames.length === 0) {
335
+ lines.push(`Bodies in the room hear nothing -- nobody there is carrying a live link.`);
336
+ }
337
+ return lines.join('\n');
320
338
  }
321
339
  return farOff;
322
340
  }
@@ -52,10 +52,21 @@ export class ShoutCommand extends Command {
52
52
  // each other or they do not, and no amount of volume crosses a
53
53
  // host wall. So this routes to the same fan-out `say` uses rather
54
54
  // than inventing a louder one.
55
- const heard = speakOnTheGrid(this.scene, actor, message).map(a => a.name);
56
- return heard.length > 0
57
- ? `You push it out across the grid: "${message}"\n Heard by ${heard.join(', ')}.`
58
- : `You push it out across the grid -- nothing out here is listening.`;
55
+ // Both halves of the fan-out, named apart: a persona beside you and
56
+ // a commlink in somebody's pocket are two different places for your
57
+ // voice to come out of, and only one of them is standing in the
58
+ // same room as your body (FhXadizkMCxAdMaJx).
59
+ const { personas, links } = speakOnTheGrid(this.scene, actor, message);
60
+ const heard = personas.map(a => a.name);
61
+ const onLink = links.map(a => a.name);
62
+ if (heard.length === 0 && onLink.length === 0) {
63
+ return `You push it out across the grid -- nothing out here is listening.`;
64
+ }
65
+ const who = [
66
+ ...(heard.length > 0 ? [`Heard by ${heard.join(', ')}.`] : []),
67
+ ...(onLink.length > 0 ? [`Out of ${onLink.join(', ')}'s commlink, where they stand in the flesh.`] : []),
68
+ ];
69
+ return `You push it out across the grid: "${message}"\n ${who.join('\n ')}`;
59
70
  }
60
71
  const heard = actor.currentLocation.shout(actor, message);
61
72
  if (!(this.actor instanceof NPC)) {
@@ -1205,5 +1205,35 @@
1205
1205
  // other at all. The meat case is pinned in the same test file, because
1206
1206
  // a fix that traded this report for the older one it was built from
1207
1207
  // would be no fix.
1208
- export const ENGINE_VERSION = '1.78.0';
1208
+ // 1.79.0 (2026-09-16): A COMMLINK IS FOR TALKING (FhXadizkMCxAdMaJx,
1209
+ // Maka, rejecting the first fix: "this looks like it's backwards. If I
1210
+ // jack in, and Grunt's icon is in reach, he SHOULD hear me through his
1211
+ // icon when I do a 'say'"). He is right and the books agree. p.246
1212
+ // names this as the exception to the host wall itself -- from outside
1213
+ // a host "you can't interact directly with icons inside it, ALTHOUGH
1214
+ // you can still send messages, make commcalls, and that sort of thing"
1215
+ // -- and nothing anywhere requires the person on the other end to be
1216
+ // jacked in. The old answer, "a PAN icon is a commlink in somebody's
1217
+ // pocket, not a person you can talk to", had the fact right and drew
1218
+ // the wrong conclusion from it: a commlink in somebody's pocket is
1219
+ // exactly a thing a voice can come out of.
1220
+ // - `say` and `shout` from the grid now reach the PERSONAS beside you
1221
+ // (as always) AND the live commlinks of people standing around your
1222
+ // BODY, delivered with hear()'s existing viaLink so the NPC knows it
1223
+ // came out of their pocket rather than from a mouth in the room.
1224
+ // - SCOPED TO THE VICINITY, not the district. Canon would let a
1225
+ // persona call a commlink anywhere on the grid -- distance is noise,
1226
+ // not range -- but a `say` that rang every commlink in the barrens
1227
+ // is a broadcast nobody asked for. "In reach" means what it means in
1228
+ // the icon list the reporter was reading: the room the body is in.
1229
+ // `call <name>` still reaches further.
1230
+ // - isLinked is the test, the same predicate that decides whether a
1231
+ // body is a ▣ icon at all, so the icon list and the voice agree: no
1232
+ // link, no icon, no pocket for the words to come out of -- and the
1233
+ // refusal says which of the two reasons it was.
1234
+ // The old test asserted the right refusal for the WRONG REASON: its
1235
+ // comment claimed "Krow is HERE, in the flesh, with a PAN icon on the
1236
+ // grid" while its fixture gave him no commlink at all, so there was
1237
+ // never a PAN icon in it. Pinned properly now, both ways.
1238
+ export const ENGINE_VERSION = '1.79.0';
1209
1239
  //# sourceMappingURL=engine-version.js.map
@@ -1,5 +1,6 @@
1
1
  import { roomsInReach } from './grid-reach.js';
2
2
  import { NPC } from '../models/npc.js';
3
+ import { isLinked } from './comm-style.js';
3
4
  /**
4
5
  * WHO A PERSONA CAN REACH (increment 2 of the Matrix position model,
5
6
  * 2026-09-02). The persona's position is `Player.matrixPosition` -- on a
@@ -35,13 +36,62 @@ export function cybercombatCandidates(scene, actor) {
35
36
  export function speechAudience(scene, actor) {
36
37
  return personasOnMySide(scene, actor);
37
38
  }
38
- /** Delivers a persona's words to its audience, the way Room.say delivers a body's. */
39
+ /**
40
+ * THE COMMLINKS IN EARSHOT OF YOUR BODY (FhXadizkMCxAdMaJx, Maka: "this
41
+ * looks like it's backwards. If I jack in, and Grunt's icon is in reach,
42
+ * he SHOULD hear me through his icon when I do a 'say'").
43
+ *
44
+ * He is right, and the books agree. A commlink is a COMMUNICATION
45
+ * device: p.246 says that even across a host wall "you can still send
46
+ * messages, make commcalls, and that sort of thing", and nothing
47
+ * anywhere requires the person on the other end to be jacked in -- a
48
+ * commcall rings a commlink in somebody's pocket, which is what a
49
+ * commlink is for. The engine's old answer, "a PAN icon is a commlink in
50
+ * somebody's pocket, not a person you can talk to", had the fact right
51
+ * and drew the wrong conclusion from it.
52
+ *
53
+ * SCOPED TO THE VICINITY, not the district. Canon would let a persona
54
+ * call a commlink anywhere on the grid -- distance is noise, not range
55
+ * -- but `say` is the verb for speaking to the people around you, and a
56
+ * `say` that rang every commlink in the barrens would be a broadcast
57
+ * nobody asked for. The room your body is slumped in is what "in reach"
58
+ * means in the icon list the reporter was reading, so it is what it
59
+ * means here. `call <name>` is still the verb for reaching somebody
60
+ * further off, and it still works.
61
+ *
62
+ * A PAN IS THE TEST, not a pulse: isLinked is the same predicate that
63
+ * decides whether a body shows up as a ▣ icon at all. Somebody with no
64
+ * commlink, or running silent, has nothing for your voice to come out
65
+ * of -- and does not appear in the icon list either, so the two answers
66
+ * agree.
67
+ */
68
+ export function panAudience(scene, actor) {
69
+ const here = actor.bodyRoom ?? actor.currentLocation;
70
+ if (!here)
71
+ return [];
72
+ return scene.allActors.filter(a => a !== actor
73
+ && a.plane === 'meat'
74
+ && (a.bodyRoom ?? a.currentLocation) === here
75
+ && isLinked(a));
76
+ }
77
+ /**
78
+ * Delivers a persona's words to its audience, the way Room.say delivers
79
+ * a body's -- to the personas beside it, and out of the commlinks of the
80
+ * people standing around its body.
81
+ */
39
82
  export function speakOnTheGrid(scene, actor, message) {
40
- const audience = speechAudience(scene, actor);
41
- for (const recipient of audience) {
83
+ const personas = speechAudience(scene, actor);
84
+ for (const recipient of personas) {
42
85
  void Promise.resolve(recipient.hear(actor, message, { broadcast: true })).catch(() => undefined);
43
86
  }
44
- return audience;
87
+ // viaLink, so the NPC knows this arrived out of their commlink rather
88
+ // than from a mouth in the room -- which is the whole difference, and
89
+ // the thing their reply has to be able to react to.
90
+ const links = panAudience(scene, actor);
91
+ for (const recipient of links) {
92
+ void Promise.resolve(recipient.hear(actor, message, { broadcast: true, viaLink: true })).catch(() => undefined);
93
+ }
94
+ return { personas, links };
45
95
  }
46
96
  /**
47
97
  * The combat exchange's "same place": on the matrix plane it is the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.219.0",
3
+ "version": "5.220.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.",