@maka/maka-cli 5.375.0 → 5.377.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/archetypes.js +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/disable.js +16 -6
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +16 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +8 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/overwatch.js +3 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +15 -6
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +17 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-turn.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/overwatch.js +11 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.377.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.",
|
|
@@ -828,9 +828,19 @@ export function outfitArchetype(player, archetype) {
|
|
|
828
828
|
* as a manual equip would.
|
|
829
829
|
*/
|
|
830
830
|
export function equipIntoEmptySlots(player) {
|
|
831
|
+
// A DECK IS ALREADY THE COMMLINK (RsnjkLpgLcSmsrzKa, Maka: "I have a
|
|
832
|
+
// cyberdeck, not a commlink, yet the commlink section still shows").
|
|
833
|
+
// A deck "performs all commlink functions" (p.223) -- calls ride it
|
|
834
|
+
// (call.ts liveLink) -- so a commlink looted mid-run and left in the
|
|
835
|
+
// bag got WORN on the next boot with no word said, and the sheet then
|
|
836
|
+
// honestly showed gear the runner never chose to put on. Only a runner
|
|
837
|
+
// with no working deck or RCC gets one slotted for them.
|
|
838
|
+
const coveredLink = !!player.getCyberdeck() || !!player.getRcc();
|
|
831
839
|
for (const item of [...player.inventory.getAllItems()]) {
|
|
832
840
|
if (!EQUIP_ON_START.has(item.category))
|
|
833
841
|
continue;
|
|
842
|
+
if (item.category === Category.Commlink && coveredLink)
|
|
843
|
+
continue;
|
|
834
844
|
const slotInfo = player.determineEquipmentSlot(item.category);
|
|
835
845
|
if (!slotInfo)
|
|
836
846
|
continue;
|
|
@@ -429,17 +429,27 @@ export class DisableCommand extends Command {
|
|
|
429
429
|
}
|
|
430
430
|
else {
|
|
431
431
|
// A gun is a Rating 2 device with a monitor of 9 (p.228, the
|
|
432
|
-
// book's own smartgun example)
|
|
433
|
-
//
|
|
434
|
-
|
|
435
|
-
|
|
432
|
+
// book's own smartgun example).
|
|
433
|
+
//
|
|
434
|
+
// THE BOXES STAY (AD8TzWT89yFGDHMAE, Maka: "the gun I bricked
|
|
435
|
+
// never actually gets bricked"). Each spike used to be judged
|
|
436
|
+
// alone -- 9 in one hit or nothing -- so the Ancient's Browning
|
|
437
|
+
// ate 7, 6, 6 and 8 boxes and was still "one more spike from
|
|
438
|
+
// bricked" after all four. A condition monitor FILLS (p.228), the
|
|
439
|
+
// same as a camera's or a deck's; it goes on the gun's own track,
|
|
440
|
+
// and a reboot unlocks the smartlink but mends none of it.
|
|
441
|
+
const monitor = 8 + Math.ceil(this.deviceRatingOf(t) / 2);
|
|
442
|
+
t.gun.takeDeckDamage(dealt);
|
|
443
|
+
const filled = Math.min(monitor, t.gun.deckDamage);
|
|
444
|
+
if (filled >= monitor) {
|
|
436
445
|
t.gun.bricked = true;
|
|
437
|
-
lines.push(`The spike lands on ${t.owner.name}'s ${t.gun.name} -- it sparks, crackles and smokes in their hand. BRICKED: it will not fire again until somebody's bench work.`);
|
|
446
|
+
lines.push(`The spike lands on ${t.owner.name}'s ${t.gun.name} -- ${dealt} box${dealt === 1 ? '' : 'es'} (${filled}/${monitor}): it sparks, crackles and smokes in their hand. BRICKED: it will not fire again until somebody's bench work.`);
|
|
438
447
|
t.owner.performAction('suffers a PAN crack', `their ${t.gun.name} smoking in their hand`);
|
|
439
448
|
}
|
|
440
449
|
else {
|
|
441
450
|
t.gun.jammed = true;
|
|
442
|
-
|
|
451
|
+
const left = monitor - filled;
|
|
452
|
+
lines.push(`The spike lands on ${t.owner.name}'s ${t.gun.name} -- ${dealt} box${dealt === 1 ? '' : 'es'} (${filled}/${monitor}): the smartlink seizes and LOCKS. A reboot unlocks it; ${left} more box${left === 1 ? '' : 'es'} bricks it.`);
|
|
443
453
|
t.owner.performAction('suffers a PAN crack', `their ${t.gun.name} locking up in their hand`);
|
|
444
454
|
}
|
|
445
455
|
this.scene.addWorldEvent(`${actor.name} spiked ${t.owner.name}'s ${t.gun.name}.`);
|
|
@@ -1103,8 +1103,22 @@ export class HackCommand extends BypassCommand {
|
|
|
1103
1103
|
const now = Math.min(MAX_MARKS, heldMarks + 1);
|
|
1104
1104
|
target.panMarksBy.set(actor.name, now);
|
|
1105
1105
|
actor.noteConditionChanged();
|
|
1106
|
-
|
|
1107
|
-
|
|
1106
|
+
// THE SECOND KEY SAYS SO (WbCPspahquHnXX3wF, Maka: "this is
|
|
1107
|
+
// confusing"). The line that bought mark 2 used to read the same as
|
|
1108
|
+
// the one that bought mark 1 -- "that is all it is yet ... (2/2 to
|
|
1109
|
+
// command it)" and "hack again" -- so a player holding everything
|
|
1110
|
+
// Control Device asks for was told they were still short. Same
|
|
1111
|
+
// wording as the device hack's (wan-device-hack.test.ts).
|
|
1112
|
+
if (now >= MARKS_TO_COMMAND_A_PAN) {
|
|
1113
|
+
const held = target.equipment.rightHand?.weapon ?? target.equipment.leftHand?.weapon;
|
|
1114
|
+
lines.push(held?.isFirearm()
|
|
1115
|
+
? ` IN -- a second key settles on ${target.name}'s PAN (${now}/${MARKS_TO_COMMAND_A_PAN} marks). That is enough to command it: "disable ${target.name}" locks their ${held.name} (Control Device), and "brick ${target.name}" spikes it at +${2 * now} DV for the marks.`
|
|
1116
|
+
: ` IN -- a second key settles on ${target.name}'s PAN (${now}/${MARKS_TO_COMMAND_A_PAN} marks). That is enough to command it.`);
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
lines.push(` IN -- and that is all it is yet. You have a MARK on ${target.name}'s PAN (${now} of the ${MARKS_TO_COMMAND_A_PAN} it takes to command it), not a hand on the controls.`);
|
|
1120
|
+
lines.push(` A mark is the key; making their gear DO something is the next action, and it wants ${MARKS_TO_COMMAND_A_PAN}.${hint(` ("hack ${target.name}" again, or "mark ${target.name}" -- both place keys.)`)}`);
|
|
1121
|
+
}
|
|
1108
1122
|
target.performAction('is marked', 'an unfamiliar key settling on their PAN');
|
|
1109
1123
|
lines.push(...godLines);
|
|
1110
1124
|
this.scene.updateStatus();
|
|
@@ -813,7 +813,14 @@ export class MoveCommand extends Command {
|
|
|
813
813
|
// gap, not answer "you're already there".
|
|
814
814
|
if (spotOf(actor) === theirSpot)
|
|
815
815
|
return this.crossToActor(target);
|
|
816
|
-
|
|
816
|
+
// THE OPEN FLOOR IS NOT A PLACE TO WALK TO (prod X82wWvdwR3pPjZyR3,
|
|
817
|
+
// 2026-10-03: every phase of a street gang crashed here). It is
|
|
818
|
+
// bare ground in every room, named spots or none -- a street has
|
|
819
|
+
// no `spots` at all -- so someone standing on it is at their CELL,
|
|
820
|
+
// and walking to them is walking to that cell.
|
|
821
|
+
if (theirSpot === OPEN_FLOOR)
|
|
822
|
+
return this.crossToActor(target);
|
|
823
|
+
spot = room.spots?.find(s => s.name === theirSpot) ?? { name: theirSpot };
|
|
817
824
|
toWhom = target.name;
|
|
818
825
|
}
|
|
819
826
|
}
|
|
@@ -24,7 +24,9 @@ export class OverwatchCommand extends Command {
|
|
|
24
24
|
if (actor.plane !== 'matrix' && !actor.isTechnomancer() && !deck) {
|
|
25
25
|
return `Checking your overwatch takes a live persona -- jack in, or put a working deck in hand.`;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
// No file means no illegal action since the last boot -- not a score
|
|
28
|
+
// of 0, which a defender who rolled nothing leaves on a running tally.
|
|
29
|
+
if (actor.overwatchLastTick === 0) {
|
|
28
30
|
return `You listen for the grid's counting... nothing. Fresh boot, clean slate -- GOD has no file on this persona yet.`;
|
|
29
31
|
}
|
|
30
32
|
const skill = actor.skillRating('electronic-warfare');
|
|
@@ -31,7 +31,7 @@ export class RebootCommand extends Command {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
const hadArDown = actor.arOffline;
|
|
34
|
-
const hadOverwatch = actor.
|
|
34
|
+
const hadOverwatch = actor.overwatchLastTick > 0;
|
|
35
35
|
const marksOnMe = marksOnYou(this.scene, actor).length;
|
|
36
36
|
if (jammedGear.length === 0 && !hadArDown && !hadOverwatch && marksOnMe === 0) {
|
|
37
37
|
return `Your PAN is clean -- nothing needs a reboot.`;
|
|
@@ -72,7 +72,12 @@ export class RestCommand extends Command {
|
|
|
72
72
|
// like a deck does. This filtered on isCyberdeck(), so the link
|
|
73
73
|
// panel's "BRICKED (rest to repair)" sent that rider to bed and the
|
|
74
74
|
// link stayed bricked with no way out. The bench takes the whole bag.
|
|
75
|
-
|
|
75
|
+
// A spiked gun carries boxes on the same track (disable.ts), and the
|
|
76
|
+
// one in your hand is not always in the bag.
|
|
77
|
+
const damagedDevices = [...new Set([
|
|
78
|
+
...actor.inventory.getAllItems(),
|
|
79
|
+
actor.equipment.rightHand?.weapon, actor.equipment.leftHand?.weapon,
|
|
80
|
+
])].filter((i) => !!i && i.deckDamage > 0 && !i.permanentlyBricked);
|
|
76
81
|
// Any implant with a track of its own (every piece of cyberware --
|
|
77
82
|
// a housed deck is in the inventory above). p.228 bench work mends
|
|
78
83
|
// it the same way; the datajack end of a toolkit reaches it.
|
|
@@ -253,7 +258,7 @@ export class RestCommand extends Command {
|
|
|
253
258
|
if (cannotBench)
|
|
254
259
|
lines.push(` The bench waits: ${cannotBench}`);
|
|
255
260
|
for (const deck of cannotBench ? [] : damagedDevices) {
|
|
256
|
-
const wasBricked = deck.isBricked;
|
|
261
|
+
const wasBricked = deck.isBricked || deck.bricked;
|
|
257
262
|
const bench = benchRoll(actor);
|
|
258
263
|
lines.push(` Bench work on the ${deck.name}: ${formatRoll(bench.roll)}`);
|
|
259
264
|
if (bench.permanent) {
|
|
@@ -264,14 +269,18 @@ export class RestCommand extends Command {
|
|
|
264
269
|
continue;
|
|
265
270
|
}
|
|
266
271
|
const repaired = deck.repairDeck(bench.boxes);
|
|
272
|
+
// A spiked gun bricks at its own monitor (disable.ts) and carries
|
|
273
|
+
// the flag attack.ts reads; one box off a full track works again.
|
|
274
|
+
if (repaired > 0 && deck.bricked)
|
|
275
|
+
deck.bricked = false;
|
|
267
276
|
if (repaired > 0)
|
|
268
277
|
actor.noteConditionChanged(); // the deck track on the live sheet moves with the bench
|
|
269
278
|
this.logger.write(`RestCommand: ${actor.name} repaired ${repaired} boxes on ${deck.name} (${deck.deckDamage}/${deck.maxDeckBoxes}).`);
|
|
270
|
-
lines.push(` ${repaired} box${repaired === 1 ? '' : 'es'} repaired: ${deck.deckSummary()}${wasBricked && !deck.isBricked ? ' -- it boots again.' : ''}`);
|
|
279
|
+
lines.push(` ${repaired} box${repaired === 1 ? '' : 'es'} repaired: ${deck.deckSummary()}${wasBricked && !deck.isBricked && !deck.bricked ? ' -- it boots again.' : ''}`);
|
|
271
280
|
}
|
|
272
|
-
//
|
|
273
|
-
// a
|
|
274
|
-
//
|
|
281
|
+
// FLAG-ONLY BRICKS (a gun saved before spikes kept a count): one box
|
|
282
|
+
// off a full track is a device that works again -- so one hit brings
|
|
283
|
+
// it back, none leaves it dead.
|
|
275
284
|
for (const gear of cannotBench ? [] : brickedGear) {
|
|
276
285
|
const bench = benchRoll(actor);
|
|
277
286
|
lines.push(` Bench work on the ${gear.name}: ${formatRoll(bench.roll)}`);
|
|
@@ -3212,5 +3212,21 @@
|
|
|
3212
3212
|
// drops onto a weapon or garment to "fit", not onto the body.
|
|
3213
3213
|
// - A currency carrier reports its balance (IGearItem.currencyAmount), so
|
|
3214
3214
|
// a credstick stashed at home still says what it holds.
|
|
3215
|
-
|
|
3215
|
+
// 1.188.0 (2026-10-03): A GANG THAT WALKS TO YOU.
|
|
3216
|
+
// - "move to <person>" on someone standing on the OPEN FLOOR walks to
|
|
3217
|
+
// their cell. It looked the name up in `room.spots`, which a street
|
|
3218
|
+
// does not have, and threw -- so every phase of a street gang closing
|
|
3219
|
+
// on a runner failed and the gang stood still (prod X82wWvdwR3pPjZyR3).
|
|
3220
|
+
// - A failed NPC phase logs its stack, not just the message.
|
|
3221
|
+
// 1.189.0 (2026-10-03): THE SPIKE THAT STAYS, THE SECOND KEY, GOD'S BILL.
|
|
3222
|
+
// - A Data Spike's boxes on a gun ACCUMULATE on its Matrix monitor
|
|
3223
|
+
// (p.228); each was judged alone against 9, so a gun ate 27 boxes and
|
|
3224
|
+
// never bricked (AD8TzWT89yFGDHMAE). The bench mends a gun's boxes.
|
|
3225
|
+
// - The hack that places a PAN's second mark says it is enough and names
|
|
3226
|
+
// "disable" / "brick", instead of "that is all it is yet" (WbCPspahquHnXX3wF).
|
|
3227
|
+
// - Overwatch adds the defender's hits with no floor of 1 (p.231-232);
|
|
3228
|
+
// a 0-hit defence starts the tally but adds nothing (XR9xDyyxygCGzTLCz).
|
|
3229
|
+
// - A runner carrying a working deck or RCC no longer has a looted
|
|
3230
|
+
// commlink worn for them at session load (p.223, RsnjkLpgLcSmsrzKa).
|
|
3231
|
+
export const ENGINE_VERSION = '1.189.0';
|
|
3216
3232
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -600,7 +600,7 @@ export class CombatEncounter {
|
|
|
600
600
|
await runNpcActionPhase(this, next.actor);
|
|
601
601
|
}
|
|
602
602
|
catch (err) {
|
|
603
|
-
this.logger.error(`Encounter: ${next.actor.name}'s phase failed: ${err}`);
|
|
603
|
+
this.logger.error(`Encounter: ${next.actor.name}'s phase failed: ${err instanceof Error && err.stack ? err.stack : err}`);
|
|
604
604
|
}
|
|
605
605
|
this.closePhase(next);
|
|
606
606
|
}
|
|
@@ -30,7 +30,8 @@ function d6() {
|
|
|
30
30
|
}
|
|
31
31
|
/** The canon time bill (+2d6 per 15 min while tallying), settled lazily. */
|
|
32
32
|
function billTime(actor) {
|
|
33
|
-
|
|
33
|
+
// The tally runs from the first illegal action, even one that scored 0.
|
|
34
|
+
if (actor.overwatchLastTick === 0)
|
|
34
35
|
return;
|
|
35
36
|
const ticks = Math.min(MAX_BACKLOG_TICKS, Math.floor((Date.now() - actor.overwatchLastTick) / TICK_MS));
|
|
36
37
|
if (ticks <= 0)
|
|
@@ -60,9 +61,16 @@ export function accrueOverwatch(scene, actor, defenseHits, why) {
|
|
|
60
61
|
if (actor.overwatchLastTick === 0)
|
|
61
62
|
actor.overwatchLastTick = Date.now();
|
|
62
63
|
billTime(actor);
|
|
64
|
+
// NO FLOOR (XR9xDyyxygCGzTLCz): "your OS increases by the number of
|
|
65
|
+
// hits the target gets on its defense test" (p.231-232) -- a defender
|
|
66
|
+
// who rolls nothing adds nothing. This was max(1, hits), and two 0-hit
|
|
67
|
+
// Data Spikes' worth of it was the difference between 39 and a
|
|
68
|
+
// convergence. The action still STARTS the tally (the clock above):
|
|
69
|
+
// "the moment you perform an illegal action, you get an OS".
|
|
63
70
|
const before = actor.overwatchScore;
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
const added = Math.max(0, defenseHits);
|
|
72
|
+
actor.overwatchScore += added;
|
|
73
|
+
logger.write(`Overwatch: ${actor.name} +${added} (${why}) -> OS ${actor.overwatchScore}.`);
|
|
66
74
|
if (actor.overwatchScore >= CONVERGENCE_AT) {
|
|
67
75
|
return { lines: converge(scene, actor), converged: true };
|
|
68
76
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.377.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.",
|