@maka/maka-cli 5.383.0 → 5.385.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.
Files changed (28) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +89 -3
  3. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +276 -43
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.js +15 -7
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +24 -9
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +10 -2
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +8 -3
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +7 -6
  9. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +31 -1
  10. package/bundle/typescript/src/commands/game/sideQuest/game.js +7 -3
  11. package/bundle/typescript/src/commands/game/sideQuest/headless.js +3 -1
  12. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +8 -6
  13. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +4 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +87 -18
  15. package/bundle/typescript/src/commands/game/sideQuest/utilities/astral-move.js +4 -1
  16. package/bundle/typescript/src/commands/game/sideQuest/utilities/barrier-combat.js +102 -0
  17. package/bundle/typescript/src/commands/game/sideQuest/utilities/barrier-geometry.js +277 -0
  18. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +32 -1
  19. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-turn.js +25 -0
  20. package/bundle/typescript/src/commands/game/sideQuest/utilities/condition-report.js +1 -1
  21. package/bundle/typescript/src/commands/game/sideQuest/utilities/map-glyphs.js +15 -0
  22. package/bundle/typescript/src/commands/game/sideQuest/utilities/map-rulers.js +58 -0
  23. package/bundle/typescript/src/commands/game/sideQuest/utilities/map-view.js +30 -4
  24. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js +22 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js +51 -11
  26. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +24 -2
  27. package/bundle/typescript/src/commands/game/sideQuest/utilities/ways-out.js +34 -2
  28. package/package.json +1 -1
@@ -4,7 +4,9 @@ import { Direction } from '../types/shared/direction-enum.js';
4
4
  import { OPEN_FLOOR, spotOf, visibleActorsIn, exitSpot, seatingIn, theSpot, isPerchSpot } from './spots.js';
5
5
  import { placeName } from './log-style.js';
6
6
  import { DRONE_COLOUR } from './drone-prose.js';
7
- import { PLAYER_GLYPH, OTHER_PLAYER_GLYPH, OTHER_PLAYER_COLOR, NEUTRAL_GLYPH, HOSTILE_GLYPH, CREW_GLYPH, DEAD_GLYPH, DOWNED_GLYPH, SPOT_GLYPH, RIGGED_GLYPH, UP_GLYPH, CAMERA_GLYPH, CAMERA_COLOR, CAMERA_DEAD_COLOR, exitGlyph, GLYPH_RANK, } from './map-glyphs.js';
7
+ import { PLAYER_GLYPH, OTHER_PLAYER_GLYPH, OTHER_PLAYER_COLOR, NEUTRAL_GLYPH, HOSTILE_GLYPH, CREW_GLYPH, DEAD_GLYPH, DOWNED_GLYPH, SPOT_GLYPH, RIGGED_GLYPH, UP_GLYPH, CAMERA_GLYPH, CAMERA_COLOR, CAMERA_DEAD_COLOR, BARRIER_GLYPH, BARRIER_COLOR, MANA_BARRIER_COLOR, exitGlyph, GLYPH_RANK, } from './map-glyphs.js';
8
+ import { barriersWire } from './ways-out.js';
9
+ import { xRulerLine, yRulerLabel, yGutterWidth } from './map-rulers.js';
8
10
  import { key, levelFootprint, dimsForSize, roomRasterRows, METERS_PER_LEVEL, METERS_PER_CELL, } from './room-grid.js';
