@maka/maka-cli 5.161.0 → 5.162.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/engine-version.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/game.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/headless.js +13 -2
- package/bundle/typescript/src/commands/game/sideQuest/utilities/condition-report.js +60 -8
- package/bundle/typescript/src/commands/game/sideQuest/utilities/map-view.js +33 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js +10 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.162.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.",
|
|
@@ -392,5 +392,9 @@
|
|
|
392
392
|
// aggressor and their target; the world event carries it; "initiative"
|
|
393
393
|
// marks them. Hostiles are red on the room map by stance or by live
|
|
394
394
|
// enmity, not by the retired exchange flag.
|
|
395
|
-
|
|
395
|
+
// 1.45.0 (2026-09-11): THE MAPS AS DATA. The condition beat carries the
|
|
396
|
+
// room's live grid, every body's engine-resolved square, and the room
|
|
397
|
+
// panel's own hostile test; the map event carries the fog-applied
|
|
398
|
+
// district layout (data.district). Envelope and beat shape both grew.
|
|
399
|
+
export const ENGINE_VERSION = '1.45.0';
|
|
396
400
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -2401,7 +2401,11 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
2401
2401
|
if (!this._conditionSink && process.env.MAKA_NO_DDP === '1')
|
|
2402
2402
|
return;
|
|
2403
2403
|
void import('./utilities/condition-report.js')
|
|
2404
|
-
.then(m => m.reportConditionSoon(this.player, this._conditionSink
|
|
2404
|
+
.then(m => m.reportConditionSoon(this.player, this._conditionSink, {
|
|
2405
|
+
// The room panel's own red (updateExits below): stance OR live
|
|
2406
|
+
// enmity in the viewer's encounter. Same predicate, same map.
|
|
2407
|
+
isHostile: a => hostileByStance(a) || enemyInFight(this.scene, this.player, a),
|
|
2408
|
+
}))
|
|
2405
2409
|
.catch(() => undefined);
|
|
2406
2410
|
};
|
|
2407
2411
|
// A ROOM CHANGE IS A SAVE BEAT (player request 2026-09-02): where you
|
|
@@ -17,7 +17,7 @@ import { Logger } from './utilities/logger.js';
|
|
|
17
17
|
import { currentSession, runInSession } from './utilities/session-context.js';
|
|
18
18
|
import { restorePlayer, serializePlayer } from './utilities/persistence.js';
|
|
19
19
|
import { runWithAuthToken } from './utilities/cloud-saves.js';
|
|
20
|
-
import { renderMapViewport, mapCaption } from './utilities/map-view.js';
|
|
20
|
+
import { renderMapViewport, mapCaption, serializeDistrictMap } from './utilities/map-view.js';
|
|
21
21
|
import { hostileByStance, enemyInFight, renderRoomLayoutCompact, crewOfTable, roomCaption } from './utilities/room-view.js';
|
|
22
22
|
import { serializeRoomGrid } from './utilities/room-grid.js';
|
|
23
23
|
import { entrySpotFor, spotOf } from './utilities/spots.js';
|
|
@@ -297,7 +297,18 @@ export async function createHeadlessSession(opts) {
|
|
|
297
297
|
const mapLines = renderMapViewport(rooms, here, sealing, eyes);
|
|
298
298
|
if (mapLines.length > 0)
|
|
299
299
|
mapLines.push(mapCaption(here.name));
|
|
300
|
-
|
|
300
|
+
// STRUCTURED DISTRICT MAP, ADDITIVE (2026-09-11): the same layout
|
|
301
|
+
// as data, on this same event, so the browser client draws it in
|
|
302
|
+
// its own style instead of reparsing the text (the `data.grid`
|
|
303
|
+
// pattern below). A decoration must never kill the push.
|
|
304
|
+
let district;
|
|
305
|
+
try {
|
|
306
|
+
district = serializeDistrictMap(rooms, here, sealing, eyes);
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
logger.write(`District map serialize failed for ${p.name} in ${here.name}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
|
|
310
|
+
}
|
|
311
|
+
logger.system(mapLines.join('\n'), { actor: p.name }, 'map', district ? { district } : undefined);
|
|
301
312
|
// THE ROOM BOX'S TWIN (playtest 2026-08-27, reported twice:
|
|
302
313
|
// "the room minimap isn't updating in the shared run, I only
|
|
303
314
|
// see @open floor"). Game.updateExits early-returns on
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Logger } from './logger.js';
|
|
2
|
-
import { visibleActorsIn } from './spots.js';
|
|
2
|
+
import { visibleActorsIn, seatingIn } from './spots.js';
|
|
3
3
|
import { hostLabel } from './grid-names.js';
|
|
4
|
+
import { serializeRoomGrid } from './room-grid.js';
|
|
5
|
+
import { hostileByStance } from './room-view.js';
|
|
4
6
|
/**
|
|
5
7
|
* THE LIVE CONDITION EMITTER -- the CLI half of the realtime sheet lane
|
|
6
8
|
* (contract agreed with maka-cli.com, 2026-09-01). On every condition
|
|
@@ -71,13 +73,56 @@ export function setMarksPlacedResolver(fn) {
|
|
|
71
73
|
export function setActorRoleResolver(fn) {
|
|
72
74
|
actorRole = fn;
|
|
73
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* THE ROOM'S GEOMETRY, ON THE BEAT (2026-09-11, 8bZXzNXLgKwE5wxgh: "no
|
|
78
|
+
* icons being drawn on the character sheet in the active run"). The web
|
|
79
|
+
* sheet rebuilt its room from the SAVE's hub seed, and a generated run
|
|
80
|
+
* room is not in it -- so in a run the sheet had no furniture, no doors,
|
|
81
|
+
* and no cell to seat a spot-sitting NPC on. The beat already fires on
|
|
82
|
+
* every position change and is the one channel both the sheet and the
|
|
83
|
+
* play page read, so the live grid rides it: the SAME wire shape the
|
|
84
|
+
* room event carries for the browser client (headless.ts pushMap ->
|
|
85
|
+
* serializeRoomGrid). Physical planes only -- jacked in, the meat map is
|
|
86
|
+
* a lie the sheet already refuses to draw. A decoration never kills the
|
|
87
|
+
* beat: any throw here is an absent field, not a lost report.
|
|
88
|
+
*/
|
|
89
|
+
function roomGridFor(player) {
|
|
90
|
+
const room = player.currentLocation;
|
|
91
|
+
if (!room || (player.plane !== 'meat' && player.plane !== 'drone'))
|
|
92
|
+
return undefined;
|
|
93
|
+
try {
|
|
94
|
+
const grid = room.ensureGrid();
|
|
95
|
+
return grid ? serializeRoomGrid(grid, room.name) : undefined;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
74
101
|
export function buildConditionReport(player, deps) {
|
|
75
|
-
const { saveSlug, arSightUp } = deps;
|
|
102
|
+
const { saveSlug, arSightUp, isHostile } = deps;
|
|
76
103
|
// BRICKED INCLUDED (web report 2026-09-02: "my deck got bricked, but its
|
|
77
104
|
// status still says 4/12"): a forced dump clears activeDeck and
|
|
78
105
|
// getCyberdeck() hides a bricked deck, so the beat read nothing and the
|
|
79
106
|
// sheet froze on the last number. Status reads getAnyCyberdeck.
|
|
80
107
|
const deck = player.activeDeck ?? player.getAnyCyberdeck();
|
|
108
|
+
// WHERE EVERYONE ACTUALLY STANDS (2026-09-11, user: "movement isn't
|
|
109
|
+
// snappy" on the web). The beat sent `atCell` alone, and `atCell` is
|
|
110
|
+
// the EXCEPTION -- set by a tactical step, cleared by "move to <spot>"
|
|
111
|
+
// and by most arrivals -- so after a plain move the beat carried no
|
|
112
|
+
// cell, the server kept the last one it had, and the dot on the web
|
|
113
|
+
// sat still at a square the runner had left. seatingIn is the engine's
|
|
114
|
+
// one answer to "which square" (the room panel, cover and ranges all
|
|
115
|
+
// read it), so the web now gets the same square the CLI draws. Read at
|
|
116
|
+
// fire time like everything else here; a throw means "no seat", never
|
|
117
|
+
// a lost beat.
|
|
118
|
+
const room = player.currentLocation;
|
|
119
|
+
let seats = new Map();
|
|
120
|
+
if (room && (player.plane === 'meat' || player.plane === 'drone')) {
|
|
121
|
+
try {
|
|
122
|
+
seats = seatingIn(room);
|
|
123
|
+
}
|
|
124
|
+
catch { /* keep the empty map */ }
|
|
125
|
+
}
|
|
81
126
|
return {
|
|
82
127
|
slug: saveSlug(player.name),
|
|
83
128
|
damageTaken: player.damageTaken,
|
|
@@ -85,7 +130,8 @@ export function buildConditionReport(player, deps) {
|
|
|
85
130
|
overflow: 0,
|
|
86
131
|
room: player.currentLocation?.name,
|
|
87
132
|
spot: player.atSpot,
|
|
88
|
-
cell: player.atCell,
|
|
133
|
+
cell: player.atCell ?? seats.get(player.name),
|
|
134
|
+
grid: roomGridFor(player),
|
|
89
135
|
plane: player.plane,
|
|
90
136
|
// Where the meat sits while the persona rides the grid (web accepts
|
|
91
137
|
// since 2026-09-02): "jacked in from Your Hideout". Absent in the meat.
|
|
@@ -196,11 +242,14 @@ export function buildConditionReport(player, deps) {
|
|
|
196
242
|
.slice(0, 24)
|
|
197
243
|
.map(a => ({
|
|
198
244
|
name: a.name,
|
|
245
|
+
// THE SAME RED THE ROOM PANEL USES (see IConditionReportDeps
|
|
246
|
+
// .isHostile): stance flag OR live enmity, and never a
|
|
247
|
+
// surrendered one -- hostileByStance owns that clause.
|
|
199
248
|
kind: a.allyOf ? 'ally'
|
|
200
|
-
: a
|
|
249
|
+
: (isHostile?.(a) ?? hostileByStance(a)) ? 'hostile' : 'neutral',
|
|
201
250
|
role: actorRole(a),
|
|
202
251
|
spot: a.atSpot,
|
|
203
|
-
cell: a.atCell,
|
|
252
|
+
cell: a.atCell ?? seats.get(a.name),
|
|
204
253
|
})),
|
|
205
254
|
};
|
|
206
255
|
}
|
|
@@ -227,7 +276,7 @@ function noteRoster(report) {
|
|
|
227
276
|
const ADDITIVE_FIELDS = [
|
|
228
277
|
'ar', 'actors', 'deckLine', 'deckDamage', 'astralPerceiving', 'bodyRoom', 'sustainingPenalty',
|
|
229
278
|
'matrix', 'panMarks', 'hostMarksOnYou', 'marksPlaced', 'marksOnYou', 'deckStored', 'droneDamage', 'companions', 'pools', 'initiative',
|
|
230
|
-
'karma', 'nuyen', 'edge',
|
|
279
|
+
'karma', 'nuyen', 'edge', 'grid',
|
|
231
280
|
];
|
|
232
281
|
/** Refusal reasons already written to the session log -- one line each. */
|
|
233
282
|
const announcedRefusals = new Map();
|
|
@@ -242,7 +291,10 @@ function noteRefusal(why) {
|
|
|
242
291
|
catch { /* no session */ }
|
|
243
292
|
}
|
|
244
293
|
}
|
|
245
|
-
export function reportConditionSoon(player, sink
|
|
294
|
+
export function reportConditionSoon(player, sink,
|
|
295
|
+
/** The Scene-side half of the beat (IConditionReportDeps.isHostile);
|
|
296
|
+
* the caller that owns a Scene supplies it, nobody else needs to. */
|
|
297
|
+
extra) {
|
|
246
298
|
// Before the TIMER, not just before the call: a scheduled beat firing
|
|
247
299
|
// after a Jest file finished lazy-imported into a torn-down
|
|
248
300
|
// environment and failed CI with every test green (2026-09-01). Under
|
|
@@ -269,7 +321,7 @@ export function reportConditionSoon(player, sink) {
|
|
|
269
321
|
// stance and brandish flow through their accessors' own reports;
|
|
270
322
|
// sustained and deck programs are composed here and ride whatever
|
|
271
323
|
// beat fires next. All read at FIRE time, like position.
|
|
272
|
-
const report = buildConditionReport(player, { saveSlug, arSightUp });
|
|
324
|
+
const report = buildConditionReport(player, { saveSlug, arSightUp, isHostile: extra?.isHostile });
|
|
273
325
|
noteRoster(report);
|
|
274
326
|
if (sink) {
|
|
275
327
|
// IN-PROCESS: no HubLink, no MAKA_NO_DDP gate, no version-skew
|
|
@@ -220,6 +220,39 @@ export function renderMapViewport(rooms, here, sealing, eyes, viewW = 21, viewH
|
|
|
220
220
|
out[halfH] = out[halfH].slice(0, halfW) + `{inverse}${out[halfH][halfW] === ' ' ? '▣' : out[halfH][halfW]}{/inverse}` + out[halfH].slice(halfW + 1);
|
|
221
221
|
return out;
|
|
222
222
|
}
|
|
223
|
+
export function serializeDistrictMap(rooms, here, sealing, eyes) {
|
|
224
|
+
const floors = layoutFloors(rooms, here, sealing);
|
|
225
|
+
if (floors.length === 0)
|
|
226
|
+
return undefined;
|
|
227
|
+
const eye = fogEye(rooms, eyes?.visited);
|
|
228
|
+
const ar = eyes?.arEyes !== false;
|
|
229
|
+
const out = floors.map(floor => {
|
|
230
|
+
const linkSeen = (k) => {
|
|
231
|
+
const a = floor.roomAt.get(`${k.ax},${k.ay}`);
|
|
232
|
+
const b = floor.roomAt.get(`${k.bx},${k.by}`);
|
|
233
|
+
return !!a && !!b && eye.sees(a) && eye.sees(b) && (eye.knows(a) || eye.knows(b));
|
|
234
|
+
};
|
|
235
|
+
const seen = [...floor.posByRoom.entries()].filter(([r]) => eye.sees(r));
|
|
236
|
+
return {
|
|
237
|
+
rooms: seen.map(([r, p]) => {
|
|
238
|
+
const known = eye.knows(r);
|
|
239
|
+
return {
|
|
240
|
+
x: p.x, y: p.y,
|
|
241
|
+
...(known ? { name: r.name } : {}),
|
|
242
|
+
known,
|
|
243
|
+
here: r === here,
|
|
244
|
+
node: known && ar && !!r.hasNode,
|
|
245
|
+
up: known && hasWay(r, 'up'),
|
|
246
|
+
down: known && hasWay(r, 'down'),
|
|
247
|
+
};
|
|
248
|
+
}),
|
|
249
|
+
links: floor.links.filter(linkSeen).map(k => ({ ...k })),
|
|
250
|
+
};
|
|
251
|
+
});
|
|
252
|
+
const current = Math.max(0, floors.findIndex(f => f.posByRoom.has(here)));
|
|
253
|
+
const notes = fogNotes(floors[current]?.notes ?? [], rooms, eye);
|
|
254
|
+
return { current, floors: out, notes };
|
|
255
|
+
}
|
|
223
256
|
/**
|
|
224
257
|
* The DETAILED view (the "map" text command): named cells, box-drawing
|
|
225
258
|
* connections, and annotations -- the renderer shipped in 5.48.0, now
|
|
@@ -822,6 +822,14 @@ export function serializeRoomGrid(grid, roomName) {
|
|
|
822
822
|
const [x, y, z] = cellKey.split(',').map(Number);
|
|
823
823
|
return { kind: link.kind, x, y, z };
|
|
824
824
|
});
|
|
825
|
+
const terrain = [...grid.terrain.values()].map(r => ({
|
|
826
|
+
id: r.id, label: r.label, cover: r.cover, blocking: r.blocking, cleared: r.cleared,
|
|
827
|
+
cells: r.cells.map(c => ({ x: c.x, y: c.y, z: c.z })),
|
|
828
|
+
}));
|
|
829
|
+
const levels = [];
|
|
830
|
+
for (let z = 1; z < grid.dims.z; z++) {
|
|
831
|
+
levels.push({ z, cells: levelFootprint(grid, z).map(c => ({ x: c.x, y: c.y })) });
|
|
832
|
+
}
|
|
825
833
|
return {
|
|
826
834
|
name: roomName,
|
|
827
835
|
dims: { x: grid.dims.x, y: grid.dims.y, z: grid.dims.z },
|
|
@@ -829,6 +837,8 @@ export function serializeRoomGrid(grid, roomName) {
|
|
|
829
837
|
doors,
|
|
830
838
|
footprints,
|
|
831
839
|
vertical,
|
|
840
|
+
terrain,
|
|
841
|
+
levels,
|
|
832
842
|
};
|
|
833
843
|
}
|
|
834
844
|
function clamp(n, lo, hi) { return Math.max(lo, Math.min(hi, n)); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.162.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.",
|