@maka/maka-cli 5.233.0 → 5.234.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.233.0",
3
+ "version": "5.234.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.",
@@ -1455,5 +1455,40 @@
1455
1455
  // the roll shows what the network lends it. Those are two different
1456
1456
  // facts on purpose, and there is a test pinning them apart so nobody
1457
1457
  // "fixes" one to match the other.
1458
- export const ENGINE_VERSION = '1.84.0';
1458
+ // 1.85.0 (2026-09-16): SPAMMING `look` COSTS SOMETHING NOW (p.49)
1459
+ // Asked directly, and it was the right question: "what is the penalty
1460
+ // of just spamming 'look' to eventually get the details?"
1461
+ // THE ANSWER WAS NONE. `look` on the grid is billed as the Complex
1462
+ // Action it is (p.241) -- but billAction only charges inside an
1463
+ // encounter, so out of combat a sweep cost nothing at all. A decker
1464
+ // could stand in the dark typing `look` until the dice came up, which
1465
+ // means every hidden icon in the game was on a TIMER rather than
1466
+ // behind a test, and the whole of 1.80-1.84 was theatre for anyone
1467
+ // willing to press a key four times. My own find-rate measurements
1468
+ // said "found within 1-2 looks" and I read that as the mechanic
1469
+ // working rather than as the mechanic being free.
1470
+ // - TRYING AGAIN (p.49): "each attempt after the first takes a
1471
+ // cumulative -2 dice pool penalty." Counted per room on
1472
+ // Player.sweepAttempts, and only FAILED sweeps count -- pinning
1473
+ // what you contested is not a retry. A 7-dice sweeper gets 7, 5, 3,
1474
+ // 1, then nothing.
1475
+ // - AND IT REACHES ZERO. The base pool floors at 1 so wounds and
1476
+ // noise never lock you out, but the retry penalty is allowed
1477
+ // through that floor, because that is the part that has to stop the
1478
+ // grind: a pool of nothing cannot be rolled. The line says so
1479
+ // rather than printing a mystery 0d6.
1480
+ // - THE BREAK is the one piece p.49 hands to the table ("left to
1481
+ // gamemaster discretion"), so it was asked rather than guessed.
1482
+ // Ruling: JACKING OUT, and nothing less -- not moving node, not
1483
+ // spending a turn on something else. So clearing a stack of failed
1484
+ // sweeps costs your position, your part-made marks and a fresh
1485
+ // entry.
1486
+ // - NO OVERWATCH COST, deliberately. OS rises on Attack and Sleaze
1487
+ // actions (p.231-232) and Matrix Perception is neither. Charging
1488
+ // one would be inventing a rule to fix a balance problem canon
1489
+ // already fixes.
1490
+ // Nothing here is new to this engine: clue-reading has used p.49 and
1491
+ // the same -2 since 1.40. It had simply never been pointed at the one
1492
+ // test a player can repeat by holding down a key.
1493
+ export const ENGINE_VERSION = '1.85.0';
1459
1494
  //# sourceMappingURL=engine-version.js.map
