@maka/maka-cli 5.139.0 → 5.141.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/env/prompt-for-env.js +3 -8
- package/bundle/typescript/src/commands/env/set.sub.cmd.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +5 -1
- package/bundle/typescript/src/commands/game/sideQuest/game.js +4 -1
- package/bundle/typescript/src/commands/game/sideQuest/headless.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-turn.js +15 -2
- package/bundle/typescript/src/commands/game/sideQuest/utilities/hostile-contact.js +6 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js +38 -12
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.141.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.",
|
|
@@ -4,15 +4,10 @@ import { Log } from '../../tools/log/log.class.js';
|
|
|
4
4
|
* Prompts for which environment to target when --env wasn't given.
|
|
5
5
|
*/
|
|
6
6
|
export async function promptForEnv(cfg, opts = {}) {
|
|
7
|
-
const { message = 'Which environment?'
|
|
8
|
-
|
|
9
|
-
if (excludeLocal) {
|
|
10
|
-
configs = configs.filter(config => config !== 'local');
|
|
11
|
-
}
|
|
7
|
+
const { message = 'Which environment?' } = opts;
|
|
8
|
+
const configs = cfg.getAppProjectConfigs();
|
|
12
9
|
if (configs.length === 0) {
|
|
13
|
-
Log.error(
|
|
14
|
-
? 'No configuration other than local found. Consider running "maka g:config production" first.'
|
|
15
|
-
: 'No configurations found. Consider running "maka g:config local" first.');
|
|
10
|
+
Log.error('No configurations found. Consider running "maka g:config local" first.');
|
|
16
11
|
process.exit(1);
|
|
17
12
|
}
|
|
18
13
|
if (configs.length === 1) {
|
|
@@ -34,7 +34,7 @@ Command.create({
|
|
|
34
34
|
]
|
|
35
35
|
}, async function (args, opts) {
|
|
36
36
|
try {
|
|
37
|
-
const env = opts.env ? this.cfg.getEnvFromShorthand(opts.env) : await promptForEnv(this.cfg
|
|
37
|
+
const env = opts.env ? this.cfg.getEnvFromShorthand(opts.env) : await promptForEnv(this.cfg);
|
|
38
38
|
if (!this.cfg.checkConfigExists(env)) {
|
|
39
39
|
Log.error(`No configuration: ${env}, consider running "maka g:config ${env}"`);
|
|
40
40
|
process.exit(1);
|
|
@@ -388,5 +388,9 @@
|
|
|
388
388
|
// tells a human holding the Action Phase that the table is waiting; the
|
|
389
389
|
// third nudge on one phase ends it for them (forfeit, as "end turn" with
|
|
390
390
|
// nothing spent). Off the phase it is a private tap on the shoulder.
|
|
391
|
-
|
|
391
|
+
// 1.44.0 (2026-09-07): WHO STARTED IT. The fight's first line names the
|
|
392
|
+
// aggressor and their target; the world event carries it; "initiative"
|
|
393
|
+
// marks them. Hostiles are red on the room map by stance or by live
|
|
394
|
+
// enmity, not by the retired exchange flag.
|
|
395
|
+
export const ENGINE_VERSION = '1.44.0';
|
|
392
396
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -35,7 +35,7 @@ import { hostileContactBeat } from './utilities/hostile-contact.js';
|
|
|
35
35
|
import { requestAlarmBeat, newAlarmBeatLatch, ALARM_STIMULUS } from './utilities/alarmed-staff.js';
|
|
36
36
|
import { renderMapViewport, mapCaption } from './utilities/map-view.js';
|
|
37
37
|
import { tallestRoomRows } from './utilities/room-grid.js';
|
|
38
|
-
import { renderRoomLayoutCompact, crewOfTable, roomCaption } from './utilities/room-view.js';
|
|
38
|
+
import { hostileByStance, enemyInFight, renderRoomLayoutCompact, crewOfTable, roomCaption } from './utilities/room-view.js';
|
|
39
39
|
import { getPref } from './utilities/prefs.js';
|
|
40
40
|
import { rollPool, formatRoll } from './utilities/dice.js';
|
|
41
41
|
import { Direction } from './types/shared/direction-enum.js';
|
|
@@ -5623,6 +5623,9 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
5623
5623
|
isTableAlly: crewOfTable(this.scene.getPlayers()),
|
|
5624
5624
|
boxWidth,
|
|
5625
5625
|
isOtherPlayer: a => this.scene.isHumanControlled(a),
|
|
5626
|
+
// Red is the stance flag OR whoever the Combat Turn says is
|
|
5627
|
+
// fighting you (room-view.ts hostileByStance).
|
|
5628
|
+
isHostile: a => hostileByStance(a) || enemyInFight(this.scene, this.player, a),
|
|
5626
5629
|
});
|
|
5627
5630
|
if (roomLines.length > 0) {
|
|
5628
5631
|
roomLines.push(caption(roomCaption(here, this.player), boxWidth));
|
|
@@ -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 { renderMapViewport, mapCaption } from './utilities/map-view.js';
|
|
20
|
-
import { renderRoomLayoutCompact, crewOfTable, roomCaption } from './utilities/room-view.js';
|
|
20
|
+
import { hostileByStance, enemyInFight, renderRoomLayoutCompact, crewOfTable, roomCaption } from './utilities/room-view.js';
|
|
21
21
|
import { serializeRoomGrid } from './utilities/room-grid.js';
|
|
22
22
|
import { entrySpotFor, spotOf } from './utilities/spots.js';
|
|
23
23
|
import { equipIntoEmptySlots } from './archetypes.js';
|
|
@@ -319,6 +319,7 @@ export async function createHeadlessSession(opts) {
|
|
|
319
319
|
try {
|
|
320
320
|
const roomLines = renderRoomLayoutCompact(here, p, {
|
|
321
321
|
isOtherPlayer: a => game.scene.isHumanControlled(a),
|
|
322
|
+
isHostile: a => hostileByStance(a) || enemyInFight(game.scene, p, a),
|
|
322
323
|
// The leader's crew are the TABLE's crew -- without this
|
|
323
324
|
// every non-leader saw them as neutral bystanders.
|
|
324
325
|
isTableAlly: crewOfTable(game.scene.getPlayers()),
|
|
@@ -84,6 +84,10 @@ export class CombatEncounter {
|
|
|
84
84
|
/** Whose Action Phase is live, and their budget. */
|
|
85
85
|
phaseActor;
|
|
86
86
|
budget;
|
|
87
|
+
/** Who opened the fight, and on whom (playtest 2026-09-07: "nothing
|
|
88
|
+
* tells me who the aggressor is"). The tracker marks them. */
|
|
89
|
+
openedBy;
|
|
90
|
+
openedOn;
|
|
87
91
|
pairs = new Map();
|
|
88
92
|
surrenderSeenAt = new Map();
|
|
89
93
|
running = false;
|
|
@@ -106,13 +110,21 @@ export class CombatEncounter {
|
|
|
106
110
|
async start(members, opts) {
|
|
107
111
|
this.turn = 1;
|
|
108
112
|
this.pass = 1;
|
|
113
|
+
this.openedBy = opts.aggressor;
|
|
114
|
+
this.openedOn = opts.target;
|
|
109
115
|
for (const m of members)
|
|
110
116
|
this.addParticipant(m);
|
|
111
117
|
if (opts.target)
|
|
112
118
|
this.noteAttack(opts.aggressor, opts.target);
|
|
113
119
|
this.seedHostility();
|
|
114
120
|
const metaLines = ['Initiative (SR5 p.159):'];
|
|
115
|
-
|
|
121
|
+
// WHO STARTED IT is the first thing the room hears (playtest
|
|
122
|
+
// 2026-09-07): before the Surprise results and before the order.
|
|
123
|
+
const worldLines = [
|
|
124
|
+
opts.target
|
|
125
|
+
? `{red-fg}⚔ ${opts.aggressor.name} starts it -- opening on ${opts.target.name}.{/red-fg}`
|
|
126
|
+
: `{red-fg}⚔ ${opts.aggressor.name} starts it.{/red-fg}`,
|
|
127
|
+
];
|
|
116
128
|
// SURPRISE (p.192): the ambushed side rolls Reaction + Intuition (3).
|
|
117
129
|
// Failure: -10 Initiative and no defense against the surprisers
|
|
118
130
|
// until their next Action Phase. The aggressor never rolls -- an
|
|
@@ -141,7 +153,7 @@ export class CombatEncounter {
|
|
|
141
153
|
metaLines.push(` ${p.actor.name}: ${formatInitiative(p.roll)}${p.score !== p.roll.score ? ` -> ${p.score}` : ''}`);
|
|
142
154
|
}
|
|
143
155
|
worldLines.push(`Combat Turn ${this.turn} -- ${this.orderLine()}.`);
|
|
144
|
-
this.scene.addWorldEvent(`A fight broke out in ${this.room.name}: ${this.participants.map(p => p.actor.name).join(', ')}.`);
|
|
156
|
+
this.scene.addWorldEvent(`A fight broke out in ${this.room.name} -- ${opts.aggressor.name} opened${opts.target ? ` on ${opts.target.name}` : ''}. In it: ${this.participants.map(p => p.actor.name).join(', ')}.`);
|
|
145
157
|
await this.announce(worldLines, metaLines);
|
|
146
158
|
await this.run();
|
|
147
159
|
}
|
|
@@ -624,6 +636,7 @@ export class CombatEncounter {
|
|
|
624
636
|
: p.actor.surrendered ? 'surrendered'
|
|
625
637
|
: p.acted ? 'acted' : p.delayed ? 'delaying' : p.score > 0 ? 'to act' : 'spent';
|
|
626
638
|
const marks = [
|
|
639
|
+
p.actor === this.openedBy ? 'started it' : '',
|
|
627
640
|
p.surprised ? 'surprised' : '',
|
|
628
641
|
p.fullDefenseTurn === this.turn ? 'full defense' : '',
|
|
629
642
|
p.actor.aimBonus > 0 ? `aiming +${p.actor.aimBonus}` : '',
|
|
@@ -98,7 +98,12 @@ async function open(scene, room, npc, player, why) {
|
|
|
98
98
|
: why === 'contact'
|
|
99
99
|
? `{yellow-fg}${npc.name} is done looking at you.{/yellow-fg}`
|
|
100
100
|
: `{yellow-fg}${npc.name} has waited long enough.{/yellow-fg}`;
|
|
101
|
+
// Logged to the room BEFORE the encounter opens, not returned after
|
|
102
|
+
// it: the fight announces itself ("⚔ X starts it -- opening on Y",
|
|
103
|
+
// Surprise, the order) while startEncounter runs, and a returned line
|
|
104
|
+
// would print under all of that, out of order.
|
|
105
|
+
Logger.getInstance().log(line, { room: room.name });
|
|
101
106
|
await scene.startEncounter(room, { aggressor: npc, target: player, ambush: false });
|
|
102
|
-
return [
|
|
107
|
+
return [];
|
|
103
108
|
}
|
|
104
109
|
//# sourceMappingURL=hostile-contact.js.map
|
|
@@ -276,7 +276,33 @@ export function crewOfTable(humans) {
|
|
|
276
276
|
return !!master && seated.has(master);
|
|
277
277
|
};
|
|
278
278
|
}
|
|
279
|
-
|
|
279
|
+
/**
|
|
280
|
+
* RED MEANS HOSTILE (playtest 2026-09-07: "the icons on the map should
|
|
281
|
+
* be red if they are hostile"). Until then the sword was keyed to
|
|
282
|
+
* `inExchange`, which only the old two-actor exchange ever set -- the
|
|
283
|
+
* Combat Turn (combat-turn.ts) never touches it, so a ganger standing
|
|
284
|
+
* over you drew as a white local. The stance flag (Player.hostile: set
|
|
285
|
+
* only by a seed or a scene that means it, cleared when talked round)
|
|
286
|
+
* is the structural answer that needs no Scene; the exchange case stays
|
|
287
|
+
* for the astral/Matrix fights still on that engine. A caller with a
|
|
288
|
+
* Scene adds live enmity through `isHostile` (game.ts, headless.ts).
|
|
289
|
+
*/
|
|
290
|
+
export function hostileByStance(a) {
|
|
291
|
+
if (a.surrendered)
|
|
292
|
+
return false;
|
|
293
|
+
const npc = a;
|
|
294
|
+
return (npc.hostile === true && !npc.allyOf) || a.inExchange;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Live enmity, for a caller that holds the Scene: `a` is in a running
|
|
298
|
+
* Combat Turn the viewer is also in, on the other side. Typed
|
|
299
|
+
* structurally so this file stays free of the Scene import.
|
|
300
|
+
*/
|
|
301
|
+
export function enemyInFight(scene, viewer, a) {
|
|
302
|
+
const enc = scene.encounterFor?.(a);
|
|
303
|
+
return !!enc && enc.has(viewer) && enc.enemiesOf(viewer).includes(a);
|
|
304
|
+
}
|
|
305
|
+
function actorGlyph(a, viewer, isOtherPlayer, isTableAlly, isHostile) {
|
|
280
306
|
// Still an actor, so still alive -- the dead are Room.bodies, drawn
|
|
281
307
|
// separately as skulls. This is someone face-down and revivable.
|
|
282
308
|
if (a.isIncapacitated())
|
|
@@ -313,7 +339,7 @@ function actorGlyph(a, viewer, isOtherPlayer, isTableAlly) {
|
|
|
313
339
|
if (isTableAlly?.(a) ?? a.allyOf === viewer.name) {
|
|
314
340
|
return { ch: CREW_GLYPH, color: 'green' };
|
|
315
341
|
}
|
|
316
|
-
if (a
|
|
342
|
+
if (isHostile?.(a) ?? hostileByStance(a))
|
|
317
343
|
return { ch: HOSTILE_GLYPH, color: 'red' };
|
|
318
344
|
return { ch: NEUTRAL_GLYPH, color: 'white' };
|
|
319
345
|
}
|
|
@@ -321,18 +347,18 @@ function actorGlyph(a, viewer, isOtherPlayer, isTableAlly) {
|
|
|
321
347
|
* The glyph for one cell, in priority order: people, then terrain, then
|
|
322
348
|
* vertical links, then exits, then the spot itself.
|
|
323
349
|
*/
|
|
324
|
-
function glyphAt(c, maps, viewer, isOtherPlayer, isTableAlly) {
|
|
350
|
+
function glyphAt(c, maps, viewer, isOtherPlayer, isTableAlly, isHostile) {
|
|
325
351
|
const k = key(c);
|
|
326
352
|
const occ = maps.occupants.get(k);
|
|
327
353
|
if (occ && occ.length > 0) {
|
|
328
354
|
if (occ.includes(viewer))
|
|
329
|
-
return actorGlyph(viewer, viewer, isOtherPlayer, isTableAlly);
|
|
355
|
+
return actorGlyph(viewer, viewer, isOtherPlayer, isTableAlly, isHostile);
|
|
330
356
|
if (occ.length === 1)
|
|
331
|
-
return actorGlyph(occ[0], viewer, isOtherPlayer, isTableAlly);
|
|
357
|
+
return actorGlyph(occ[0], viewer, isOtherPlayer, isTableAlly, isHostile);
|
|
332
358
|
// A crowded cell shows the most urgent icon standing in it: a fight
|
|
333
359
|
// outranks a runner outranks a bystander, and a body never hides
|
|
334
360
|
// someone still upright.
|
|
335
|
-
const glyphs = occ.map(a => actorGlyph(a, viewer, isOtherPlayer, isTableAlly));
|
|
361
|
+
const glyphs = occ.map(a => actorGlyph(a, viewer, isOtherPlayer, isTableAlly, isHostile));
|
|
336
362
|
return glyphs.find(g => g.ch === HOSTILE_GLYPH)
|
|
337
363
|
?? glyphs.find(g => g.ch === PLAYER_GLYPH)
|
|
338
364
|
// A fellow runner outranks a bystander for the same reason you
|
|
@@ -398,7 +424,7 @@ function levelPlansFor(room) {
|
|
|
398
424
|
* glyph used. Returns [] for a spotless room (no grid ever synthesizes
|
|
399
425
|
* -- see Room.ensureGrid).
|
|
400
426
|
*/
|
|
401
|
-
export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly) {
|
|
427
|
+
export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly, isHostile) {
|
|
402
428
|
const grid = room.ensureGrid();
|
|
403
429
|
if (!grid)
|
|
404
430
|
return [];
|
|
@@ -419,7 +445,7 @@ export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly) {
|
|
|
419
445
|
let row = '';
|
|
420
446
|
for (let x = x0; x <= x1; x++) {
|
|
421
447
|
const c = { x, y, z: lvl.z };
|
|
422
|
-
row += exists.has(key(c)) ? cell(glyphAt(c, maps, viewer, isOtherPlayer, isTableAlly)) : ' ';
|
|
448
|
+
row += exists.has(key(c)) ? cell(glyphAt(c, maps, viewer, isOtherPlayer, isTableAlly, isHostile)) : ' ';
|
|
423
449
|
}
|
|
424
450
|
rows.push(row.trimEnd());
|
|
425
451
|
}
|
|
@@ -483,7 +509,7 @@ export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly) {
|
|
|
483
509
|
}
|
|
484
510
|
// People, once, only for the icons actually standing in the room --
|
|
485
511
|
// a key to four kinds of person in an empty bar is just noise.
|
|
486
|
-
const present = new Set(visibleActorsIn(room, viewer).map(a => actorGlyph(a, viewer, isOtherPlayer, isTableAlly).ch));
|
|
512
|
+
const present = new Set(visibleActorsIn(room, viewer).map(a => actorGlyph(a, viewer, isOtherPlayer, isTableAlly, isHostile).ch));
|
|
487
513
|
const who = [
|
|
488
514
|
// "you" leads the key: on a party job it is the answer to the
|
|
489
515
|
// question that sent the player to the legend in the first place.
|
|
@@ -508,7 +534,7 @@ export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly) {
|
|
|
508
534
|
present.has(OTHER_PLAYER_GLYPH) ? `{${OTHER_PLAYER_COLOR}-fg}${OTHER_PLAYER_GLYPH}{/${OTHER_PLAYER_COLOR}-fg} runner` : '',
|
|
509
535
|
present.has(CREW_GLYPH) ? `{green-fg}${CREW_GLYPH}{/green-fg} yours` : '',
|
|
510
536
|
present.has(NEUTRAL_GLYPH) ? `{white-fg}${NEUTRAL_GLYPH}{/white-fg} local` : '',
|
|
511
|
-
present.has(HOSTILE_GLYPH) ? `{red-fg}${HOSTILE_GLYPH}{/red-fg}
|
|
537
|
+
present.has(HOSTILE_GLYPH) ? `{red-fg}${HOSTILE_GLYPH}{/red-fg} hostile` : '',
|
|
512
538
|
// The knocked-out mark was missing from this key entirely -- the one
|
|
513
539
|
// glyph on the map a player is most likely to need explained.
|
|
514
540
|
present.has(DOWNED_GLYPH) ? `{yellow-fg}${DOWNED_GLYPH}{/yellow-fg} out cold` : '',
|
|
@@ -712,7 +738,7 @@ function renderShellCompact(room, viewer, availW, viewH, boxWidth) {
|
|
|
712
738
|
export function renderRoomLayoutCompact(room, viewer, opts = {}) {
|
|
713
739
|
// Default to the TALLEST room class, not a fixed 9 -- see
|
|
714
740
|
// room-grid.ts tallestRoomRows for why that number is derived.
|
|
715
|
-
const { viewH = tallestRoomRows(), boxWidth, isOtherPlayer, isTableAlly } = opts;
|
|
741
|
+
const { viewH = tallestRoomRows(), boxWidth, isOtherPlayer, isTableAlly, isHostile } = opts;
|
|
716
742
|
// Width left for the grid once the right-justified elevation bar and
|
|
717
743
|
// its gap are reserved.
|
|
718
744
|
const availW = Math.max(6, (boxWidth ?? 27) - ELEVATION_WIDTH - 1);
|
|
@@ -735,7 +761,7 @@ export function renderRoomLayoutCompact(room, viewer, opts = {}) {
|
|
|
735
761
|
const row = [];
|
|
736
762
|
for (let x = x0; x <= x1; x++) {
|
|
737
763
|
const c = { x, y, z };
|
|
738
|
-
row.push(exists.has(key(c)) ? glyphAt(c, maps, viewer, isOtherPlayer, isTableAlly) : undefined);
|
|
764
|
+
row.push(exists.has(key(c)) ? glyphAt(c, maps, viewer, isOtherPlayer, isTableAlly, isHostile) : undefined);
|
|
739
765
|
}
|
|
740
766
|
cells.push(row);
|
|
741
767
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.141.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.",
|