9
11
  /**
10
12
  * WHO THIS PANEL DRAWS: everyone you can see, plus every ARO your AR is
@@ -276,7 +278,16 @@ function buildRenderMaps(grid, room, viewer) {
276
278
  if (c)
277
279
  cameraAt.set(`${c.x},${c.y}`, { cam, z: c.z });
278
280
  }
279
- return { cellToSpot, cellToExit, verticalAt, cellTerrain, occupants, corpses, slumpedBody, cameraAt };
281
+ const barrierAt = new Map();
282
+ for (const b of barriersWire(room, viewer)) {
283
+ for (const f of b.faces) {
284
+ if (f.breached)
285
+ continue;
286
+ for (const c of f.cells)
287
+ barrierAt.set(key(c), b.kind);
288
+ }
289
+ }
290
+ return { cellToSpot, cellToExit, verticalAt, cellTerrain, occupants, corpses, slumpedBody, cameraAt, barrierAt };
280
291
  }
281
292
  function paint(g) {
282
293
  const body = g.inverse ? `{inverse}${g.ch}{/inverse}` : g.ch;
@@ -484,6 +495,9 @@ function glyphAt(c, maps, viewer, isOtherPlayer, isTableAlly, isHostile) {
484
495
  const lens = maps.cameraAt.get(`${c.x},${c.y}`);
485
496
  if (lens && lens.z >= c.z)
486
497
  return cameraGlyph(lens.cam);
498
+ const wall = maps.barrierAt.get(k);
499
+ if (wall)
500
+ return { ch: BARRIER_GLYPH, color: wall === 'mana' ? MANA_BARRIER_COLOR : BARRIER_COLOR, rank: GLYPH_RANK.barrier };
487
501
  const terrain = maps.cellTerrain.get(k);
488
502
  if (terrain)
489
503
  return terrain.blocking ? { ch: '▓', color: 'red', rank: GLYPH_RANK.terrainBlocking } : { ch: '▒', color: 'yellow', rank: GLYPH_RANK.terrainCover };
@@ -552,7 +566,9 @@ function levelPlansFor(room) {
552
566
  * glyph used. Returns [] for a spotless room (no grid ever synthesizes
553
567
  * -- see Room.ensureGrid).
554
568
  */
