@maka/maka-cli 5.218.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.
- package/bundle/typescript/package.json +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +22 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/shout.js +15 -4
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +51 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/matrix-roster.js +54 -4
- package/bundle/typescript/src/commands/game/sideQuest/utilities/npc-authorization.js +20 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
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
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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)) {
|
|
@@ -1185,5 +1185,55 @@
|
|
|
1185
1185
|
// read the win condition without reading completion would make the job
|
|
1186
1186
|
// unwinnable, which is a worse bug than the one being fixed, and that
|
|
1187
1187
|
// is the load-bearing test in the file.
|
|
1188
|
-
|
|
1188
|
+
// 1.78.0 (2026-09-16): CYBERCOMBAT IS NOT LAYING HANDS ON ANYBODY
|
|
1189
|
+
// (27dAZvLuoaW4PjF6s, Maka, the whole report verbatim):
|
|
1190
|
+
// "[order] Agent (Sony CIY-720): attack patrol ic ,
|
|
1191
|
+
// You can't lay hands on Patrol IC: they are not present in their
|
|
1192
|
+
// body, and there is nobody home to resist you."
|
|
1193
|
+
// The absent-body gate in npc-authorization.ts exists for a good
|
|
1194
|
+
// reason -- somebody grabbing a jacked-in runner's slumped shell is
|
|
1195
|
+
// reaching for a person who is not home -- but it asked the question
|
|
1196
|
+
// of every AI-driven actor on every contact verb. An Agent is a
|
|
1197
|
+
// matrix-plane construct (companions.ts builds it with plane:
|
|
1198
|
+
// 'matrix') and Patrol IC has no body to be absent FROM, so a rule
|
|
1199
|
+
// about hands fired on a program attacking a program and refused the
|
|
1200
|
+
// most ordinary order anyone gives an agent. Attacking an icon is
|
|
1201
|
+
// Cybercombat (Data Spike, p.239): two icons, no meat, nobody's hands
|
|
1202
|
+
// anywhere near it. The question is now asked only of an actor
|
|
1203
|
+
// standing in the meat; canPerceive, which is the rule that actually
|
|
1204
|
+
// knows about planes, still decides whether the two can reach each
|
|
1205
|
+
// other at all. The meat case is pinned in the same test file, because
|
|
1206
|
+
// a fix that traded this report for the older one it was built from
|
|
1207
|
+
// would be no fix.
|
|
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';
|
|
1189
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
|
-
/**
|
|
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
|
|
41
|
-
for (const recipient of
|
|
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
|
-
|
|
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
|
|
@@ -66,7 +66,26 @@ export function authorizeNpcAction(scene, actor, verb, args) {
|
|
|
66
66
|
// which is lying right there in the room and is what the act was
|
|
67
67
|
// reaching for. For a hands-on verb the absent-body reason is the one
|
|
68
68
|
// that tells the fiction something it can use.
|
|
69
|
-
|
|
69
|
+
// ...AND IT IS A RULE ABOUT HANDS, SO IT ONLY BINDS A BODY
|
|
70
|
+
// (27dAZvLuoaW4PjF6s, Maka -- the whole report, verbatim):
|
|
71
|
+
//
|
|
72
|
+
// "[order] Agent (Sony CIY-720): attack patrol ic ,
|
|
73
|
+
// You can't lay hands on Patrol IC: they are not present in their
|
|
74
|
+
// body, and there is nobody home to resist you."
|
|
75
|
+
//
|
|
76
|
+
// An Agent is a matrix-plane construct (commands/companions.ts builds
|
|
77
|
+
// it with plane: 'matrix') and Patrol IC has no body to be absent
|
|
78
|
+
// from -- so a gate written for "somebody grabbed a jacked-in
|
|
79
|
+
// runner's slumped shell" fired on a program attacking a program,
|
|
80
|
+
// and refused the single most ordinary order anyone gives an agent.
|
|
81
|
+
//
|
|
82
|
+
// Attacking an icon is CYBERCOMBAT (Data Spike, p.239): two icons,
|
|
83
|
+
// no meat, nobody's hands anywhere near it. So the absent-body
|
|
84
|
+
// question is only asked of an actor standing in the meat. Everything
|
|
85
|
+
// else is still gated -- canPerceive below decides whether these two
|
|
86
|
+
// can reach each other at all, and it is the rule that actually knows
|
|
87
|
+
// about planes.
|
|
88
|
+
if (contact && actor.plane === 'meat' && !isPresentInBody(target)) {
|
|
70
89
|
return {
|
|
71
90
|
allowed: false,
|
|
72
91
|
reason: `You can't lay hands on ${target.name}: they are not present in their body, and there is nobody home to resist you. Describe what you see, or wait for them to come back.`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
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.",
|