@@ -717,6 +717,37 @@ export class Player extends AbstractPlayer {
717
717
  * (-2 per retry). In memory only: a save and reload is the "break"
718
718
  * the rule says clears it. See utilities/clue-reading.ts. */
719
719
  readAttempts = new Map();
720
+ /**
721
+ * FAILED MATRIX PERCEPTION SWEEPS, by room name -- p.49's Trying Again
722
+ * pointed at the one test a player can repeat for free.
723
+ *
724
+ * Asked directly (2026-09-16): "what is the penalty of just spamming
725
+ * 'look' to eventually get the details?" The answer was NONE. `look`
726
+ * is billed as the Complex Action it is (p.241), but billAction only
727
+ * charges inside an encounter, so out of combat a sweep cost nothing
728
+ * at all and a decker could stand in the dark typing `look` until the
729
+ * dice came up. Every hidden icon in the game was on a timer rather
730
+ * than behind a test.
731
+ *
732
+ * p.49: "you can try again, but each attempt after the first takes a
733
+ * cumulative -2 dice pool penalty ... a sufficient break lets you
734
+ * begin again with no penalty." Not invented here and not new to this
735
+ * engine either -- clue-reading has used the same rule and the same
736
+ * citation since 1.40. A sweeper at 7 dice gets 7, 5, 3, 1: four looks
737
+ * and brute force is spent, which is the point.
738
+ *
739
+ * NO OVERWATCH COST, deliberately. OS rises on Attack and Sleaze
740
+ * actions (p.231-232) and Matrix Perception is neither, so charging
741
+ * one would be inventing a rule to solve a balance problem that canon
742
+ * already solves.
743
+ *
744
+ * WHAT COUNTS AS THE BREAK is the one part p.49 hands to the table
745
+ * ("left to gamemaster discretion"), so it was asked and ruled on
746
+ * rather than guessed: JACKING OUT, and nothing less (user ruling
747
+ * 2026-09-16). Not moving node, not spending a turn elsewhere. See
748
+ * planes.ts leaveMatrix, which is the only thing that clears this.
749
+ */
750
+ sweepAttempts = new Map();
720
751
  /** True for the one Electronics item that is a trode net (p.439-440). */
721
752
  static isTrodeNet(item) {
722
753
  // The item's OWN row, not Item.effects: that getter folds fitted
@@ -222,6 +222,19 @@ export function leaveMatrix(scene, actor, opts) {
222
222
  // bricked, Scramble, convergence -- is no longer a participant. Before
223
223
  // endPersona clears the position the arena is read off.
224
224
  scene.encounterFor?.(actor)?.leave(actor);
225
+ // THE BREAK p.49 DESCRIBES, and the book hands this one to the table:
226
+ // "if the character takes a sufficient break from the task -- the
227
+ // length of which is left to gamemaster discretion." Asked directly
228
+ // (user ruling 2026-09-16, "what is a break?"), and the answer was the
229
+ // strictest of the four on offer: NOTHING SHORT OF JACKING OUT.
230
+ //
231
+ // Not moving to another node, not spending a turn on something else.
232
+ // So a stack of failed sweeps costs you your position on the grid,
233
+ // whatever marks you were part-way through, and a fresh entry -- which
234
+ // makes a dark room genuinely expensive rather than a typing exercise.
235
+ // Optional-chained because leaveMatrix is reached from a dozen places
236
+ // with stub actors that are not full Players.
237
+ actor.sweepAttempts?.clear();
225
238
  if (opts?.forced) {
226
239
  lines.push(...applyDumpshock(actor, opts.reason ?? `The connection is severed from the other side.`));
227
240
  }
@@ -116,8 +116,21 @@ export function spotHiddenIcons(scene, actor, room) {
116
116
  // sweeper's own silence, wounds and sustaining the way every other
117
117
  // Matrix action in this engine does.
118
118
  const computer = actor.skillRating('computer');
119
- const pool = Math.max(1, actor.intuition + (computer > 0 ? computer : -1)
119
+ // TRYING AGAIN (p.49): "each attempt after the first takes a
120
+ // cumulative -2 dice pool penalty." Without it, spamming `look` was
121
+ // free outside a fight -- billAction charges a Complex Action only
122
+ // inside an encounter -- so every hidden icon was on a timer instead
123
+ // of behind a test. Counted per room and cleared by a break
124
+ // (Player.sweepAttempts).
125
+ const retries = actor.sweepAttempts.get(room.name) ?? 0;
126
+ // The base floors at 1 -- wounds and noise grind you down but never
127
+ // out. The RETRY penalty is allowed through that floor to zero,
128
+ // because that is what stops the grind: a pool reduced to nothing
129
+ // cannot be rolled, so after enough failures you are not looking
130
+ // harder, you are done looking until you take the break p.49 names.
131
+ const base = Math.max(1, actor.intuition + (computer > 0 ? computer : -1)
120
132
  + actor.matrixActionPenalty + actor.woundModifier - actor.sustainingPenalty);
133
+ const pool = Math.max(0, base - 2 * retries);
121
134
  const roll = rollPool(pool, actor.matrixAttribute('dataProcessing'), { gremlins: actor.deckGremlins });
122
135
  const found = [];
123
136
  let missed = 0;
@@ -139,7 +152,14 @@ export function spotHiddenIcons(scene, actor, room) {
139
152
  against.push({ roll: resist });
140
153
  }
141
154
  }
142
- return { found, missed, roll, against };
155
+ // p.49 counts FAILED attempts. A sweep that pinned everything it
156
+ // contested was not a failure, so it costs the next one nothing --
157
+ // and an icon you already found is not in `hidden` to be re-rolled.
158
+ if (missed > 0)
159
+ actor.sweepAttempts.set(room.name, retries + 1);
160
+ else
161
+ actor.sweepAttempts.delete(room.name);
162
+ return { found, missed, roll, retries, against };
143
163
  }
144
164
  /**
145
165
  * The mechanics lines for a sweep.
@@ -153,8 +173,15 @@ export function spotHiddenIcons(scene, actor, room) {
153
173
  * Perception HIT buys).
154
174
  */
155
175
  export function spotMechanics(outcome) {
176
+ // Said out loud, because a pool that quietly shrinks every time you
177
+ // type the same word reads as a bug rather than as p.49.
178
+ const again = outcome.roll.pool === 0
179
+ ? ` -- NO DICE LEFT (p.49: -${outcome.retries * 2} for ${outcome.retries} failed attempts). Staring harder will not do it; break off and come back.`
180
+ : outcome.retries > 0
181
+ ? ` -- trying again, -${outcome.retries * 2} (p.49: -2 per failed attempt; a break clears it)`
182
+ : '';
156
183
  return [
157
- `Matrix Perception, silent icons (Computer + Intuition [Data Processing]): ${formatRoll(outcome.roll)}`,
184
+ `Matrix Perception, silent icons (Computer + Intuition [Data Processing]): ${formatRoll(outcome.roll)}${again}`,
158
185
  ...outcome.against.map(a => (a.name
159
186
  ? ` ${a.name} hides (${a.label}): ${formatRoll(a.roll)}`
160
187
  : ` something holds its cover: ${formatRoll(a.roll)}`)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.233.0",
3
+ "version": "5.234.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.",