@maka/maka-cli 5.146.0 → 5.147.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/advance.js +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/counterspell.js +54 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/repro-scene.js +24 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.js +6 -1
- package/bundle/typescript/src/commands/game/sideQuest/types/repro.js +4 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.147.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.",
|
|
@@ -73,6 +73,14 @@ export class AdvanceCommand extends Command {
|
|
|
73
73
|
this.actor.performAction('trains', skill);
|
|
74
74
|
this.logger.write(`${actor.name} advanced ${skill} to ${next} (spent ${cost} karma, ${actor.karma} left).`);
|
|
75
75
|
this.scene.updateStatus();
|
|
76
|
+
// A DURABLE CHANGE (fDswPCSf8tGhMPMZ6, same shape as vR8ajvm3wXdexxdF2:
|
|
77
|
+
// buy/sell learned this, advance never did). Karma spent and a skill
|
|
78
|
+
// raised is exactly the kind of change equip.ts already asks for a
|
|
79
|
+
// save over -- without it, a player who trains and quits before the
|
|
80
|
+
// next autosave beat reloads to a lower skill AND the karma still
|
|
81
|
+
// gone, and the web character sheet (reading the last saved doc)
|
|
82
|
+
// shows the stale rating.
|
|
83
|
+
this.game?.requestSave('advance');
|
|
76
84
|
return `You drill ${skill} until it sticks: ${current} -> ${next}. (${cost} karma spent, ${actor.karma} left.)`;
|
|
77
85
|
}
|
|
78
86
|
trainAttribute(attr) {
|
|
@@ -111,6 +119,7 @@ export class AdvanceCommand extends Command {
|
|
|
111
119
|
this.actor.performAction('trains', attr);
|
|
112
120
|
this.logger.write(`${actor.name} advanced ${attr} to base ${attr === 'magic' ? actor.baseMagic : actor.baseResonance} (effective ${effective}; spent ${cost} karma, ${actor.karma} left).`);
|
|
113
121
|
this.scene.updateStatus();
|
|
122
|
+
this.game?.requestSave('advance');
|
|
114
123
|
return `The talent deepens: ${attr} ${base} -> ${base + 1}${actor.essenceBurn > 0 ? ` (effective ${effective} after the chrome's toll)` : ''}. (${cost} karma spent, ${actor.karma} left.)`;
|
|
115
124
|
}
|
|
116
125
|
const current = actor[attr];
|
|
@@ -136,6 +145,7 @@ export class AdvanceCommand extends Command {
|
|
|
136
145
|
this.actor.performAction('trains', attr);
|
|
137
146
|
this.logger.write(`${actor.name} advanced ${attr} to ${next} (spent ${cost} karma, ${actor.karma} left).`);
|
|
138
147
|
this.scene.updateStatus();
|
|
148
|
+
this.game?.requestSave('advance');
|
|
139
149
|
const trackNote = attr === 'body' ? ` Your physical track grows with it.` : attr === 'reaction' ? ` Your stun track grows with it.` : '';
|
|
140
150
|
return `Weeks of drills compressed into downtime: ${attr} ${current} -> ${next}. (${cost} karma spent, ${actor.karma} left.)${trackNote}`;
|
|
141
151
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Command } from './command.js';
|
|
2
|
+
/**
|
|
3
|
+
* "there should be a command for counterspelling" (3vCH5uQvpKZdjzWYQ).
|
|
4
|
+
*
|
|
5
|
+
* There already is counterspelling -- it just isn't a command. RAW
|
|
6
|
+
* (p.294) is a STANDING PARRY: a Counterspelling-skilled defender's
|
|
7
|
+
* dice roll straight into the defense test the instant a hostile spell
|
|
8
|
+
* targets them, and a co-located party caster can extend theirs over a
|
|
9
|
+
* teammate the same way ("one source, no stacking" -- the better rating
|
|
10
|
+
* covers, they don't add). combat-exchange.ts already does exactly
|
|
11
|
+
* this, automatically, on every spell strike, and narrates it on the
|
|
12
|
+
* Defense line ("+N counterspell -- Name's cover") when it bites.
|
|
13
|
+
*
|
|
14
|
+
* So the report's actual gap is not mechanics, it's that typing
|
|
15
|
+
* "counterspell" got "I don't understand the command" -- a player
|
|
16
|
+
* reaching for a verb that (correctly, per RAW) does not exist. This
|
|
17
|
+
* command answers that reach with the truth instead of a parser error:
|
|
18
|
+
* what protection they're already carrying and why nothing needs to be
|
|
19
|
+
* typed to use it.
|
|
20
|
+
*
|
|
21
|
+
* ONE HONEST GAP: canon also lets a defender who has already spent
|
|
22
|
+
* their Free Action declare protection reactively as an Interrupt
|
|
23
|
+
* Action at Initiative -5 (p.294). This engine's Action Phase economy
|
|
24
|
+
* (utilities/action-cost.ts) has no off-phase Free Action today --
|
|
25
|
+
* `IBillOptions.anyPhase` is declared and reserved for exactly this,
|
|
26
|
+
* unused by anything yet -- so the reactive-declare half of spell
|
|
27
|
+
* defense isn't modeled. What's implemented (the standing parry, which
|
|
28
|
+
* is what actually answers "I have counterspelling, why didn't it
|
|
29
|
+
* help") works with no action spent at all, which is the more common
|
|
30
|
+
* case by far.
|
|
31
|
+
*/
|
|
32
|
+
export class CounterspellCommand extends Command {
|
|
33
|
+
static verb = 'counterspell';
|
|
34
|
+
static description = 'Check your counterspelling cover. Nothing to spend: your Counterspelling skill (or a co-located party caster\'s, if theirs is better) automatically rolls into the defense the instant a hostile spell targets you or an ally.';
|
|
35
|
+
async execute(_args = []) {
|
|
36
|
+
const actor = this.actor;
|
|
37
|
+
if (actor.isAspected && !actor.canWorkSorcery()) {
|
|
38
|
+
return `Counterspelling is Sorcery, and your gift is aspected elsewhere -- you can see the weave, not turn it.`;
|
|
39
|
+
}
|
|
40
|
+
if (typeof actor.isCaster !== 'function' || !actor.isCaster()) {
|
|
41
|
+
return `Counterspelling takes the Art or a spell formula, grimoire, or focus on you -- you have none.`;
|
|
42
|
+
}
|
|
43
|
+
const rating = actor.skillRating('counterspelling');
|
|
44
|
+
if (rating <= 0) {
|
|
45
|
+
return `You have no Counterspelling trained -- "qualities" or "advance" at home if you want to pick it up.`;
|
|
46
|
+
}
|
|
47
|
+
return `Counterspelling isn't something you trigger -- it's a standing parry (SR5 p.294).`
|
|
48
|
+
+ `\nEvery hostile spell that targets you, or a party member you're co-located with, automatically`
|
|
49
|
+
+ `\nrolls your counterspelling ${rating} into their defense test -- no action spent. If more than one`
|
|
50
|
+
+ `\ncaster in the room could cover the same target, the best single rating applies (it doesn't stack).`
|
|
51
|
+
+ `\nWatch the Defense line for "+${rating} counterspell" when it bites.`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=counterspell.js.map
|
|
@@ -162,6 +162,24 @@ export function buildReproScene(fixture) {
|
|
|
162
162
|
throw new ReproFixtureError(`"${misplacedIce.name}" is ice, so it cannot also stand in a doorway, guard, circulate or carry a gun. `
|
|
163
163
|
+ `Ice lives in the host, not on the floor -- drop the meat-side flags, or drop "ice".`);
|
|
164
164
|
}
|
|
165
|
+
// A SEED HAS ONE PLANE. astral maps to 'astral' the same way ice maps
|
|
166
|
+
// to 'matrix' (npc-seed.ts) -- asking for both is asking the builder
|
|
167
|
+
// to pick a lie.
|
|
168
|
+
const bothPlanes = npcs.find(n => n.ice && n.astral);
|
|
169
|
+
if (bothPlanes) {
|
|
170
|
+
throw new ReproFixtureError(`"${bothPlanes.name}" is asked to be both ice and astral -- an actor has one plane, pick one.`);
|
|
171
|
+
}
|
|
172
|
+
// AN ASTRAL FORM CANNOT HOLD A DOOR SHUT OR CARRY A GUN. "guarding"
|
|
173
|
+
// means blocking meat movement, and "an astral presence has no body
|
|
174
|
+
// to block with" IS the fix this flag exists to bench -- a fixture
|
|
175
|
+
// asking for both would prove the opposite of what it claims. armed
|
|
176
|
+
// hands out a pistol and a commlink, and an astral form carries no
|
|
177
|
+
// physical object (SR5 p.301-302, Spirit Basics).
|
|
178
|
+
const contradictoryAstral = npcs.find(n => n.astral && (n.guarding || n.armed));
|
|
179
|
+
if (contradictoryAstral) {
|
|
180
|
+
throw new ReproFixtureError(`"${contradictoryAstral.name}" is astral, so it cannot guard a door or carry a gun -- `
|
|
181
|
+
+ `an astral form has no body to block or hold anything with. Drop the flag, or drop "astral".`);
|
|
182
|
+
}
|
|
165
183
|
const barrier = fixture.barrier;
|
|
166
184
|
const direction = barrier?.sealsExit ?? Direction.NORTH;
|
|
167
185
|
const startSide = fixture.start ?? 'barrier';
|
|
@@ -339,6 +357,12 @@ export function buildReproScene(fixture) {
|
|
|
339
357
|
};
|
|
340
358
|
}
|
|
341
359
|
return {
|
|
360
|
+
// UNLIKE ice, astral does not move the NPC anywhere -- it keeps
|
|
361
|
+
// the ordinary placement below. The whole point of the fix this
|
|
362
|
+
// flag benches is that an astral actor stands right where a meat
|
|
363
|
+
// one would and still does not block (seatingIn is unchanged by
|
|
364
|
+
// the fix; only WHETHER it blocks changed, never WHERE it sits).
|
|
365
|
+
...(npc.astral ? { plane: 'astral' } : {}),
|
|
342
366
|
description: npc.concept,
|
|
343
367
|
dialogue: [],
|
|
344
368
|
player: {
|
|
@@ -170,6 +170,7 @@ import { SneakCommand } from './commands/sneak.js';
|
|
|
170
170
|
import { PersuadeCommand } from './commands/persuade.js';
|
|
171
171
|
import { BluffCommand } from './commands/bluff.js';
|
|
172
172
|
import { DispelCommand } from './commands/dispel.js';
|
|
173
|
+
import { CounterspellCommand } from './commands/counterspell.js';
|
|
173
174
|
import { BreachCommand } from './commands/breach.js';
|
|
174
175
|
import { EmoteCommand } from './commands/emote.js';
|
|
175
176
|
import { OverwatchCommand } from './commands/overwatch.js';
|
|
@@ -2648,6 +2649,10 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
2648
2649
|
CommandFactory.registerCommand('dispel', DispelCommand);
|
|
2649
2650
|
CommandFactory.registerCommand('dispell', DispelCommand);
|
|
2650
2651
|
CommandFactory.registerCommand('breach', BreachCommand);
|
|
2652
|
+
// "counterspell" is not an action to spend -- it explains the
|
|
2653
|
+
// standing parry combat-exchange.ts already applies automatically
|
|
2654
|
+
// (3vCH5uQvpKZdjzWYQ).
|
|
2655
|
+
CommandFactory.registerCommand('counterspell', CounterspellCommand);
|
|
2651
2656
|
// Free-form roleplay ("me" is the classic MUD spelling).
|
|
2652
2657
|
CommandFactory.registerCommand('emote', EmoteCommand);
|
|
2653
2658
|
CommandFactory.registerCommand('me', EmoteCommand);
|
|
@@ -6051,7 +6056,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
6051
6056
|
// "players" folded back under crew after playtesting ("crew
|
|
6052
6057
|
// players" / "crew street"); the bare verb still answers quietly.
|
|
6053
6058
|
{ title: 'party', entries: [['crew', 'party'], ['hire'], ['dismiss'], ['train'], ['order'], ['lead'], ['command'], ['deploy'], ['recall'], ['stow']] },
|
|
6054
|
-
{ title: 'magic', entries: [['spells'], ['cast'], ['summon', 'conjure'], ['project', 'astral'], ['return'], ['assense']] },
|
|
6059
|
+
{ title: 'magic', entries: [['spells'], ['cast'], ['summon', 'conjure'], ['project', 'astral'], ['return'], ['assense'], ['counterspell']] },
|
|
6055
6060
|
{ title: 'matrix', entries: [['jack'], ['jackpoint'], ['deck'], ['load'], ['unload'], ['hack'], ['mark'], ['download'], ['enter'], ['exit-host'], ['hop'], ['tap', 'splice'], ['snoop'], ['overwatch', 'os'], ['pan'], ['ar'], ['aros'], ['silent'], ['reboot'], ['agent'], ['drone'], ['jump', 'rig']] },
|
|
6056
6061
|
// The Emerged get their own shelf (player request: "there MUST be
|
|
6057
6062
|
// resonance" -- matrix is the place, Resonance is the talent).
|
|
@@ -20,8 +20,11 @@
|
|
|
20
20
|
* would themselves have been a MINOR bump had a version already
|
|
21
21
|
* existed. Read it as "schema, second edition", not "second edition
|
|
22
22
|
* broke something".
|
|
23
|
+
*
|
|
24
|
+
* 2.4.0 adds IReproNpc.astral -- purely additive, older drafts still
|
|
25
|
+
* valid.
|
|
23
26
|
*/
|
|
24
|
-
export const REPRO_SCHEMA_VERSION = '2.
|
|
27
|
+
export const REPRO_SCHEMA_VERSION = '2.4.0';
|
|
25
28
|
/** THE LIVE REASON, from the field or the card's own words.
|
|
26
29
|
*
|
|
27
30
|
* `repro.live` is authoritative -- but the server's validator must
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.147.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.",
|