555
- export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly, isHostile) {
569
+ export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly, isHostile,
570
+ /** The room's PLANE origin: rulers in plane coordinates (map-rulers.ts). */
571
+ origin) {
556
572
  const grid = room.ensureGrid();
557
573
  if (!grid)
558
574
  return [];
@@ -569,8 +585,11 @@ export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly, isHos
569
585
  const exists = new Set(footprint.map(key));
570
586
  if (levelPlans.length > 1)
571
587
  rows.push(`{bold}${lvl.label}{/bold}`);
588
+ const gutter = origin ? yGutterWidth(origin.y + y0, y1 - y0 + 1) + 1 : 0;
589
+ if (origin)
590
+ rows.push(`${' '.repeat(gutter)}${xRulerLine(origin.x + x0, x1 - x0 + 1)}`);
572
591
  for (let y = y0; y <= y1; y++) {
573
- let row = '';
592
+ let row = origin ? `${yRulerLabel(origin.y + y, gutter - 1)} ` : '';
574
593
  for (let x = x0; x <= x1; x++) {
575
594
  const c = { x, y, z: lvl.z };
576
595
  row += exists.has(key(c)) ? cell(glyphAt(c, maps, viewer, isOtherPlayer, isTableAlly, isHostile)) : ' ';
@@ -589,6 +608,11 @@ export function renderRoomLayout(room, viewer, isOtherPlayer, isTableAlly, isHos
589
608
  // that reads as a keyword in the description must read as one here.
590
609
  legend.push(`{yellow-fg}${SPOT_GLYPH}{/yellow-fg} places: ${places.map(placeName).join(', ')}`);
591
610
  }
611
+ const kinds = new Set(maps.barrierAt.values());
612
+ if (kinds.has('physical'))
613
+ legend.push(`{${BARRIER_COLOR}-fg}${BARRIER_GLYPH}{/${BARRIER_COLOR}-fg} force barrier -- nothing physical passes; "attack barrier" to break a face`);
614
+ if (kinds.has('mana'))
615
+ legend.push(`{${MANA_BARRIER_COLOR}-fg}${BARRIER_GLYPH}{/${MANA_BARRIER_COLOR}-fg} mana barrier -- stops the astral and spirits; the living walk through`);
592
616
  for (const dir of grid.exitCells.keys()) {
593
617
  // Name the spot you actually stand in to leave this way -- read
594
618
  // from the same resolver movement uses, not from cell coincidence:
@@ -716,10 +740,13 @@ export function rightAlign(text, width) {
716
740
  const pad = Math.max(0, width - visibleWidth(text));
717
741
  return `${' '.repeat(pad)}${text}`;
718
742
  }
719
- export function roomMetresPerDot(room, availW, viewH) {
743
+ export function roomMetresPerDot(room, availW, viewH, origin) {
720
744
  const grid = room.ensureGrid();
721
745
  const dims = grid ? { x: grid.dims.x, y: grid.dims.y } : dimsForSize(room.size);
722
- return frameScale(dims.x, dims.y, availW, viewH) * METERS_PER_CELL;
746
+ // The rulers come out of the same raster (frameCells), so the scale
747
+ // read-out has to subtract them too or it would claim a finer dot.
748
+ const gutter = origin ? yGutterWidth(origin.y, dims.y) + 1 : 0;
749
+ return frameScale(dims.x, dims.y, availW - gutter, origin ? viewH - 1 : viewH) * METERS_PER_CELL;
723
750
  }
724
751
  export function frameScale(width, height, availW, viewH) {
725
752
  // One column a dot -- see cell(). The /2 this used to carry was the
@@ -758,15 +785,22 @@ export function frameScale(width, height, availW, viewH) {
758
785
  * The block is centred inside a fixed `viewH` rows so the box height
759
786
  * never jumps between rooms either.
760
787
  */
761
- function frameCells(cells, availW, viewH) {
788
+ function frameCells(cells, availW, viewH,
789
+ /** PLANE coordinates of cells[0][0]: draw the x ruler above and the y
790
+ * gutter beside (map-rulers.ts). Both come out of the same raster, so
791
+ * the box never grows -- the room scales a little harder instead. */
792
+ axes) {
762
793
  const height = cells.length;
763
794
  const width = Math.max(...cells.map(r => r.length), 0);
764
- const scale = frameScale(width, height, availW, viewH);
795
+ const gutter = axes ? yGutterWidth(axes.y, height) + 1 : 0;
796
+ const cols = availW - gutter;
797
+ const rows = axes ? viewH - 1 : viewH;
798
+ const scale = frameScale(width, height, cols, rows);
765
799
  const outW = Math.ceil(width / scale);
766
800
  const outH = Math.ceil(height / scale);
767
801
  const body = [];
768
802
  for (let ry = 0; ry < outH; ry++) {
769
- let line = '';
803
+ let line = axes ? `${yRulerLabel(axes.y + ry * scale, gutter - 1)} ` : '';
770
804
  for (let rx = 0; rx < outW; rx++) {
771
805
  // The highest-ranked thing in the block wins the dot -- see
772
806
  // IGlyph.rank. At scale 1 this is exactly the old behaviour,
@@ -783,6 +817,8 @@ function frameCells(cells, availW, viewH) {
783
817
  }
784
818
  body.push(line.trimEnd());
785
819
  }
820
+ if (axes)
821
+ body.unshift(`${' '.repeat(gutter)}${xRulerLine(axes.x, outW, scale)}`);
786
822
  // Vertically centre into a fixed height: a short room shouldn't sit
787
823
  // jammed against the top while a tall one fills the box.
788
824
  const pad = Math.max(0, viewH - body.length);
@@ -968,7 +1004,7 @@ export function renderRoomLayoutCompact(room, viewer, opts = {}) {
968
1004
  // room is SCALED onto it (frameCells), so this bounds the picture
969
1005
  // rather than the room: an outdoor room takes its extent from the
970
1006
  // district plane and can be far deeper than any size class.
971
- const { viewH = roomRasterRows(), boxWidth, isOtherPlayer, isTableAlly, isHostile } = opts;
1007
+ const { viewH = roomRasterRows(), boxWidth, isOtherPlayer, isTableAlly, isHostile, origin } = opts;
972
1008
  // Width left for the grid once the right-justified elevation bar and
973
1009
  // its gap are reserved.
974
1010
  const availW = Math.max(6, (boxWidth ?? 27) - ELEVATION_WIDTH - 1);
@@ -1023,7 +1059,7 @@ export function renderRoomLayoutCompact(room, viewer, opts = {}) {
1023
1059
  const lenses = new Map();
1024
1060
  for (const { cam, z: lz } of maps.cameraAt.values())
1025
1061
  lenses.set(lz, [...(lenses.get(lz) ?? []), cam]);
1026
- return withElevation(frameCells(cells, availW, viewH),
1062
+ return withElevation(frameCells(cells, availW, viewH, origin ? { x: origin.x + x0, y: origin.y + y0 } : undefined),
1027
1063
  // Circulating has no level to claim, same as it has no cell.
1028
1064
  elevationColumn(grid, viewerCell ? z : undefined, viewH, partialMeters, lenses), boxWidth);
1029
1065
  }
@@ -1099,6 +1135,10 @@ export function roomCaption(room, viewer) {
1099
1135
  const base = spot ? `at ${spot}` : '(moving through)';
1100
1136
  const { above, below } = actorsOnOtherLevels(room, viewer);
1101
1137
  const notes = [];
1138
+ // WHERE, IN THE RULERS' NUMBERS -- what "cast barrier x,y" is aimed with.
1139
+ const here = room.scene?.worldCellOf?.(viewer);
1140
+ if (here)
1141
+ notes.push(`${here.x},${here.y},${here.z}`);
1102
1142
  if (above > 0)
1103
1143
  notes.push(`+${above} above`);
1104
1144
  if (below > 0)
@@ -1,6 +1,6 @@
1
1
  import { droneImmobile } from './drone-prose.js';
2
2
  import { Direction } from '../types/shared/direction-enum.js';
3
- import { isOpenGround, isVoid, roomAtCell, streetDoors } from './district-plane.js';
3
+ import { isOpenGround, isVoid, roomAtCell, streetDoors, worldCell } from './district-plane.js';
4
4
  import { isOutdoorsPlace } from './outdoors.js';
5
5
  import { wayOut } from './ways-out.js';
6
6
  import { significantWords, wordsAreCloseEnough } from './fuzzy-match.js';
@@ -889,11 +889,24 @@ export function stepFrom(room, mover, direction, count) {
889
889
  const openOutside = openGroundBeyond(room);
890
890
  const furniture = furnitureCells(room);
891
891
  const bodies = standingOccupants(room, mover);
892
+ // A BARRIER SPELL is its own word (barrier-geometry.ts): it is drawn,
893
+ // it shimmers, and "the way is blocked" would hide whose working it is.
894
+ // Asked on the plane, so it stops a step off an outdoor room's edge too.
895
+ const scenePlane = room.scene?.spellBarriers?.('physical').length ? room.scene.ensurePlane() : undefined;
896
+ const barrierHere = (local) => {
897
+ if (!scenePlane || !room.scene)
898
+ return undefined;
899
+ const world = worldCell(scenePlane, room.name, local);
900
+ return world ? room.scene.barrierAt(world, 'physical')?.barrier.caster : undefined;
901
+ };
892
902
  let cur = start;
893
903
  let taken = 0;
894
904
  for (let i = 0; i < count; i++) {
895
905
  const next = { x: cur.x + step[0], y: cur.y + step[1], z: cur.z };
896
906
  const k = key(next);
907
+ const walledBy = barrierHere(next);
908
+ if (walledBy !== undefined)
909
+ return { cell: cur, taken, blockedBy: 'barrier', blocker: walledBy };
897
910
  if (!standable.has(k)) {
898
911
  const inBounds = next.x >= 0 && next.y >= 0 && next.x < grid.dims.x && next.y < grid.dims.y;
899
912
  // Off the edge of an OUTDOOR room is just more street, unless the
@@ -2526,7 +2539,10 @@ export function describeSpotRoster(room, viewer) {
2526
2539
  * district, and the room it happens to be over is a label the caller
2527
2540
  * re-derives.
2528
2541
  */
2529
- export function astralStepFrom(plane, from, direction, count, roomAt) {
2542
+ export function astralStepFrom(plane, from, direction, count, roomAt,
2543
+ /** Whose MANA BARRIER stands on a cell, if any (Scene.barrierAt --
2544
+ * the creator's own weave already excluded, p.315). */
2545
+ manaBarrierAt) {
2530
2546
  /**
2531
2547
  * UP AND DOWN ARE DIRECTIONS OUT HERE (p.313-314: an astral form
2532
2548
  * "can move in any direction" and is "not limited by gravity").
@@ -2564,6 +2580,12 @@ export function astralStepFrom(plane, from, direction, count, roomAt) {
2564
2580
  if (here !== there) {
2565
2581
  return { cell: cur, taken, blockedBy: 'ward', ward: (there ?? here)?.name };
2566
2582
  }
2583
+ // A MANA BARRIER SPELL: "solid, hazily opaque walls that stop astral
2584
+ // movement" (p.315-316). `go` always refused one; a `move` drift
2585
+ // used to float straight through it, because this walk never asked.
2586
+ const weaver = manaBarrierAt?.(next);
2587
+ if (weaver !== undefined)
2588
+ return { cell: cur, taken, blockedBy: 'barrier', ward: weaver };
2567
2589
  cur = next;
2568
2590
  taken++;
2569
2591
  }
@@ -71,7 +71,7 @@ export function wayOut(room, direction, from) {
71
71
  * (map-view.ts serializePlane); here a street keeps only what the plane
72
72
  * could not place.
73
73
  */
74
- export function roomGridWire(room) {
74
+ export function roomGridWire(room, viewer) {
75
75
  const grid = room.ensureGrid();
76
76
  if (!grid)
77
77
  return undefined;
@@ -83,9 +83,41 @@ export function roomGridWire(room) {
83
83
  damage: cam.damage, structure: cam.structure, destroyed: cam.destroyed,
84
84
  }] : [];
85
85
  });
86
- const wire = cameras.length > 0 ? { ...base, cameras } : base;
86
+ const withCams = cameras.length > 0 ? { ...base, cameras } : base;
87
+ const barriers = barriersWire(room, viewer);
88
+ const wire = barriers.length > 0 ? { ...withCams, barriers } : withCams;
87
89
  if (!isOutdoorsPlace(room))
88
90
  return wire;
89
91
  return { ...wire, doors: wire.doors.filter(d => wayOut(room, d.dir)?.kind === 'grid') };
90
92
  }
93
+ /**
94
+ * The barrier spells a viewer can see around this room, in its local
95
+ * cells: every standing Physical Barrier with a cell within one of the
96
+ * footprint (its wall ring included), and a Mana Barrier only for its
97
+ * weaver or an eye on the astral (p.294: "invisible to the physical
98
+ * plane"). Breached faces ride along marked, so a client can draw the
99
+ * gap rather than lose the face.
100
+ */
101
+ export function barriersWire(room, viewer) {
102
+ const scene = room.scene;
103
+ const all = scene?.spellBarriers?.() ?? [];
104
+ if (!scene || all.length === 0)
105
+ return [];
106
+ const frame = scene.ensurePlane().placements.get(roomKey(room.name));
107
+ if (!frame)
108
+ return [];
109
+ const seesAstral = !!viewer && (viewer.plane === 'astral' || viewer.astralPerceiving);
110
+ const near = (c) => c.x >= frame.origin.x - 1 && c.x <= frame.origin.x + frame.dims.x
111
+ && c.y >= frame.origin.y - 1 && c.y <= frame.origin.y + frame.dims.y;
112
+ return all
113
+ .filter(b => b.kind === 'physical' || seesAstral || b.caster === viewer?.name)
114
+ .filter(b => b.faces.some(f => f.cells.some(near)))
115
+ .map(b => ({
116
+ kind: b.kind, caster: b.caster, shape: b.shape, force: b.force, structure: b.structure,
117
+ faces: b.faces.map(f => ({
118
+ id: f.id, damage: f.damage, breached: f.breached,
119
+ cells: f.cells.map(c => ({ x: c.x - frame.origin.x, y: c.y - frame.origin.y, z: c.z - frame.origin.z })),
120
+ })),
121
+ }));
122
+ }
91
123
  //# sourceMappingURL=ways-out.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.383.0",
3
+ "version": "5.385.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.",