@maka/maka-cli 5.132.0 → 5.134.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/jump.js +17 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +17 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +15 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +7 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/companion-heel.js +67 -9
- package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +39 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.134.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.",
|
|
@@ -38,10 +38,23 @@ export class JumpCommand extends Command {
|
|
|
38
38
|
// (utilities/owned-drones.ts): a frame parked at home jumps from
|
|
39
39
|
// home and stays parked while you ride it.
|
|
40
40
|
const owned = ownedDrones(actor, this.game?.homeRoom, { reach: 'here' });
|
|
41
|
-
|
|
41
|
+
// ...AND EVERY DEPLOYED FRAME, WHEREVER IT FLIES (transcript
|
|
42
|
+
// 2026-09-06: a Direktionssekretar deployed from the garage could
|
|
43
|
+
// not be jumped into once the rigger left the hideout -- "Nothing
|
|
44
|
+
// to jump into -- no drone in your pack" -- while this verb's own
|
|
45
|
+
// description promised seizure "wherever it flies"). Jumping in
|
|
46
|
+
// rides the link, not your hands: the hands rule above is for
|
|
47
|
+
// frames still stowed; a frame already out is in reach by definition.
|
|
48
|
+
const deployed = (this.game?.companions ?? [])
|
|
49
|
+
.filter(c => c.kind === 'drone' && c.boundDevice && !owned.some(d => d.item === c.boundDevice));
|
|
50
|
+
const drones = [...owned.map(d => d.item), ...deployed.map(c => c.boundDevice)];
|
|
51
|
+
const listing = [
|
|
52
|
+
...droneListLines(owned),
|
|
53
|
+
...deployed.map(c => ` • ${c.boundDevice.name.padEnd(26)} deployed -- ${c.npc.currentLocation.name}${c.boundDevice.maxDroneBoxes > 0 ? ` hull ${c.boundDevice.droneSummary()}` : ''}`),
|
|
54
|
+
];
|
|
42
55
|
const atHome = this.game?.homeRoom !== undefined && actor.currentLocation === this.game.homeRoom;
|
|
43
56
|
if (drones.length === 0) {
|
|
44
|
-
return `Nothing to jump into -- no drone in your pack${atHome ? ' or your garage' : ''}.${hint(` (Ratchet's Circuit Bazaar stocks airframes.)`)}`;
|
|
57
|
+
return `Nothing to jump into -- no drone in your pack${atHome ? ' or your garage' : ''}, none deployed.${hint(` (Ratchet's Circuit Bazaar stocks airframes.)`)}`;
|
|
45
58
|
}
|
|
46
59
|
// "jump hot" / "jump <drone> hot": strip the mode word, pick the frame.
|
|
47
60
|
const words = args.map(w => w.toLowerCase());
|
|
@@ -52,11 +65,11 @@ export class JumpCommand extends Command {
|
|
|
52
65
|
const picked = fuzzyPickName(nameWords.join(' '), drones.map(d => d.name));
|
|
53
66
|
drone = picked ? drones.find(d => d.name === picked) : undefined;
|
|
54
67
|
if (!drone) {
|
|
55
|
-
return [`No drone by that name in reach. Yours:`, ...
|
|
68
|
+
return [`No drone by that name in reach. Yours:`, ...listing].join('\n');
|
|
56
69
|
}
|
|
57
70
|
}
|
|
58
71
|
if (!drone) {
|
|
59
|
-
return [`Which airframe? ("jump <name>", add "hot" for hot-sim)`, ...
|
|
72
|
+
return [`Which airframe? ("jump <name>", add "hot" for hot-sim)`, ...listing].join('\n');
|
|
60
73
|
}
|
|
61
74
|
if (drone.isWrecked) {
|
|
62
75
|
return `The ${drone.name} is a wreck -- ${droneWreckFragment(drone)} -- and a wreck jumps nowhere. It repairs while you rest somewhere safe.`;
|
|
@@ -7,13 +7,14 @@ import { Direction } from '../types/shared/direction-enum.js';
|
|
|
7
7
|
import { Door } from '../models/door.js';
|
|
8
8
|
import { fuzzyPickName } from '../utilities/fuzzy-match.js';
|
|
9
9
|
import { heldBy, holding, physicalLimit } from '../utilities/grapple.js';
|
|
10
|
-
import { spotsActive, resolveSpot, spotOf, describeSpotRoster, OPEN_FLOOR, pathBetweenSpots, spotDistanceMeters, spotClimbSteps, blockingRegion, terrainRefusal, reseatOpenFloor, routeForMove, stepFrom, spotForCell, actorCell, standingRoomNear, furnitureCells, approachCellFor, exitSpot, concealedFrom, revealActor, onTheFloor } from '../utilities/spots.js';
|
|
10
|
+
import { spotsActive, resolveSpot, spotOf, describeSpotRoster, OPEN_FLOOR, pathBetweenSpots, spotDistanceMeters, spotClimbSteps, blockingRegion, terrainRefusal, reseatOpenFloor, routeForMove, stepFrom, spotForCell, actorCell, standingRoomNear, yieldSquareToMaster, furnitureCells, approachCellFor, exitSpot, concealedFrom, revealActor, onTheFloor } from '../utilities/spots.js';
|
|
11
11
|
// fallDamageDVMeters, not fallDamageDV: a climb that fell short drops
|
|
12
12
|
// you from where you GOT to, which is a distance in metres, not a whole
|
|
13
13
|
// storey. The storey-based wrapper still exists for callers that really
|
|
14
14
|
// do fall a level.
|
|
15
15
|
import { fallDamageDVMeters, climbHitsFor, climbMetersFrom, climbSurfaceModifier, METERS_PER_CELL, METERS_PER_LEVEL, key, distanceMeters } from '../utilities/room-grid.js';
|
|
16
16
|
import { rollPool, formatRoll } from '../utilities/dice.js';
|
|
17
|
+
import { trailMaster } from '../utilities/companion-heel.js';
|
|
17
18
|
/**
|
|
18
19
|
* "walk to <spot>" / "run to <spot>" / "sprint to <spot>" -- crossing
|
|
19
20
|
* the CURRENT room to one of its "at" spots (see utilities/spots.ts;
|
|
@@ -101,6 +102,15 @@ export class MoveCommand extends Command {
|
|
|
101
102
|
}
|
|
102
103
|
return `Spots are a meat-world concern -- on the grid there is no distance at all, so "go <room>" puts your persona anywhere by name.`;
|
|
103
104
|
}
|
|
105
|
+
/** Your shells trail you across the room (companion-heel.ts
|
|
106
|
+
* trailMaster): after every in-room move they re-place themselves
|
|
107
|
+
* beside you. An NPC mover has no Game in its context and drags
|
|
108
|
+
* nobody. */
|
|
109
|
+
trailCompanions() {
|
|
110
|
+
if (!this.game)
|
|
111
|
+
return;
|
|
112
|
+
trailMaster(this.actor, this.game.companions);
|
|
113
|
+
}
|
|
104
114
|
async execute(args = []) {
|
|
105
115
|
const actor = this.actor;
|
|
106
116
|
const room = actor.currentLocation;
|
|
@@ -633,6 +643,7 @@ export class MoveCommand extends Command {
|
|
|
633
643
|
// an NPC's own spot is a social event for them (the bartender greets
|
|
634
644
|
// you when you reach the bar).
|
|
635
645
|
actor.performAction('moves to', `the ${spot.name}`);
|
|
646
|
+
this.trailCompanions();
|
|
636
647
|
this.logger.write(`MoveCommand: ${actor.name} ${this.mode()}s to ${spot.name} in ${room.name}.`);
|
|
637
648
|
// Companions drift along with their master (go.ts follow precedent).
|
|
638
649
|
if (actor === this.scene.getPlayer() && this.game) {
|
|
@@ -759,7 +770,10 @@ export class MoveCommand extends Command {
|
|
|
759
770
|
const wasAt = actor.atSpot;
|
|
760
771
|
actor.atCell = cell;
|
|
761
772
|
actor.atSpot = spotForCell(room, cell);
|
|
773
|
+
// A shell of yours on that square steps aside (spots.ts heelsTo).
|
|
774
|
+
yieldSquareToMaster(room, actor);
|
|
762
775
|
actor.performAction('steps', `${taken} ${taken === 1 ? 'pace' : 'paces'} ${direction}`);
|
|
776
|
+
this.trailCompanions();
|
|
763
777
|
this.scene.updateStatus();
|
|
764
778
|
this.scene.updateExits(room);
|
|
765
779
|
// QUIET STEPS (user, 2026-09-01: "You step 1 pace east" is spammy now
|
|
@@ -865,6 +879,7 @@ export class MoveCommand extends Command {
|
|
|
865
879
|
actor.atCell = cell;
|
|
866
880
|
actor.atSpot = spotForCell(room, cell);
|
|
867
881
|
actor.performAction('steps up beside', target.name);
|
|
882
|
+
this.trailCompanions();
|
|
868
883
|
this.scene.updateStatus();
|
|
869
884
|
this.scene.updateExits(room);
|
|
870
885
|
const pace = actor.isRunningThisTurn ? ` You're running now -- everything else this turn is at -2.` : '';
|
|
@@ -1014,6 +1029,7 @@ export class MoveCommand extends Command {
|
|
|
1014
1029
|
actor.atCell = cell;
|
|
1015
1030
|
actor.atSpot = spotForCell(room, cell);
|
|
1016
1031
|
actor.performAction('moves to', `the ${direction} doorway`);
|
|
1032
|
+
this.trailCompanions();
|
|
1017
1033
|
this.scene.updateStatus();
|
|
1018
1034
|
this.scene.updateExits(room);
|
|
1019
1035
|
return `You cross to the ${direction} way out.${hint(` ("go ${direction}" to step through.)`)}`;
|
|
@@ -345,5 +345,19 @@
|
|
|
345
345
|
// while "order <drone> fold/stow" packs. A frame you jump out of HOLDS
|
|
346
346
|
// its position (Game.companions.holding) instead of teleporting to your
|
|
347
347
|
// heel on your next step; recall clears the hold. HUD shows "holding at".
|
|
348
|
-
|
|
348
|
+
// 1.38.0 (2026-09-06): YOUR OWN SHELL IS NEVER IN YOUR WAY. COMMAND SEMANTICS
|
|
349
|
+
// in shared scenes: a companion shell (drone, spirit, sprite, agent) at its
|
|
350
|
+
// master's heel no longer blocks the master's "move <direction>" -- it
|
|
351
|
+
// yields the square (spots.ts heelsTo / yieldSquareToMaster); hired crew
|
|
352
|
+
// still hold theirs. "jump" reaches every DEPLOYED frame wherever it flies,
|
|
353
|
+
// garage-born or not (it used to answer "no drone in your pack" away from
|
|
354
|
+
// the hideout). An NPC with no dialogue and no AI no longer greets you
|
|
355
|
+
// with "<name>: undefined".
|
|
356
|
+
// 1.39.0 (2026-09-06): COMPANIONS TRAIL YOU ACROSS THE ROOM. COMMAND SEMANTICS
|
|
357
|
+
// in shared scenes: every in-room move ("move <direction>", "move to
|
|
358
|
+
// <spot>", the doorway) re-places your shells on the nearest free square
|
|
359
|
+
// beside you (companion-heel.ts trailMaster) -- they used to heel only on
|
|
360
|
+
// a room change and sat in the doorway while you paced. "recall" judges
|
|
361
|
+
// "already at your side" by cells, not spot names.
|
|
362
|
+
export const ENGINE_VERSION = '1.39.0';
|
|
349
363
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -1546,7 +1546,13 @@ ${worldEventsSummary}
|
|
|
1546
1546
|
this._triedThisStimulus = [];
|
|
1547
1547
|
if (!AI.isConfigured()) {
|
|
1548
1548
|
this.logger.write(`No AI available`);
|
|
1549
|
-
|
|
1549
|
+
// A shell with no dialogue -- every companion is built with
|
|
1550
|
+
// `dialog: []` -- has nothing to say, and used to say so as
|
|
1551
|
+
// "<name>: undefined" (transcript 2026-09-06: a drone waking as
|
|
1552
|
+
// its rigger walked in). Silence is the right line.
|
|
1553
|
+
const line = this.speak();
|
|
1554
|
+
if (line)
|
|
1555
|
+
await actor.hear(this, line);
|
|
1550
1556
|
return;
|
|
1551
1557
|
}
|
|
1552
1558
|
// ---------- helpers ----------
|
|
@@ -2679,6 +2679,10 @@ export class Player extends AbstractPlayer {
|
|
|
2679
2679
|
const overLink = opts?.viaLink || this.activeCallPartner === actor;
|
|
2680
2680
|
if (!overLink && !this.canPerceive(actor))
|
|
2681
2681
|
return;
|
|
2682
|
+
// Nothing said is nothing heard: a caller passing an empty line
|
|
2683
|
+
// (an NPC with no dialogue, AI off) must not print "<name>: undefined".
|
|
2684
|
+
if (!message)
|
|
2685
|
+
return;
|
|
2682
2686
|
this.logger.write(`${this.name} heard ${actor.name}`);
|
|
2683
2687
|
// A voice arriving over an active CALL from someone who isn't in the
|
|
2684
2688
|
// room wears the comm dressing (player note: Mr. Johnson -- who lives
|
|
@@ -1,22 +1,80 @@
|
|
|
1
|
+
import { actorCell, standingRoomNear, onTheFloor, heelsTo } from './spots.js';
|
|
2
|
+
/** Chebyshev distance in cells, or undefined when either has no cell. */
|
|
3
|
+
function cellGap(room, a, b) {
|
|
4
|
+
const ca = actorCell(room, a);
|
|
5
|
+
const cb = actorCell(room, b);
|
|
6
|
+
if (!ca || !cb)
|
|
7
|
+
return undefined;
|
|
8
|
+
return Math.max(Math.abs(ca.x - cb.x), Math.abs(ca.y - cb.y), Math.abs(ca.z - cb.z));
|
|
9
|
+
}
|
|
10
|
+
/** Is the shell beside its master -- same room, same spot, and at most
|
|
11
|
+
* one square away (or on a room with no grid to measure)? */
|
|
12
|
+
export function atMastersSide(entry, master) {
|
|
13
|
+
const room = master.currentLocation;
|
|
14
|
+
if (entry.npc.currentLocation !== room || entry.npc.atSpot !== master.atSpot)
|
|
15
|
+
return false;
|
|
16
|
+
const gap = cellGap(room, entry.npc, master);
|
|
17
|
+
return gap === undefined || gap <= 1;
|
|
18
|
+
}
|
|
19
|
+
/** Put the shell on the nearest free square beside its master, at the
|
|
20
|
+
* master's spot. */
|
|
21
|
+
export function placeBeside(entry, master) {
|
|
22
|
+
const room = master.currentLocation;
|
|
23
|
+
if (entry.npc.currentLocation !== room)
|
|
24
|
+
entry.npc.currentLocation = room;
|
|
25
|
+
entry.npc.atSpot = master.atSpot;
|
|
26
|
+
const here = actorCell(room, master);
|
|
27
|
+
entry.npc.atCell = here ? standingRoomNear(room, here, entry.npc) : undefined;
|
|
28
|
+
}
|
|
1
29
|
/**
|
|
2
30
|
* BRING A COMPANION TO HEEL (player ruling 2026-09-06: "recall" is
|
|
3
31
|
* "return to me" -- the frame comes to your side and follows again;
|
|
4
32
|
* putting it away is "stow"). The shell crosses to the master's room
|
|
5
|
-
* and spot,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
33
|
+
* and spot, takes the nearest free square beside them, and any HOLDING
|
|
34
|
+
* flag clears so the next move drags it along like any companion.
|
|
35
|
+
* Returns whether it had anywhere to come from -- judged by CELLS, not
|
|
36
|
+
* spot names: a frame eight squares off on the same open floor shares
|
|
37
|
+
* the spot name and is not "at your side" (transcript 2026-09-06).
|
|
8
38
|
*
|
|
9
39
|
* One function, used by Game.heelCompanion and by the tests that pin
|
|
10
40
|
* recall, so a mock Game and the real one cannot disagree about it.
|
|
11
41
|
*/
|
|
12
42
|
export function bringToHeel(entry, master) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
if (entry.npc.currentLocation !== here)
|
|
16
|
-
entry.npc.currentLocation = here;
|
|
17
|
-
entry.npc.atSpot = master.atSpot;
|
|
18
|
-
entry.npc.atCell = undefined;
|
|
43
|
+
const moved = !!entry.holding || !atMastersSide(entry, master);
|
|
44
|
+
placeBeside(entry, master);
|
|
19
45
|
entry.holding = false;
|
|
20
46
|
return { moved };
|
|
21
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* COMPANIONS TRAIL THEIR MASTER ACROSS THE ROOM (playtest 2026-09-06:
|
|
50
|
+
* "It will follow to another room, but then just sit at the door").
|
|
51
|
+
* go.ts heels every shell on a ROOM change; nothing did on an in-room
|
|
52
|
+
* move, so a rigger who paced eight squares across the Neon Strip left
|
|
53
|
+
* the frame parked in the doorway. Called after every successful
|
|
54
|
+
* in-room move (move.ts): each shell of the mover's that is on the
|
|
55
|
+
* floor here, not HOLDING and not hired crew, is re-placed beside them.
|
|
56
|
+
* The frame answers to the meat body only (go.ts HEELS_ON): a rigger
|
|
57
|
+
* flying one frame does not drag the others around.
|
|
58
|
+
*/
|
|
59
|
+
export function trailMaster(master, entries) {
|
|
60
|
+
if (master.plane !== 'meat')
|
|
61
|
+
return [];
|
|
62
|
+
const room = master.currentLocation;
|
|
63
|
+
const moved = [];
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
if (entry.holding || entry.kind === 'ally')
|
|
66
|
+
continue;
|
|
67
|
+
if (!heelsTo(entry.npc, master))
|
|
68
|
+
continue;
|
|
69
|
+
if (entry.npc.currentLocation !== room || !onTheFloor(entry.npc))
|
|
70
|
+
continue;
|
|
71
|
+
if (entry.npc.isIncapacitated() || entry.npc.inExchange)
|
|
72
|
+
continue;
|
|
73
|
+
if (atMastersSide(entry, master))
|
|
74
|
+
continue;
|
|
75
|
+
placeBeside(entry, master);
|
|
76
|
+
moved.push(entry.npc);
|
|
77
|
+
}
|
|
78
|
+
return moved;
|
|
79
|
+
}
|
|
22
80
|
//# sourceMappingURL=companion-heel.js.map
|
|
@@ -858,6 +858,15 @@ export function standingOccupants(room, mover) {
|
|
|
858
858
|
const offPlane = new Set(room.getActors()
|
|
859
859
|
.filter(a => !onTheFloor(a) && a.bodyRoom !== room)
|
|
860
860
|
.map(a => a.name));
|
|
861
|
+
// YOUR OWN SHELL IS NEVER IN YOUR WAY (transcript 2026-09-06: a drone
|
|
862
|
+
// at heel sat on its master's next square -- "You can't step east --
|
|
863
|
+
// S-K Direktionssekretar (autopilot)'s in the way", a dozen times --
|
|
864
|
+
// and read as the frame refusing to follow). A companion shell heels
|
|
865
|
+
// AT its master's spot by construction (go.ts), so of course it is
|
|
866
|
+
// standing where the master wants to go next; it yields instead
|
|
867
|
+
// (yieldSquareToMaster, called by the step). Hired crew are people
|
|
868
|
+
// (ruling 2026-08-25) and still hold their square.
|
|
869
|
+
const heels = new Set(mover ? room.getActors().filter(a => heelsTo(a, mover)).map(a => a.name) : []);
|
|
861
870
|
for (const [name, c] of seatingIn(room).entries()) {
|
|
862
871
|
if (mover && name === mover.name)
|
|
863
872
|
continue;
|
|
@@ -865,10 +874,40 @@ export function standingOccupants(room, mover) {
|
|
|
865
874
|
continue;
|
|
866
875
|
if (offPlane.has(name))
|
|
867
876
|
continue;
|
|
877
|
+
if (heels.has(name))
|
|
878
|
+
continue;
|
|
868
879
|
out.set(key(c), name);
|
|
869
880
|
}
|
|
870
881
|
return out;
|
|
871
882
|
}
|
|
883
|
+
/** Is `a` a companion SHELL of `master` -- a drone, spirit, sprite or
|
|
884
|
+
* agent riding their bond? Hired crew (kind 'ally') are people and
|
|
885
|
+
* answer no. */
|
|
886
|
+
export function heelsTo(a, master) {
|
|
887
|
+
const shell = a;
|
|
888
|
+
return shell.allyOf === master.name && shell.companionKind !== undefined && shell.companionKind !== 'ally';
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* After `master` steps onto a square one of their own shells was
|
|
892
|
+
* holding, the shell gives it up: its cell is released so seatingIn
|
|
893
|
+
* finds it the nearest free square at the master's spot. Returns the
|
|
894
|
+
* shells that moved. */
|
|
895
|
+
export function yieldSquareToMaster(room, master) {
|
|
896
|
+
const here = master.atCell ? key(master.atCell) : undefined;
|
|
897
|
+
if (!here)
|
|
898
|
+
return [];
|
|
899
|
+
const moved = [];
|
|
900
|
+
for (const a of room.getActors()) {
|
|
901
|
+
if (!heelsTo(a, master))
|
|
902
|
+
continue;
|
|
903
|
+
if (!a.atCell || key(a.atCell) !== here)
|
|
904
|
+
continue;
|
|
905
|
+
a.atCell = undefined;
|
|
906
|
+
a.atSpot = master.atSpot;
|
|
907
|
+
moved.push(a);
|
|
908
|
+
}
|
|
909
|
+
return moved;
|
|
910
|
+
}
|
|
872
911
|
/** The cell `mover` would end up in on arriving at `spotName` -- their
|
|
873
912
|
* seat around that fixture, with everyone else's seats already taken.
|
|
874
913
|
* This, not the fixture's anchor, is where movement actually paths to:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.134.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.",
|