@maka/maka-cli 5.209.0 → 5.211.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/erase-mark.js +25 -16
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +25 -22
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +66 -9
- package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js +20 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.211.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.",
|
|
@@ -101,16 +101,19 @@ export class EraseMarkCommand extends Command {
|
|
|
101
101
|
};
|
|
102
102
|
if (target.kind === 'self') {
|
|
103
103
|
for (const [placer, count] of actor.panMarksBy.entries()) {
|
|
104
|
-
if (count > 0)
|
|
105
|
-
out.push({ name: placer, count, drop: () => dec(actor.panMarksBy, placer) });
|
|
104
|
+
if (count > 0) {
|
|
105
|
+
out.push({ name: placer, count, defence: this.defenceOfPersona(placer), drop: () => dec(actor.panMarksBy, placer) });
|
|
106
|
+
}
|
|
106
107
|
}
|
|
107
108
|
for (const room of Object.values(this.scene.getRooms())) {
|
|
108
109
|
const host = room.host;
|
|
109
110
|
const n = host?.marksOn.get(actor.name) ?? 0;
|
|
110
111
|
if (n > 0 && host) {
|
|
112
|
+
const d = hostDefensePool(host);
|
|
111
113
|
out.push({
|
|
112
114
|
name: hostLabel(room),
|
|
113
115
|
count: n,
|
|
116
|
+
defence: { label: `${d.label}, rating standing in for Willpower`, pool: d.pool },
|
|
114
117
|
drop: () => {
|
|
115
118
|
dec(host.marksOn, actor.name);
|
|
116
119
|
// A key gone is a sighting gone: an icon it no longer
|
|
@@ -128,11 +131,27 @@ export class EraseMarkCommand extends Command {
|
|
|
128
131
|
// Your own key is not a mark you "erase" -- p.239 is about reaching
|
|
129
132
|
// into an icon to scrub somebody ELSE's recognition pattern.
|
|
130
133
|
if (count > 0 && placer !== actor.name) {
|
|
131
|
-
out.push({ name: placer, count, drop: () => dec(ledger, placer) });
|
|
134
|
+
out.push({ name: placer, count, defence: this.defenceOfPersona(placer), drop: () => dec(ledger, placer) });
|
|
132
135
|
}
|
|
133
136
|
}
|
|
134
137
|
return out;
|
|
135
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* A NAMED PLACER'S OWN DEFENCE. p.237-238: "when a defense test calls
|
|
141
|
+
* for a Mental attribute, use the owner's rating", and an unattended
|
|
142
|
+
* device falls back to its Device Rating. A placer who has left the
|
|
143
|
+
* scene cannot roll, so the mark comes off unopposed -- which is the
|
|
144
|
+
* honest reading of erasing a key nobody is behind any more.
|
|
145
|
+
*/
|
|
146
|
+
defenceOfPersona(name) {
|
|
147
|
+
const who = this.scene.allActors.find(a => a.name === name);
|
|
148
|
+
if (!who)
|
|
149
|
+
return { label: 'nobody behind it any more', pool: 0 };
|
|
150
|
+
return {
|
|
151
|
+
label: 'Willpower + Firewall',
|
|
152
|
+
pool: Math.max(0, who.willpower + who.matrixAttribute('firewall')),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
136
155
|
async execute(args = []) {
|
|
137
156
|
const actor = this.actor;
|
|
138
157
|
if (actor.plane !== 'matrix') {
|
|
@@ -176,7 +195,9 @@ export class EraseMarkCommand extends Command {
|
|
|
176
195
|
const pool = Math.max(1, actor.logic + (computer > 0 ? computer : -1)
|
|
177
196
|
+ penalty + actor.matrixActionPenalty + actor.woundModifier - actor.sustainingPenalty);
|
|
178
197
|
const limit = actor.matrixAttribute('attack');
|
|
179
|
-
|
|
198
|
+
// THE PLACER ROLLS, not the icon the key is written on (p.239, see
|
|
199
|
+
// IPlacer). `chosen` is the icon whose keys this action is scrubbing.
|
|
200
|
+
const defence = chosen.defence;
|
|
180
201
|
const attempt = rollPool(pool, limit, { gremlins: actor.deckGremlins });
|
|
181
202
|
const resist = rollPool(defence.pool);
|
|
182
203
|
if (showsMechanics(this.scene, actor)) {
|
|
@@ -212,18 +233,6 @@ export class EraseMarkCommand extends Command {
|
|
|
212
233
|
...(god.length > 0 ? [`\n${god.join('\n')}`] : []),
|
|
213
234
|
].join('');
|
|
214
235
|
}
|
|
215
|
-
/** p.239: Willpower + Firewall. A host has no Willpower; its rating stands in. */
|
|
216
|
-
defenceFor(target) {
|
|
217
|
-
if (target.kind === 'host') {
|
|
218
|
-
const d = hostDefensePool(target.room.host);
|
|
219
|
-
return { label: `${d.label}, rating standing in for Willpower`, pool: d.pool };
|
|
220
|
-
}
|
|
221
|
-
const who = target.player;
|
|
222
|
-
return {
|
|
223
|
-
label: 'Willpower + Firewall',
|
|
224
|
-
pool: Math.max(0, who.willpower + who.matrixAttribute('firewall')),
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
236
|
remainingOn(target, actor) {
|
|
228
237
|
if (target.kind !== 'self')
|
|
229
238
|
return 0;
|
|
@@ -4,10 +4,11 @@ import { rollPool, formatRoll } from '../utilities/dice.js';
|
|
|
4
4
|
import { ownedDrones } from '../utilities/owned-drones.js';
|
|
5
5
|
/**
|
|
6
6
|
* The free road to STUN recovery (stim patches are the fast, risky one --
|
|
7
|
-
* see use.ts): catch your breath somewhere safe. "Safe" means
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* see use.ts): catch your breath somewhere safe. "Safe" means your own
|
|
8
|
+
* hideout (a Residence-type room) or a room with no one else in it:
|
|
9
|
+
* nobody naps in front of an audience in the sprawl. Only the hideout
|
|
10
|
+
* counts as SLEEP -- the tier's rest dice, the sleep debt and the
|
|
11
|
+
* fatigue unlock are all a bed-that-is-yours thing.
|
|
11
12
|
*
|
|
12
13
|
* Each rest is SR5 p.207's Stun half: Body + Willpower, one box per hit.
|
|
13
14
|
* A night in a bed that's yours is also the day of NATURAL RECOVERY for
|
|
@@ -60,25 +61,27 @@ export class RestCommand extends Command {
|
|
|
60
61
|
.map(d => d.item)
|
|
61
62
|
.filter(i => i.droneDamage > 0);
|
|
62
63
|
const othersHere = this.scene.getActorsInRoom(room).filter(a => a !== actor);
|
|
63
|
-
//
|
|
64
|
-
// (YxFKFqmXMgAvxwpce). 'Residence' is the roomType of Game._homeRoom
|
|
65
|
-
// and of nothing else -- and _homeRoom is only ever added to the
|
|
66
|
-
// scene on a SOLO boot (game.ts: `if (!options.sharedRun && ...)`).
|
|
67
|
-
// headless.ts hard-codes sharedRun: true, so every browser session,
|
|
68
|
-
// hub included, ran with no residence room in the world at all:
|
|
69
|
-
// isResidence was permanently false, markSlept() could never fire,
|
|
70
|
-
// the sleep-locked fatigue floor in Player.healStun never lifted,
|
|
71
|
-
// and rest printed a roll and "0 stun recovered" with nothing to
|
|
72
|
-
// explain it.
|
|
64
|
+
// A BED IS A BED, AND A STREET IS NOT ONE.
|
|
73
65
|
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
66
|
+
// 'Residence' is the roomType of Game._homeRoom and of nothing else,
|
|
67
|
+
// which is exactly right: going home to sleep IS the mechanic.
|
|
68
|
+
//
|
|
69
|
+
// This briefly also accepted any room whose seed listed your
|
|
70
|
+
// lifestyle tier in supportedLifestyles.residenceTypes, on the
|
|
71
|
+
// belief that a hosted session had no hideout to go to. That belief
|
|
72
|
+
// was wrong -- see the note on IMainGameConfig.sharedRun -- and the
|
|
73
|
+
// cost was real: the hub's own Neon Strip carries ["Street",
|
|
74
|
+
// "Squatter"], so a runner on either tier could bed down IN THE
|
|
75
|
+
// STREET and collect a night's sleep, the fatigue unlock and the
|
|
76
|
+
// lifestyle rest dice for it. scenes/scene1.json:119 carries the
|
|
77
|
+
// same field, so it landed there too.
|
|
78
|
+
//
|
|
79
|
+
// supportedLifestyles means something else in any case: its reader
|
|
80
|
+
// is Scene.validateLifestyleSupport, which checks an actor's
|
|
81
|
+
// lifestyleChoices residence against the rooms the seed provides.
|
|
82
|
+
// It answers "may someone of this tier live in this scene", never
|
|
83
|
+
// "is this particular room your bedroom".
|
|
84
|
+
const isResidence = room.roomType?.toLowerCase() === 'residence';
|
|
82
85
|
if (actor.stunTaken === 0 && actor.damageTaken === 0 && damagedDecks.length === 0 && damagedDrones.length === 0) {
|
|
83
86
|
// Nothing to heal -- but sleeping is its own point now (needs
|
|
84
87
|
// system: edge refills only fed-and-slept, see Game.
|
|
@@ -797,14 +797,14 @@
|
|
|
797
797
|
// - REST WAS SHORT AN ATTRIBUTE AND LONG A FREE BOX
|
|
798
798
|
// (YxFKFqmXMgAvxwpce). p.207's Stun half is Body + Willpower, one
|
|
799
799
|
// box per hit; this rolled Body alone and paid hits + 1. The free
|
|
800
|
-
// box is why nobody measured the missing attribute.
|
|
801
|
-
//
|
|
802
|
-
//
|
|
803
|
-
//
|
|
804
|
-
//
|
|
805
|
-
//
|
|
806
|
-
//
|
|
807
|
-
//
|
|
800
|
+
// box is why nobody measured the missing attribute. Blocked
|
|
801
|
+
// recovery also names what is blocking it now: fatigue boxes are
|
|
802
|
+
// locked to their need (p.172), so a runner whose stun IS hunger or
|
|
803
|
+
// exhaustion watched a roll happen and the track not budge, with no
|
|
804
|
+
// line connecting the two.
|
|
805
|
+
// WHAT THIS NOTE CLAIMED AND 1.68.0 TAKES BACK: that no hosted
|
|
806
|
+
// session had a bed. See the 1.68.0 entry -- it was read off one of
|
|
807
|
+
// two headless entry points and was wrong.
|
|
808
808
|
// - BREACH COULD NOT REACH A DOOR (Gfx5RFFpHJdKB87Bh, "I cant Breach
|
|
809
809
|
// East"). BypassCommand lists room.openableDevices(), which knows
|
|
810
810
|
// only Device objects, so a plain keyed Door matched nothing and
|
|
@@ -927,5 +927,62 @@
|
|
|
927
927
|
// ONE list (utilities/nameables.ts), which is what that module always
|
|
928
928
|
// claimed. The refusal listed hosts only, under a sentence promising
|
|
929
929
|
// it had looked for a PAN and a camera too.
|
|
930
|
-
|
|
930
|
+
// 1.70.0 (2026-09-16): A STREET IS NOT A BEDROOM -- taking back half of
|
|
931
|
+
// what 1.67.0 did to rest, because the premise under it was wrong.
|
|
932
|
+
// 1.67.0 widened rest's "am I somewhere I can sleep" test to accept any
|
|
933
|
+
// room whose seed listed the runner's lifestyle tier in
|
|
934
|
+
// supportedLifestyles.residenceTypes. The belief was that a hosted
|
|
935
|
+
// session had no hideout to go home to. It does.
|
|
936
|
+
// - HOW THE MISTAKE WAS MADE, because that is the reusable part: there
|
|
937
|
+
// are TWO headless entry points and only one was read.
|
|
938
|
+
// createHeadlessSession (a shared JOB) sets sharedRun: true
|
|
939
|
+
// unconditionally; createHeadlessHub (the browser's hub, 1.51.0)
|
|
940
|
+
// never sets it, so a hosted hub takes the ordinary branch, gets its
|
|
941
|
+
// hideout and its door, and the runner wakes on their own cot.
|
|
942
|
+
// Generalising the first to "every hosted session" produced the
|
|
943
|
+
// claim. What settled it: a live web hub renders `@D` beside the
|
|
944
|
+
// player, and the reporter's own CLI log walks `go down` into a
|
|
945
|
+
// High-tier hideout with a working Footlocker.
|
|
946
|
+
// - WHAT IT COST. The hub's Neon Strip carries ["Street","Squatter"],
|
|
947
|
+
// so a runner on either tier could bed down IN THE STREET and take a
|
|
948
|
+
// night's sleep, the fatigue unlock and the lifestyle rest dice for
|
|
949
|
+
// it; scene1.json's start room carries the same field. Going home to
|
|
950
|
+
// sleep IS the mechanic, and this quietly removed the going-home.
|
|
951
|
+
// supportedLifestyles was never a bedroom flag: its reader is
|
|
952
|
+
// Scene.validateLifestyleSupport, which asks whether someone of a
|
|
953
|
+
// tier may LIVE IN THIS SCENE, not which room is your cot.
|
|
954
|
+
// - WHAT STAYS, because it was right on its own terms: Body +
|
|
955
|
+
// Willpower and one box per hit (p.207), the line that names the
|
|
956
|
+
// fatigue floor when recovery is refused, and the isDown() narrowing.
|
|
957
|
+
// A shared JOB still has no hideout -- deliberate and unchanged: the
|
|
958
|
+
// crew rode to the jobsite, and nobody's cot is stapled to it.
|
|
959
|
+
// 1.71.0 (2026-09-16): WHAT f79LLqTerep4nKWcA'S OWN BENCH PRINTED. The
|
|
960
|
+
// repro bench for this item ran clean and then said three things a
|
|
961
|
+
// reviewer would have caught, which is what a bench is for.
|
|
962
|
+
// - ERASE MARK WAS DEFENDED BY THE WRONG ICON (p.239, RAG-checked).
|
|
963
|
+
// The book's worked example is explicit: "You roll your Computer +
|
|
964
|
+
// Logic ... opposed by THE IC'S RATING (standing in for Willpower) +
|
|
965
|
+
// Firewall to erase its mark on your icon." 1.61.0's defenceFor()
|
|
966
|
+
// switched on the TARGET instead, so "unmark me" rolled the
|
|
967
|
+
// DECKER'S OWN Willpower + Firewall while the line said the host was
|
|
968
|
+
// resisting -- 8 dice where a rating-8 host owes 18, and a label
|
|
969
|
+
// naming an icon that was not in the test. The defence now rides
|
|
970
|
+
// each placer (IPlacer), because the placer is who the book has
|
|
971
|
+
// rolling; the marked icon only ever mattered for the three-mark
|
|
972
|
+
// gate. A placer who has left the scene rolls nothing, which is the
|
|
973
|
+
// honest reading of scrubbing a key nobody is behind any more.
|
|
974
|
+
// - "SEALED" SURVIVED IN TWO MORE RENDERERS, gridSculpt and the host
|
|
975
|
+
// icon, and printed two lines above a row rewritten in 1.68.0 to
|
|
976
|
+
// stop saying it. It read as English there rather than as the
|
|
977
|
+
// jargon token hostLine used -- but the reporter named the word, and
|
|
978
|
+
// a reviewer meeting it twice cannot be expected to know one was
|
|
979
|
+
// innocent. Both say what they mean now.
|
|
980
|
+
// - THE DIVIDER DIVIDED NOTHING. gridIcons splices a "-- and NEAR it
|
|
981
|
+
// ... --" heading after the host's own icon; gridIconBlock then
|
|
982
|
+
// pulls the host line out into its own section, leaving the heading
|
|
983
|
+
// at the top of ICONS IN REACH with nothing above it -- a bullet
|
|
984
|
+
// that reads like an icon you could act on. It is dropped when it
|
|
985
|
+
// lands first. (1.69.0 fixed the same heading leaking into the
|
|
986
|
+
// distance bands; this is the other half.)
|
|
987
|
+
export const ENGINE_VERSION = '1.71.0';
|
|
931
988
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -119,7 +119,7 @@ export function gridIcons(scene, actor, room) {
|
|
|
119
119
|
room.host?.spotted.has(actor.name) ? 'and it can see you'
|
|
120
120
|
: room.hostAlert ? 'and it knows someone is on the grid' : undefined,
|
|
121
121
|
].filter(Boolean).join(', ');
|
|
122
|
-
icons.push(`● ${hostLabel(room, { capital: true })}${hr}${purpose} -- sculpted ice geometry,
|
|
122
|
+
icons.push(`● ${hostLabel(room, { capital: true })}${hr}${purpose} -- sculpted ice geometry, closed all the way round. Whatever it holds is inside it.${standing.length > 0 ? ` ${standing.charAt(0).toUpperCase()}${standing.slice(1)}.` : ''} (${ways.join('; ')})`);
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
// WHERE THE HOST'S OWN ICON ENDS AND THE STREET BEGINS.
|
|
@@ -521,7 +521,13 @@ export function gridSculpt(actor, room) {
|
|
|
521
521
|
const flat = `{light-blue-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)' : ''}.` : ''}{/light-blue-fg}`;
|
|
522
522
|
if (!room.hasNode)
|
|
523
523
|
return `${header}\n${flat}`;
|
|
524
|
-
|
|
524
|
+
// "SEALED" IS A WORD THE REPORTER NAMED (f79LLqTerep4nKWcA). It read
|
|
525
|
+
// as English here rather than as the jargon token hostLine used it
|
|
526
|
+
// as -- but it survived into this item's own repro bench two lines
|
|
527
|
+
// above a row that had just been rewritten to stop saying it, and a
|
|
528
|
+
// reviewer meeting the same word twice cannot be expected to know one
|
|
529
|
+
// of them was innocent. Says what it means instead.
|
|
530
|
+
return `${header}\n${flat}\n{light-blue-fg}${hostLabel(room, { capital: true })} hangs directly overhead -- a closed shape. Everything it holds is inside it, and you get in by putting a mark on it.{/light-blue-fg}`;
|
|
525
531
|
}
|
|
526
532
|
/**
|
|
527
533
|
* The icons rendered as the standard cyan block, or the thin-grid line.
|
|
@@ -559,7 +565,18 @@ export function gridIconBlock(scene, actor, room, rooms) {
|
|
|
559
565
|
out.push(hosts.length > 0
|
|
560
566
|
? `{light-blue-fg}HOSTS OVERHEAD:{/light-blue-fg}\n${hosts.map(h => ` {light-blue-fg}${hostLine(h, actor)}{/light-blue-fg}`).join('\n')}`
|
|
561
567
|
: `{light-blue-fg}Nothing overhead -- no host stands over this district.{/light-blue-fg}`);
|
|
562
|
-
|
|
568
|
+
// THE DIVIDER DIVIDES NOTHING HERE. gridIcons puts the host's own icon
|
|
569
|
+
// first and splices a "-- and NEAR it ... --" heading after it; this
|
|
570
|
+
// block then pulls the host line OUT (it has its own HOSTS OVERHEAD
|
|
571
|
+
// section above), which left the heading standing at the top of the
|
|
572
|
+
// list with nothing above it to be divided from -- a bullet reading
|
|
573
|
+
// "-- and NEAR it, out on the open grid (not inside the host): --"
|
|
574
|
+
// as if it were an icon you could act on. Seen in this item's own
|
|
575
|
+
// repro bench (f79LLqTerep4nKWcA). It earns its place only when
|
|
576
|
+
// something survives above it.
|
|
577
|
+
const near = gridIcons(scene, actor, gridVicinity(actor))
|
|
578
|
+
.filter(i => !isHostLine(i))
|
|
579
|
+
.filter((i, idx) => !(idx === 0 && isNearDivider(i)));
|
|
563
580
|
out.push(near.length > 0
|
|
564
581
|
? `{light-blue-fg}ICONS IN REACH:{/light-blue-fg}\n${near.map(i => ` {light-blue-fg}${i}{/light-blue-fg}`).join('\n')}`
|
|
565
582
|
: `{light-blue-fg}Nothing near -- background noise around your signal.{/light-blue-fg}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.211.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.",
|