@maka/maka-cli 5.211.0 → 5.213.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/bypass-command.js +22 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/download.js +31 -5
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +60 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/tap.js +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +62 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/action-cost.js +35 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.213.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.",
|
|
@@ -89,6 +89,15 @@ export class BypassCommand extends Command {
|
|
|
89
89
|
}
|
|
90
90
|
return { error: `There's nothing here called "${raw}".` };
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* THE ACTION PHASE COST OF ONE ATTEMPT, charged at the moment the
|
|
94
|
+
* attempt becomes real (see execute()). Free by default; a subclass
|
|
95
|
+
* whose verb IS a canon Matrix or combat action overrides it and
|
|
96
|
+
* returns the refusal when the phase cannot pay.
|
|
97
|
+
*/
|
|
98
|
+
billForAttempt() {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
92
101
|
async execute(args) {
|
|
93
102
|
const room = this.actor.currentLocation;
|
|
94
103
|
const all = room?.openableDevices() ?? [];
|
|
@@ -164,6 +173,19 @@ export class BypassCommand extends Command {
|
|
|
164
173
|
if (reach.refusal)
|
|
165
174
|
return reach.refusal;
|
|
166
175
|
const crossNote = reach.line ? `${reach.line}\n` : '';
|
|
176
|
+
// THE LAST POINT BEFORE THE ATTEMPT IS REAL -- the target resolved,
|
|
177
|
+
// the verb allowed, the runner standing where the mechanism is. A
|
|
178
|
+
// subclass with an Action Phase cost pays it HERE and nowhere
|
|
179
|
+
// earlier, so every refusal above is free (GoEyGG9PSRM4mpXzG: "I
|
|
180
|
+
// shouldn't be hit on a simple/complex action if the command is
|
|
181
|
+
// rejected").
|
|
182
|
+
//
|
|
183
|
+
// Default is free: most verbs on this base (pick, breach, use) have
|
|
184
|
+
// never billed an action and this is not the change that gives them
|
|
185
|
+
// one. Only HackCommand overrides it today.
|
|
186
|
+
const billed = this.billForAttempt();
|
|
187
|
+
if (billed)
|
|
188
|
+
return billed;
|
|
167
189
|
const outcome = await this.determineOutcome(device);
|
|
168
190
|
if (outcome === 'progress') {
|
|
169
191
|
this.logger.write(`${this.actor.name} keeps working "${device.name}" (${this.verbName}, in progress)`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command } from './command.js';
|
|
2
|
+
import { billAction } from '../utilities/action-cost.js';
|
|
2
3
|
import { roomsInReach, gridVicinity } from '../utilities/grid-reach.js';
|
|
3
4
|
import { hint } from '../utilities/hints.js';
|
|
4
5
|
import { Category } from '../types/shared/item-enum.js';
|
|
@@ -38,11 +39,15 @@ import { fileReach, planeTintItemName } from '../utilities/planes.js';
|
|
|
38
39
|
* else's sprite is not a shelf you may take from -- that is a hack, and
|
|
39
40
|
* hack.ts owns it.
|
|
40
41
|
*
|
|
41
|
-
* DELIBERATELY NOT A TEST. Pulling a file you
|
|
42
|
-
* not an opposed action in this engine --
|
|
43
|
-
* either, and getting past whatever GUARDED
|
|
44
|
-
* crack and the mark already charged for.
|
|
45
|
-
* the same obstacle twice.
|
|
42
|
+
* DELIBERATELY NOT A TEST -- BUT IT IS AN ACTION. Pulling a file you
|
|
43
|
+
* already have reach on is not an opposed action in this engine --
|
|
44
|
+
* `take` does not roll for it either, and getting past whatever GUARDED
|
|
45
|
+
* the file is what the host crack and the mark already charged for.
|
|
46
|
+
* Adding dice here would price the same obstacle twice.
|
|
47
|
+
*
|
|
48
|
+
* The TIME is a separate bill, and it was going unpaid: copying a file
|
|
49
|
+
* is Edit File, a COMPLEX ACTION (p.239). See the billAction call in
|
|
50
|
+
* execute() for what that was costing in a fight.
|
|
46
51
|
*/
|
|
47
52
|
export class DownloadCommand extends Command {
|
|
48
53
|
static verb = 'download';
|
|
@@ -116,6 +121,27 @@ export class DownloadCommand extends Command {
|
|
|
116
121
|
if (wc?.type === 'erase' && wc.item.toLowerCase() === item.name.toLowerCase()) {
|
|
117
122
|
return `Pulling ${item.name} down changes nothing -- the desk keeps its own. ${hint(`"erase ${item.name}" is what makes it go away (Edit File, p.239).`)}`;
|
|
118
123
|
}
|
|
124
|
+
// COPYING A FILE IS EDIT FILE, AND EDIT FILE IS A COMPLEX ACTION
|
|
125
|
+
// (p.239: "Edit File allows you to create, change, COPY, delete, or
|
|
126
|
+
// protect any kind of file"). The no-dice ruling above stands -- the
|
|
127
|
+
// host crack already charged for the obstacle -- but "not a test"
|
|
128
|
+
// was silently read as "not an action", and in a Combat Turn that is
|
|
129
|
+
// a different claim entirely. Found in jfjjTmJ7NYwDaFiyB's own
|
|
130
|
+
// bench: walking into a rating-3 host wakes Patrol IC, `search` is
|
|
131
|
+
// correctly refused ("a Matrix Search takes minutes ... a Combat
|
|
132
|
+
// Turn is three seconds") and `download` then emptied the archive
|
|
133
|
+
// for FREE with the ice mid-turn on the runner. That makes the fight
|
|
134
|
+
// the report asked for optional -- stroll in, take the paydata, and
|
|
135
|
+
// the ice never mattered.
|
|
136
|
+
//
|
|
137
|
+
// billAction is a no-op outside an encounter, so the quiet case --
|
|
138
|
+
// an empty host, a file loose on the grid -- is untouched: still one
|
|
139
|
+
// verb, no roll, no friction. It bills only where an action economy
|
|
140
|
+
// exists to bill against, and a refusal returns BEFORE the item
|
|
141
|
+
// moves, so a runner out of actions keeps both the file and the turn.
|
|
142
|
+
const bill = billAction(this.scene, actor, 'complex', 'Edit File (copy)');
|
|
143
|
+
if (bill)
|
|
144
|
+
return bill;
|
|
119
145
|
// THE CONVERSION, identical to take.ts's. Same two fields, because a
|
|
120
146
|
// file that came down by one verb and a file that came down by the
|
|
121
147
|
// other must be the same object afterwards -- a chip you can `give`.
|
|
@@ -13,7 +13,7 @@ import { TapCommand } from './tap.js';
|
|
|
13
13
|
import { nameableNames } from '../utilities/nameables.js';
|
|
14
14
|
import { sameHostSide } from '../models/player.js';
|
|
15
15
|
import { MAX_MARKS } from '../utilities/marks.js';
|
|
16
|
-
import { billAction } from '../utilities/action-cost.js';
|
|
16
|
+
import { actionRefusal, billAction } from '../utilities/action-cost.js';
|
|
17
17
|
import { showsMechanics, mechanicsActorPrefix } from '../utilities/mechanics-audience.js';
|
|
18
18
|
import { declarationPenalty, parseMarkDeclaration, hostDefensePool, wanDefensePool, freeMatrixPerceptionHits, bruteForceMatrixDv, overwatchFromDefense, GO_BIG_QUALITY, deviceDefensePool } from '../utilities/matrix-intrusion.js';
|
|
19
19
|
/**
|
|
@@ -54,6 +54,23 @@ export class HackCommand extends BypassCommand {
|
|
|
54
54
|
actionName() {
|
|
55
55
|
return this.mode() === 'attack' ? 'Brute Force' : 'Hack on the Fly';
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* THE SPEND, at the moment the verb commits -- see the long note in
|
|
59
|
+
* execute() for why it is not at the top any more
|
|
60
|
+
* (GoEyGG9PSRM4mpXzG). Called immediately before a roll, or before
|
|
61
|
+
* handing off to a command that does the work. Returns the refusal
|
|
62
|
+
* when the phase cannot pay, undefined when it just did.
|
|
63
|
+
*/
|
|
64
|
+
bill() {
|
|
65
|
+
return billAction(this.scene, this.actor, 'complex', this.actionName());
|
|
66
|
+
}
|
|
67
|
+
/** The meat-world barrier route (super.execute) pays at the same
|
|
68
|
+
* moment every other route does -- once the device is resolved and
|
|
69
|
+
* the attempt is about to happen, never for a name that matched
|
|
70
|
+
* nothing. */
|
|
71
|
+
billForAttempt() {
|
|
72
|
+
return this.bill();
|
|
73
|
+
}
|
|
57
74
|
async execute(args = []) {
|
|
58
75
|
const mode = this.mode();
|
|
59
76
|
const afterMode = [...(args ?? [])];
|
|
@@ -66,9 +83,26 @@ export class HackCommand extends BypassCommand {
|
|
|
66
83
|
// BRUTE FORCE and HACK ON THE FLY are Complex Actions (p.238, p.240)
|
|
67
84
|
// -- billed only inside a Combat Turn, where one intrusion is the
|
|
68
85
|
// whole Action Phase.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
//
|
|
87
|
+
// ASKED HERE, PAID LATER (GoEyGG9PSRM4mpXzG, Maka: "I shouldn't be
|
|
88
|
+
// hit on a simple/complex action if the command is rejected").
|
|
89
|
+
// This used to be the spend, at the top, before a single line of
|
|
90
|
+
// target resolution had run -- so "hack camera" in a room with no
|
|
91
|
+
// camera printed "Nothing on the grid answers to 'camera'" AND ate
|
|
92
|
+
// the whole Action Phase. The player was charged an intrusion for a
|
|
93
|
+
// word the parser did not recognise.
|
|
94
|
+
//
|
|
95
|
+
// The affordability question still belongs up here: a runner with
|
|
96
|
+
// nothing left this phase should hear it before the game goes
|
|
97
|
+
// hunting through PANs, devices and hosts on their behalf. What
|
|
98
|
+
// moved is the SPEND -- down to `bill()` below, called at each point
|
|
99
|
+
// the verb actually commits: immediately before a roll, or before
|
|
100
|
+
// handing off to a command that does the work (TapCommand, the
|
|
101
|
+
// barrier bypass in super.execute). Every refusal between here and
|
|
102
|
+
// there costs nothing, which is the report.
|
|
103
|
+
const unaffordable = actionRefusal(this.scene, this.actor, 'complex');
|
|
104
|
+
if (unaffordable)
|
|
105
|
+
return unaffordable;
|
|
72
106
|
// PAN warfare: "hack <name>" cracks a meat actor's personal area
|
|
73
107
|
// network -- from inside the Matrix, or right here in AR with a
|
|
74
108
|
// working deck in hand (which is also how enemy deckers get YOU).
|
|
@@ -99,6 +133,11 @@ export class HackCommand extends BypassCommand {
|
|
|
99
133
|
// puzzle that happens to be named for one still falls through to
|
|
100
134
|
// the bypass path below, untouched.
|
|
101
135
|
if (rest.length > 0 && matchesCameraName(rest.join(' ')) && isWatched(this.actor.currentLocation)) {
|
|
136
|
+
// NOT BILLED HERE. TapCommand has its own refusals past this point
|
|
137
|
+
// -- a direct connection is a cable and wants your BODY in the
|
|
138
|
+
// room -- so it pays at its own commit point instead. Billing
|
|
139
|
+
// before the handoff charged a Complex Action for being told the
|
|
140
|
+
// route was not available (GoEyGG9PSRM4mpXzG, one layer down).
|
|
102
141
|
return new TapCommand({
|
|
103
142
|
actor: this.actor,
|
|
104
143
|
scene: this.scene,
|
|
@@ -208,6 +247,12 @@ export class HackCommand extends BypassCommand {
|
|
|
208
247
|
const burned = this.actor.burnedAttributeRefusal(mode === 'attack' ? 'attack' : 'sleaze');
|
|
209
248
|
if (burned)
|
|
210
249
|
return burned;
|
|
250
|
+
// Committed: a real host, resolved by name, with marks left to
|
|
251
|
+
// take and a bracket to take them with. Every refusal above this
|
|
252
|
+
// line is free.
|
|
253
|
+
const billedHost = this.bill();
|
|
254
|
+
if (billedHost)
|
|
255
|
+
return billedHost;
|
|
211
256
|
this.actor.performAction('hacks the host', room.name, { quiet: this.scene.isHumanControlled(this.actor) });
|
|
212
257
|
const attempt = await this.hostIntrusionRoll(mode, declared, room);
|
|
213
258
|
const success = attempt.success;
|
|
@@ -592,6 +637,13 @@ export class HackCommand extends BypassCommand {
|
|
|
592
637
|
const burned = actor.burnedAttributeRefusal(mode === 'attack' ? 'attack' : 'sleaze');
|
|
593
638
|
if (burned)
|
|
594
639
|
return burned;
|
|
640
|
+
// Committed: a real slaved icon, named and reachable. Note that
|
|
641
|
+
// every `return null` above is a FALL-THROUGH, not a refusal -- it
|
|
642
|
+
// hands the query on to the host or PAN route, which pays for
|
|
643
|
+
// itself if it commits. Billing before those would charge twice.
|
|
644
|
+
const billedDevice = this.bill();
|
|
645
|
+
if (billedDevice)
|
|
646
|
+
return billedDevice;
|
|
595
647
|
const declared = Math.min(declaredMarks, MAX_MARKS - held);
|
|
596
648
|
const declarePenalty = declarationPenalty(declared, actor.hasQuality(GO_BIG_QUALITY));
|
|
597
649
|
const silence = actor.matrixActionPenalty;
|
|
@@ -845,6 +897,10 @@ export class HackCommand extends BypassCommand {
|
|
|
845
897
|
const burnedPan = actor.burnedAttributeRefusal(mode === 'attack' ? 'attack' : 'sleaze');
|
|
846
898
|
if (burnedPan)
|
|
847
899
|
return burnedPan;
|
|
900
|
+
// Committed: a real person, in reach, running a PAN worth cracking.
|
|
901
|
+
const billedPan = this.bill();
|
|
902
|
+
if (billedPan)
|
|
903
|
+
return billedPan;
|
|
848
904
|
const limit = actor.matrixAttribute(mode === 'attack' ? 'attack' : 'sleaze');
|
|
849
905
|
// Logic + the better of HACKING and ELECTRONIC WARFARE + gear (canon:
|
|
850
906
|
// EW is the jamming-and-signals half of PAN warfare; best-of keeps
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command } from './command.js';
|
|
2
|
+
import { billAction } from '../utilities/action-cost.js';
|
|
2
3
|
import { hostLabel } from '../utilities/grid-names.js';
|
|
3
4
|
import { rollPool, formatRoll } from '../utilities/dice.js';
|
|
4
5
|
import { accrueOverwatch } from '../utilities/overwatch.js';
|
|
@@ -113,6 +114,21 @@ export class TapCommand extends Command {
|
|
|
113
114
|
return burned;
|
|
114
115
|
const limit = actor.matrixAttribute(mode === 'attack' ? 'attack' : 'sleaze');
|
|
115
116
|
const pool = Math.max(1, actor.logic + actor.skillRating('hacking') + actor.bonus('hacking') + silence + actor.woundModifier - actor.sustainingPenalty);
|
|
117
|
+
// THE SPEND, HERE AND NOT EARLIER (GoEyGG9PSRM4mpXzG). A direct
|
|
118
|
+
// connection does not change WHICH action you are taking -- it is
|
|
119
|
+
// still Hack on the Fly or Brute Force (p.240/p.238), and both are
|
|
120
|
+
// Complex Actions -- but every refusal above this line is a reason
|
|
121
|
+
// the attempt never happened: no cameras, no cable, no host on the
|
|
122
|
+
// other end, the host already open, the ring already full, a bracket
|
|
123
|
+
// eaten to zero. None of those is an action taken.
|
|
124
|
+
//
|
|
125
|
+
// It matters twice, because hack.ts routes "hack camera" here. That
|
|
126
|
+
// route USED to bill before delegating, which meant a decker out on
|
|
127
|
+
// the grid typing "hack camera" paid a Complex Action to be told a
|
|
128
|
+
// direct connection is a CABLE and needs their body in the room.
|
|
129
|
+
const billed = billAction(this.scene, actor, 'complex', mode === 'attack' ? 'Brute Force' : 'Hack on the Fly');
|
|
130
|
+
if (billed)
|
|
131
|
+
return billed;
|
|
116
132
|
actor.performAction('cables into the cameras', `${room.name} -- a hand on the housing, a filament out of their deck`);
|
|
117
133
|
const roll = rollPool(pool, limit);
|
|
118
134
|
if (showsMechanics(this.scene, actor)) {
|
|
@@ -984,5 +984,66 @@
|
|
|
984
984
|
// that reads like an icon you could act on. It is dropped when it
|
|
985
985
|
// lands first. (1.69.0 fixed the same heading leaking into the
|
|
986
986
|
// distance bands; this is the other half.)
|
|
987
|
-
|
|
987
|
+
// 1.72.0 (2026-09-16): COPYING A FILE IS AN ACTION, NOT JUST A VERB
|
|
988
|
+
// (jfjjTmJ7NYwDaFiyB, Ted: "the encrypted data chip was just laying
|
|
989
|
+
// there in the meat world...hardly a fun run for a decker that never
|
|
990
|
+
// needs to attack a host, get the data, download it, and get out").
|
|
991
|
+
// The generation half of that report shipped in 5.171.0 -- a
|
|
992
|
+
// data-shaped objective for a runner who can jack in is paydata on a
|
|
993
|
+
// host, refused at the skeleton. Building this item's bench on top of
|
|
994
|
+
// it is what exposed the rest: walk into a rating-3 host, Patrol IC
|
|
995
|
+
// opens a Combat Turn, "search" refuses itself correctly ("a Matrix
|
|
996
|
+
// Search takes minutes (p.241), and a Combat Turn is three seconds")
|
|
997
|
+
// -- and "download" then emptied the archive for FREE with the ice
|
|
998
|
+
// mid-turn on the runner. The fight the report asked for was
|
|
999
|
+
// optional: stroll in, take the paydata, the ice never mattered.
|
|
1000
|
+
// Copying a file IS Edit File, and Edit File IS a Complex Action
|
|
1001
|
+
// (p.239, RAG-checked: "Edit File allows you to create, change, COPY,
|
|
1002
|
+
// delete, or protect any kind of file"). download.ts now bills one.
|
|
1003
|
+
// The deliberate no-dice ruling it was built with stands untouched --
|
|
1004
|
+
// the host crack already charged for the obstacle, and dice here would
|
|
1005
|
+
// price it twice -- but "not a test" had been read as "not an action",
|
|
1006
|
+
// and in a Combat Turn those are different claims. billAction is a
|
|
1007
|
+
// no-op outside an encounter, so the quiet case (an empty host, a file
|
|
1008
|
+
// loose on the grid) is exactly as it was: one verb, no roll, no
|
|
1009
|
+
// friction. The refusal returns before the item moves, so a runner out
|
|
1010
|
+
// of actions keeps both the file and the turn, and listing what is in
|
|
1011
|
+
// reach ("download" bare) still costs nothing.
|
|
1012
|
+
// 1.73.0 (2026-09-16): A REJECTED COMMAND IS FREE (GoEyGG9PSRM4mpXzG,
|
|
1013
|
+
// Maka: "I shouldn't be hit on a simple/complex action if the command
|
|
1014
|
+
// is rejected: 'Nothing on the grid answers to \"camera\" -- no host by
|
|
1015
|
+
// that name, no PAN, no camera ...' should not count as a complex
|
|
1016
|
+
// action"). hack.ts billed its Complex Action at the TOP of execute(),
|
|
1017
|
+
// before one line of target resolution had run, so a name the grid
|
|
1018
|
+
// does not answer to cost exactly what an intrusion costs: the player
|
|
1019
|
+
// was told nothing matched and their Action Phase was gone. Canon
|
|
1020
|
+
// charges for an action TAKEN (p.163-167); a sentence the parser
|
|
1021
|
+
// refused is not one.
|
|
1022
|
+
// - The AFFORDABILITY question stays at the top, where it belongs: a
|
|
1023
|
+
// runner with nothing left this phase should hear that before the
|
|
1024
|
+
// game goes hunting through PANs, devices and hosts on their behalf.
|
|
1025
|
+
// That is the new actionRefusal() in utilities/action-cost.ts --
|
|
1026
|
+
// billAction's question without billAction's spend, same budget,
|
|
1027
|
+
// same wording, so the player cannot tell which one spoke.
|
|
1028
|
+
// - The SPEND moved down to each route's commit point: immediately
|
|
1029
|
+
// before the host intrusion roll, the slaved-device roll and the PAN
|
|
1030
|
+
// roll, and (for the meat-world barrier route) at the new
|
|
1031
|
+
// billForAttempt() hook in bypass-command.ts, which fires once the
|
|
1032
|
+
// device is resolved, the verb allowed and the runner standing where
|
|
1033
|
+
// the mechanism is. The hook is free by default -- pick, breach and
|
|
1034
|
+
// use have never billed an action and this is not the change that
|
|
1035
|
+
// gives them one.
|
|
1036
|
+
// - THE SAME DEFECT WAS ONE LAYER DOWN, found by the test rather than
|
|
1037
|
+
// by reading: "hack camera" RESOLVES in a watched room and hands off
|
|
1038
|
+
// to TapCommand, which then refuses because a direct connection is a
|
|
1039
|
+
// cable and wants your BODY in the room. Billing before the handoff
|
|
1040
|
+
// charged a Complex Action for that refusal. tap.ts now pays at its
|
|
1041
|
+
// own commit point, which also gives the `tap` verb the cost it
|
|
1042
|
+
// always owed -- a direct connection does not change WHICH action
|
|
1043
|
+
// you are taking (p.233), and both halves are Complex (p.238/p.240).
|
|
1044
|
+
// Note the shape, because it is easy to write again: a verb that bills
|
|
1045
|
+
// at the top of execute() has charged for the phase before it knows
|
|
1046
|
+
// whether it has a target, and a verb that bills before delegating has
|
|
1047
|
+
// charged for a refusal it cannot see.
|
|
1048
|
+
export const ENGINE_VERSION = '1.73.0';
|
|
988
1049
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -76,6 +76,41 @@ export function billAction(scene, actor, kind, label, opts = {}) {
|
|
|
76
76
|
scene.updateStatus?.();
|
|
77
77
|
return undefined;
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* THE SAME QUESTION, ASKED WITHOUT PAYING -- "could I afford this?"
|
|
81
|
+
*
|
|
82
|
+
* GoEyGG9PSRM4mpXzG (Maka): "I shouldn't be hit on a simple/complex
|
|
83
|
+
* action if the command is rejected: 'Nothing on the grid answers to
|
|
84
|
+
* "camera" -- no host by that name, no PAN, no camera' ... should not
|
|
85
|
+
* count as a complex action."
|
|
86
|
+
*
|
|
87
|
+
* Exactly right, and the shape of the bug is worth naming because it is
|
|
88
|
+
* easy to write again: a verb that bills at the TOP of execute() has
|
|
89
|
+
* charged for the Action Phase before it knows whether it has a target.
|
|
90
|
+
* Typing a name the grid does not answer to then costs the same as an
|
|
91
|
+
* intrusion -- the player is told "nothing answers to that" and their
|
|
92
|
+
* turn is gone. Canon charges for an action TAKEN (p.163-167); a
|
|
93
|
+
* sentence the game refused to parse is not one.
|
|
94
|
+
*
|
|
95
|
+
* But a verb still wants to say "you have nothing left this phase"
|
|
96
|
+
* BEFORE it goes hunting for a target, or the player gets a page of
|
|
97
|
+
* resolution and a refusal at the end of it. So: ask with this at the
|
|
98
|
+
* top, spend with billAction at the point the action actually commits
|
|
99
|
+
* -- after every refusal, immediately before the roll or the delegation.
|
|
100
|
+
* The pair reads the same budget and returns the same wording, so the
|
|
101
|
+
* player cannot tell which one spoke.
|
|
102
|
+
*/
|
|
103
|
+
export function actionRefusal(scene, actor, kind, opts = {}) {
|
|
104
|
+
const enc = encounterOf(scene, actor);
|
|
105
|
+
if (!enc || enc.ended)
|
|
106
|
+
return undefined;
|
|
107
|
+
if (!enc.isPhaseOf(actor))
|
|
108
|
+
return notYourPhase(enc, actor);
|
|
109
|
+
const budget = enc.budgetOf(actor);
|
|
110
|
+
if (!budget)
|
|
111
|
+
return notYourPhase(enc, actor);
|
|
112
|
+
return budget.refusalFor(kind, opts.attack === true);
|
|
113
|
+
}
|
|
79
114
|
/**
|
|
80
115
|
* A verb that is refused outright mid-fight unless it is your phase --
|
|
81
116
|
* for things that are not actions at all but still cannot happen while
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.213.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.",
|