@maka/maka-cli 5.64.0 → 5.65.1
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 +4 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.d.ts +29 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js +165 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.d.ts +40 -13
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +107 -25
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/breach.d.ts +11 -15
- package/bundle/typescript/src/commands/game/sideQuest/commands/breach.js +13 -16
- package/bundle/typescript/src/commands/game/sideQuest/commands/breach.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.d.ts +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +30 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.d.ts +40 -49
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +121 -160
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +7 -8
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +13 -13
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/cover.d.ts +30 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js +65 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.d.ts +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.js +2 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +25 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.d.ts +2 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +2 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/job-offer.js +9 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/job-offer.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/jobs.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/jobs.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +58 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +18 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.d.ts +2 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js +2 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/posture.d.ts +45 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/posture.js +115 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/posture.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +80 -9
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +35 -24
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.d.ts +17 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +158 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +15 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +82 -43
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +419 -263
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +82 -84
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.d.ts +23 -4
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +95 -79
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.d.ts +30 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.js +176 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.js +21 -5
- package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/headless.js +24 -0
- package/bundle/typescript/src/commands/game/sideQuest/headless.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/_index.d.ts +0 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/_index.js +0 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/_index.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/device.d.ts +121 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/device.js +145 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/device.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/door.d.ts +27 -8
- package/bundle/typescript/src/commands/game/sideQuest/models/door.js +15 -10
- package/bundle/typescript/src/commands/game/sideQuest/models/door.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +81 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +138 -21
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +68 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +102 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +72 -43
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js +82 -59
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +1 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +0 -22
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +78 -109
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +4 -101
- package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +24 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.d.ts +52 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.js +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.d.ts +63 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.js +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/player-seed.d.ts +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/scene-seed.d.ts +3 -2
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.d.ts +2 -28
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js +1 -14
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/ui.js +24 -0
- package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.d.ts +55 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.js +36 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.d.ts +65 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.js +137 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.d.ts +90 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js +103 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +37 -4
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.d.ts +26 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.js +48 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.d.ts +23 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.js +55 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.d.ts +57 -2
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js +46 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js +18 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +68 -38
- package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.d.ts +3 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js +3 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +36 -12
- package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +101 -27
- package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.d.ts +106 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.js +184 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +54 -5
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/maka.js +1 -1
- package/bundle/typescript/src/maka.js.map +1 -1
- package/bundle/typescript/src/tools/sdk-loader/relocate.d.ts +19 -2
- package/bundle/typescript/src/tools/sdk-loader/relocate.js +22 -2
- package/bundle/typescript/src/tools/sdk-loader/relocate.js.map +1 -1
- package/package.json +4 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.d.ts +0 -6
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js +0 -72
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js.map +0 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.d.ts +0 -6
- package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js +0 -26
- package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js.map +0 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +0 -152
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +0 -393
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +0 -1
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.d.ts +0 -28
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.js +0 -2
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.js.map +0 -1
|
@@ -1,64 +1,55 @@
|
|
|
1
1
|
import { Command } from './command.js';
|
|
2
2
|
import { exitSpot, ensureAtSpot } from '../utilities/spots.js';
|
|
3
|
+
import { applyDeviceOpened } from '../utilities/devices.js';
|
|
3
4
|
/**
|
|
4
|
-
* Shared implementation for pick/hack/cast/
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
5
|
+
* Shared implementation for pick/breach/hack/cast/dispel -- FORCING a
|
|
6
|
+
* device rather than opening it the way it was meant to open.
|
|
7
|
+
*
|
|
8
|
+
* The gate check (is it already open? does this verb even bite on this
|
|
9
|
+
* kind of thing?) always runs first and synchronously, so a doomed
|
|
10
|
+
* attempt never makes the player sit through an interactive challenge
|
|
11
|
+
* they were never going to be allowed to attempt. Each concrete command
|
|
12
|
+
* differs in its verb, the kind of device it works, the "nothing to do
|
|
13
|
+
* that to" message, how the outcome gets determined (see
|
|
14
|
+
* determineOutcome() -- every real one overrides it with a skill roll),
|
|
15
|
+
* and what failure costs beyond the noise (onForceFailure()).
|
|
16
|
+
*
|
|
17
|
+
* WHAT LEFT WITH THE PUZZLE MECHANIC: there is no burned route any
|
|
18
|
+
* more. A lock you failed to pick is a lock you can try again, which is
|
|
19
|
+
* what a lock IS -- the one-shot shortcut was a property of a riddle
|
|
20
|
+
* pretending to be an object. Failure is still loud and still raises
|
|
21
|
+
* the world; it just doesn't spend the thing.
|
|
13
22
|
*/
|
|
14
23
|
export class BypassCommand extends Command {
|
|
15
|
-
/** Optional: replaces the
|
|
16
|
-
*
|
|
17
|
-
*
|
|
24
|
+
/** Optional: replaces the stock per-kind failure line -- for a verb
|
|
25
|
+
* that shares a kind but fails differently (a breach doesn't snap a
|
|
26
|
+
* pick). */
|
|
18
27
|
failureFlavor;
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
* breach used to re-refuse a breach with the snapped-pick line -- a
|
|
22
|
-
* pick the player never owned (playtest 2026-08-27). A verb that
|
|
23
|
-
* shares a bypassType but burns differently says so here. */
|
|
24
|
-
burnedFlavor;
|
|
25
|
-
/** Fallback: a flat coin flip. Every real bypass overrides this with a
|
|
26
|
-
* skill roll (pick: Agility+locksmith, hack: Logic+hacking, bluff:
|
|
27
|
-
* Charisma+con, cast: the casting pool). */
|
|
28
|
+
/** Fallback: a flat coin flip. Every real one overrides this with a
|
|
29
|
+
* skill roll. */
|
|
28
30
|
async determineOutcome() {
|
|
29
31
|
return Math.random() < 0.5;
|
|
30
32
|
}
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
* lines. Default: nothing extra -- the cost of a snapped pick or a blown
|
|
35
|
-
* story is the burned shortcut and a district that heard about it. The
|
|
36
|
-
* spell bypass overrides this with ward backlash (see cast.ts), which
|
|
37
|
-
* runs through the real damage tracks and can still knock out or kill.
|
|
38
|
-
*/
|
|
39
|
-
async onBypassFailure() {
|
|
33
|
+
/** Type-specific teeth for a failed forcing, beyond the noise.
|
|
34
|
+
* Returns narration lines. */
|
|
35
|
+
async onForceFailure() {
|
|
40
36
|
return [];
|
|
41
37
|
}
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
* gives, so success still raises the world and ends any sneak.
|
|
48
|
-
*/
|
|
49
|
-
async onBypassSuccess(target) {
|
|
50
|
-
void target;
|
|
38
|
+
/** Type-specific consequences of a forcing that WORKED, before the
|
|
39
|
+
* success message renders (see breach.ts: smashing through is loud
|
|
40
|
+
* whether or not it gives). */
|
|
41
|
+
async onForceSuccess(device) {
|
|
42
|
+
void device;
|
|
51
43
|
return [];
|
|
52
44
|
}
|
|
53
45
|
/**
|
|
54
|
-
* There's no typed answer here to disambiguate with
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* "pick" still works
|
|
46
|
+
* There's no typed answer here to disambiguate with, so with more
|
|
47
|
+
* than one device in the room the player has to name which one: a
|
|
48
|
+
* direction ("pick north", for a device holding a door) or a word
|
|
49
|
+
* from its own name ("pick maglock"). With only one candidate, a bare
|
|
50
|
+
* "pick" still works.
|
|
59
51
|
*/
|
|
60
|
-
resolveTarget(args,
|
|
61
|
-
// "cast at hatch" / "pick at the maglock" -- connectives are free.
|
|
52
|
+
resolveTarget(args, devices) {
|
|
62
53
|
let words = [...args];
|
|
63
54
|
if (words[0]?.toLowerCase() === 'at')
|
|
64
55
|
words = words.slice(1);
|
|
@@ -66,160 +57,130 @@ export class BypassCommand extends Command {
|
|
|
66
57
|
words = words.slice(1);
|
|
67
58
|
const raw = words.join(' ').trim().toLowerCase();
|
|
68
59
|
if (!raw) {
|
|
69
|
-
if (
|
|
70
|
-
return {
|
|
71
|
-
const options =
|
|
60
|
+
if (devices.length === 1)
|
|
61
|
+
return { device: devices[0] };
|
|
62
|
+
const options = devices.map(d => d.opensExit ?? d.name).join(', ');
|
|
72
63
|
return { error: `There's more than one thing to ${this.verbName} at here -- say which: ${options}.` };
|
|
73
64
|
}
|
|
74
65
|
if (this.isValidDirection(raw)) {
|
|
75
|
-
const match =
|
|
76
|
-
return match ? {
|
|
66
|
+
const match = devices.find(d => d.opensExit === raw);
|
|
67
|
+
return match ? { device: match } : { error: `There's nothing to force in that direction.` };
|
|
77
68
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// (a real session tried "cast manabolt at hatch" three ways and
|
|
81
|
-
// bounced every time).
|
|
82
|
-
const matches = targets.filter(t => t.label.toLowerCase().includes(raw)
|
|
83
|
-
|| (t.puzzle.riddle ?? '').toLowerCase().includes(raw));
|
|
69
|
+
const matches = devices.filter(d => d.name.toLowerCase().includes(raw)
|
|
70
|
+
|| d.description.toLowerCase().includes(raw));
|
|
84
71
|
if (matches.length === 1)
|
|
85
|
-
return {
|
|
72
|
+
return { device: matches[0] };
|
|
86
73
|
if (matches.length > 1)
|
|
87
74
|
return { error: `That could mean more than one thing here -- be more specific.` };
|
|
88
|
-
// Generic
|
|
89
|
-
if (
|
|
90
|
-
return {
|
|
75
|
+
// Generic words land when there's only one thing here anyway.
|
|
76
|
+
if (devices.length === 1 && /^(lock|door|ward|barrier|seal|gate|hatch|glyph|field|sigil|way|node|terminal|panel|keypad)$/.test(raw)) {
|
|
77
|
+
return { device: devices[0] };
|
|
91
78
|
}
|
|
92
79
|
return { error: `There's nothing here called "${raw}".` };
|
|
93
80
|
}
|
|
94
81
|
async execute(args) {
|
|
95
82
|
const room = this.actor.currentLocation;
|
|
96
|
-
const
|
|
83
|
+
const all = room?.openableDevices() ?? [];
|
|
97
84
|
// THE ONLY HONEST "nothing here" (blocker 2026-08-27, "the barrier
|
|
98
85
|
// isn't allowing me to hack/pick/breach it"): the room holds no
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// every refusal goes through the gate.
|
|
107
|
-
if (allTargets.length === 0) {
|
|
108
|
-
return this.noPuzzleMessage;
|
|
86
|
+
// device at all. Filtering by KIND first made a wrong verb
|
|
87
|
+
// indistinguishable from an empty room, so a ward answered every
|
|
88
|
+
// physical verb by denying the door the player was staring at
|
|
89
|
+
// existed. Now the filter only picks a PREFERRED target and every
|
|
90
|
+
// refusal goes through canForce().
|
|
91
|
+
if (all.length === 0) {
|
|
92
|
+
return this.noDeviceMessage;
|
|
109
93
|
}
|
|
110
|
-
// WHO PICKS THE TARGET depends on whether the player named one.
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
// But "pick terminal" NAMES a thing, and the name settles which
|
|
117
|
-
// object is meant before the verb gets a say. Resolving that
|
|
118
|
-
// against same-type targets only was quietly wrong: resolveTarget's
|
|
119
|
-
// generic-noun fallback ("lock", "door", "terminal"...) fires
|
|
120
|
-
// whenever the candidate list has exactly one entry, so naming the
|
|
121
|
-
// terminal in a mixed room picked the MAGLOCK instead -- the right
|
|
122
|
-
// verb aimed at the wrong barrier, and it opened. Named targets
|
|
123
|
-
// therefore resolve against every barrier here; the gate below
|
|
124
|
-
// decides whether the verb suits the thing the player pointed at.
|
|
125
|
-
const mine = allTargets.filter(t => t.puzzle.bypassType === this.bypassType);
|
|
94
|
+
// WHO PICKS THE TARGET depends on whether the player named one. A
|
|
95
|
+
// bare "pick" in a room holding a maglock and a ward means the
|
|
96
|
+
// maglock. But "pick terminal" NAMES a thing, and the name settles
|
|
97
|
+
// which object is meant before the verb gets a say.
|
|
98
|
+
const mine = all.filter(d => d.kind === this.deviceKind);
|
|
126
99
|
const named = args.length > 0;
|
|
127
|
-
const primary = named ?
|
|
128
|
-
const {
|
|
129
|
-
let
|
|
130
|
-
if (!
|
|
131
|
-
|
|
132
|
-
// own barriers, which keeps the generic-noun convenience alive
|
|
133
|
-
// ("pick door" with one lock and one terminal still means the
|
|
134
|
-
// lock) without letting it hijack a name that DID match.
|
|
135
|
-
target = this.resolveTarget(args, mine).target;
|
|
100
|
+
const primary = named ? all : (mine.length > 0 ? mine : all);
|
|
101
|
+
const { device: preferred, error } = this.resolveTarget(args, primary);
|
|
102
|
+
let device = preferred;
|
|
103
|
+
if (!device && named && mine.length > 0) {
|
|
104
|
+
device = this.resolveTarget(args, mine).device;
|
|
136
105
|
}
|
|
137
|
-
if (!
|
|
138
|
-
return error ?? this.
|
|
106
|
+
if (!device) {
|
|
107
|
+
return error ?? this.noDeviceMessage;
|
|
139
108
|
}
|
|
140
|
-
// GATE BEFORE THE WALK
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
const gate = target.puzzle.checkBypassGate(this.bypassType);
|
|
109
|
+
// GATE BEFORE THE WALK: the gate is pure and synchronous, so a
|
|
110
|
+
// doomed attempt costs nothing at all -- and marching the player
|
|
111
|
+
// across the room only to tell them the verb was wrong is its own
|
|
112
|
+
// small insult.
|
|
113
|
+
const gate = device.canForce(this.verbName);
|
|
146
114
|
if (!gate.allowed) {
|
|
147
|
-
if (gate.reason === 'burned' && this.burnedFlavor)
|
|
148
|
-
return this.burnedFlavor;
|
|
149
115
|
return gate.message ?? `You can't do that here.`;
|
|
150
116
|
}
|
|
151
|
-
// "At" spots
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
117
|
+
// "At" spots: a device has a PLACE -- either its own atSpot, or the
|
|
118
|
+
// exit it holds. Out of combat you simply walk over; mid-exchange
|
|
119
|
+
// the crossing refuses.
|
|
120
|
+
// ATSPOT FIRST -- coordinator ruling 2026-08-27, reversing the
|
|
121
|
+
// original spec (`exitSpot(...) ?? atSpot`).
|
|
122
|
+
//
|
|
123
|
+
// atSpot is an AUTHOR'S EXPLICIT PLACEMENT; exitSpot is INFERRED.
|
|
124
|
+
// The migration only fills a device's atSpot from its exit's when
|
|
125
|
+
// the device had none of its own, so a device carrying BOTH means
|
|
126
|
+
// the seed said two different things -- and preferring the
|
|
127
|
+
// inference over the statement inverts the point of having the
|
|
128
|
+
// field. Generation starts emitting both once the generator moves
|
|
129
|
+
// to devices, so this is a decision about the future, not about
|
|
130
|
+
// the three seeds that exist today.
|
|
131
|
+
//
|
|
132
|
+
// It also keeps the refusal and the walk from contradicting each
|
|
133
|
+
// other: a locked door's refusal names WHERE THE MECHANISM IS, so
|
|
134
|
+
// walking the player somewhere else to touch it would say two
|
|
135
|
+
// things in one breath.
|
|
136
|
+
//
|
|
137
|
+
// NOT the pre-flip code's rule. bypass-command used to branch on
|
|
138
|
+
// door-ness (`door && direction ? exitSpot(...) : atSpot`); both
|
|
139
|
+
// call sites now ??-chain and agree with EACH OTHER, and with
|
|
140
|
+
// neither the old code nor the original spec. Do not "fix" one to
|
|
141
|
+
// match something older.
|
|
142
|
+
const where = device.atSpot
|
|
143
|
+
?? (device.opensExit ? exitSpot(room, device.opensExit) : undefined);
|
|
144
|
+
const reach = ensureAtSpot(this.actor, where);
|
|
159
145
|
if (reach.refusal)
|
|
160
146
|
return reach.refusal;
|
|
161
147
|
const crossNote = reach.line ? `${reach.line}\n` : '';
|
|
162
148
|
const succeeded = await this.determineOutcome();
|
|
163
|
-
const result =
|
|
149
|
+
const result = device.force(succeeded);
|
|
164
150
|
if (!result.success && this.failureFlavor) {
|
|
165
151
|
result.message = this.failureFlavor;
|
|
166
152
|
}
|
|
167
|
-
let
|
|
153
|
+
let consequence = [];
|
|
168
154
|
if (result.success) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
else if (target.puzzle.rewardItem) {
|
|
176
|
-
this.actor.inventory.addItem(target.puzzle.rewardItem);
|
|
177
|
-
this.scene.addWorldEvent(`${this.actor.name} bypassed "${target.puzzle.name}" (${this.bypassType}) in ${room.name}.`);
|
|
178
|
-
this.scene.updateInventory(this.scene.getPlayer().inventory);
|
|
179
|
-
}
|
|
180
|
-
// ROOM GRIDS (spatial wave 2026-08-25): the same terrain-clearing
|
|
181
|
-
// solve.ts does -- a bypass is just the risky route to the same
|
|
182
|
-
// outcome. A silent no-op for the overwhelming majority of
|
|
183
|
-
// puzzles that never synthesized blocking terrain.
|
|
184
|
-
const opened = room.clearTerrainFor(target.puzzle.name);
|
|
185
|
-
if (opened) {
|
|
186
|
-
successConsequence.push(`The ${opened} comes apart -- the way through is open.`);
|
|
187
|
-
this.scene.addWorldEvent(`${this.actor.name} cleared ${opened} in ${room.name} by bypassing "${target.puzzle.name}".`);
|
|
188
|
-
}
|
|
155
|
+
consequence = await this.onForceSuccess(device);
|
|
156
|
+
// ONE HOME FOR WHAT HAPPENS WHEN A DEVICE OPENS (see
|
|
157
|
+
// utilities/devices.ts). This and use.ts used to hand-roll the
|
|
158
|
+
// same four side effects and had already drifted apart.
|
|
159
|
+
consequence.push(...applyDeviceOpened(this.scene, this.actor, room, device, this.verbName));
|
|
189
160
|
}
|
|
190
161
|
if (result.alarmed) {
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
this.scene.addWorldEvent(`${this.actor.name}'s ${this.
|
|
195
|
-
|
|
196
|
-
this.game?.addHeat(2, `${this.bypassType} alarm on "${target.puzzle.name}"`);
|
|
162
|
+
// NPCs already listen for world events (scene.ts's
|
|
163
|
+
// getWorldEventsSummary()), so this alone lets them react without
|
|
164
|
+
// a dedicated alarm subsystem.
|
|
165
|
+
this.scene.addWorldEvent(`${this.actor.name}'s ${this.verbName} attempt on "${device.name}" in ${room.name} went loud.`);
|
|
166
|
+
this.game?.addHeat(2, `${this.verbName} alarm on "${device.name}"`);
|
|
197
167
|
}
|
|
198
|
-
this.logger.write(`${this.actor.name} attempted to ${this.
|
|
199
|
-
// The VERB, not the shared bypassType (2026-08-24 playtest: a
|
|
200
|
-
// BREACH was announced as "lockpicked" -- breach and pick share the
|
|
201
|
-
// 'lockpick' type but not the fiction).
|
|
168
|
+
this.logger.write(`${this.actor.name} attempted to ${this.verbName} "${device.name}": ${result.success ? 'success' : 'failure'}${result.alarmed ? ' (alarmed)' : ''}`);
|
|
202
169
|
this.actor.performAction(this.verbName, result.message);
|
|
203
|
-
// A failed bypass used to be instant, unrecoverable death by fiat --
|
|
204
|
-
// no dice, no damage, just endGame(). Replaced (after a real session
|
|
205
|
-
// died to it) with consequences that run through the systems that now
|
|
206
|
-
// exist: the route BURNS (puzzle.bypassBurned -- this trick never works
|
|
207
|
-
// on this barrier again; the findable answer still does), the alarm
|
|
208
|
-
// world event above puts every NPC on notice, any sneak is over, and
|
|
209
|
-
// the type-specific teeth in onBypassFailure() (spell backlash damage,
|
|
210
|
-
// see cast.ts) can still put you down -- but through the damage
|
|
211
|
-
// tracks, resisted and narrated, not by administrative decree.
|
|
212
170
|
if (result.alarmed) {
|
|
213
171
|
this.actor.sneaking = false;
|
|
214
|
-
const
|
|
172
|
+
const teeth = await this.onForceFailure();
|
|
215
173
|
this.scene.updateStatus();
|
|
216
174
|
return crossNote + [
|
|
217
175
|
result.message,
|
|
218
|
-
|
|
219
|
-
|
|
176
|
+
// NO BURN LINE any more: the route is not spent. Say what IS
|
|
177
|
+
// true -- it held, and the noise carried -- rather than
|
|
178
|
+
// promising a permanence that no longer exists.
|
|
179
|
+
` It holds. You can try again, but the noise has already carried.`,
|
|
180
|
+
...teeth,
|
|
220
181
|
].join('\n');
|
|
221
182
|
}
|
|
222
|
-
return crossNote + [result.message, ...
|
|
183
|
+
return crossNote + [result.message, ...consequence].join('\n');
|
|
223
184
|
}
|
|
224
185
|
}
|
|
225
186
|
//# sourceMappingURL=bypass-command.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bypass-command.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/bypass-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,cAAc,CAAC;AAGjD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"bypass-command.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/bypass-command.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BypassCommand } from './bypass-command.js';
|
|
2
|
-
import { PuzzleBypassType } from '../types/seed/puzzle-seed.js';
|
|
3
2
|
/**
|
|
4
3
|
* Gear-driven spellcasting (see Player's magic section): you know a spell
|
|
5
4
|
* if you carry its SpellFormula (or any Grimoire), and the best Focus on
|
|
@@ -11,9 +10,9 @@ import { PuzzleBypassType } from '../types/seed/puzzle-seed.js';
|
|
|
11
10
|
export declare class CastCommand extends BypassCommand {
|
|
12
11
|
protected static verb: string;
|
|
13
12
|
protected static description: string;
|
|
14
|
-
protected
|
|
13
|
+
protected deviceKind: "ward";
|
|
15
14
|
protected verbName: string;
|
|
16
|
-
protected
|
|
15
|
+
protected noDeviceMessage: string;
|
|
17
16
|
/** Backlash drain when a ward-bypass fails -- resisted with Willpower,
|
|
18
17
|
* like any drain. The nastiest drain in the game: forcing a hostile
|
|
19
18
|
* ward is harder on the caster than any spell of their own. */
|
|
@@ -34,7 +33,7 @@ export declare class CastCommand extends BypassCommand {
|
|
|
34
33
|
* knocks out or kills -- but through dice and the condition monitors,
|
|
35
34
|
* the same death any spell drain earns, not by fiat.
|
|
36
35
|
*/
|
|
37
|
-
protected
|
|
36
|
+
protected onForceFailure(): Promise<string[]>;
|
|
38
37
|
execute(args?: string[]): Promise<string>;
|
|
39
38
|
/**
|
|
40
39
|
* Lenient spell-name matching: the exact key, or a unique fragment of at
|
|
@@ -55,10 +54,10 @@ export declare class CastCommand extends BypassCommand {
|
|
|
55
54
|
*/
|
|
56
55
|
private resolveAllyTarget;
|
|
57
56
|
/**
|
|
58
|
-
* Does this string name a
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
57
|
+
* Does this string name a WARD in the room? Checked only AFTER actor
|
|
58
|
+
* matching fails, so a person's name always wins. Matches the exit it
|
|
59
|
+
* holds, its own name, a noun from its description, or -- with
|
|
60
|
+
* exactly one ward here -- a generic barrier word.
|
|
62
61
|
*/
|
|
63
62
|
private findSpellBarrier;
|
|
64
63
|
/** How the caster's pool reads in narration: focus-assisted, raw talent, or book-learned. */
|
|
@@ -136,9 +136,9 @@ const COMBAT_SPELL_FLAVOR = {
|
|
|
136
136
|
export class CastCommand extends BypassCommand {
|
|
137
137
|
static verb = 'cast';
|
|
138
138
|
static description = 'Cast a spell you know, at a FORCE you choose: "cast manabolt 8 at <target>" (default Force = your Magic; drain scales with it, and OVERCASTING past your talent makes the drain PHYSICAL). "spells" lists what you know. Bare "cast" tries to unweave a warded barrier.';
|
|
139
|
-
|
|
139
|
+
deviceKind = 'ward';
|
|
140
140
|
verbName = 'cast';
|
|
141
|
-
|
|
141
|
+
noDeviceMessage = "There's nothing here to cast at.";
|
|
142
142
|
/** Backlash drain when a ward-bypass fails -- resisted with Willpower,
|
|
143
143
|
* like any drain. The nastiest drain in the game: forcing a hostile
|
|
144
144
|
* ward is harder on the caster than any spell of their own. */
|
|
@@ -166,7 +166,7 @@ export class CastCommand extends BypassCommand {
|
|
|
166
166
|
* knocks out or kills -- but through dice and the condition monitors,
|
|
167
167
|
* the same death any spell drain earns, not by fiat.
|
|
168
168
|
*/
|
|
169
|
-
async
|
|
169
|
+
async onForceFailure() {
|
|
170
170
|
const actor = this.actor;
|
|
171
171
|
// Centering (metamagic, p.325): +grade dice on every drain resist --
|
|
172
172
|
// the chant that keeps the backlash outside the skull.
|
|
@@ -379,23 +379,23 @@ export class CastCommand extends BypassCommand {
|
|
|
379
379
|
return { target };
|
|
380
380
|
}
|
|
381
381
|
/**
|
|
382
|
-
* Does this string name a
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
382
|
+
* Does this string name a WARD in the room? Checked only AFTER actor
|
|
383
|
+
* matching fails, so a person's name always wins. Matches the exit it
|
|
384
|
+
* holds, its own name, a noun from its description, or -- with
|
|
385
|
+
* exactly one ward here -- a generic barrier word.
|
|
386
386
|
*/
|
|
387
387
|
findSpellBarrier(rawInput) {
|
|
388
388
|
const raw = rawInput.trim().toLowerCase();
|
|
389
389
|
const room = this.actor.currentLocation;
|
|
390
|
-
const targets = (room?.
|
|
390
|
+
const targets = (room?.openableDevices() ?? []).filter(d => d.kind === 'ward');
|
|
391
391
|
if (targets.length === 0)
|
|
392
392
|
return undefined;
|
|
393
393
|
if (this.isValidDirection(raw))
|
|
394
|
-
return targets.find(
|
|
394
|
+
return targets.find(d => d.opensExit === raw);
|
|
395
395
|
if (raw.length < 3)
|
|
396
396
|
return undefined;
|
|
397
|
-
const named = targets.filter(
|
|
398
|
-
||
|
|
397
|
+
const named = targets.filter(d => d.name.toLowerCase().includes(raw)
|
|
398
|
+
|| d.description.toLowerCase().includes(raw));
|
|
399
399
|
if (named.length === 1)
|
|
400
400
|
return named[0];
|
|
401
401
|
if (targets.length === 1 && /^(the )?(lock|door|ward|barrier|seal|gate|hatch|glyph|field|sigil|way)$/.test(raw)) {
|
|
@@ -510,12 +510,12 @@ export class CastCommand extends BypassCommand {
|
|
|
510
510
|
// manabolt at hatch", "at lock", "at down" and bounced on all
|
|
511
511
|
// three): when the words name no person but DO name a spell-keyed
|
|
512
512
|
// barrier here -- by direction, title, or a noun from its own
|
|
513
|
-
//
|
|
513
|
+
// description -- the cast routes to the ward test. The specific
|
|
514
514
|
// spell doesn't matter: "show us your spark" wants spellwork, and
|
|
515
515
|
// the bypass rolls the full casting pool.
|
|
516
516
|
const barrier = this.findSpellBarrier(targetName);
|
|
517
517
|
if (barrier) {
|
|
518
|
-
return super.execute([barrier.
|
|
518
|
+
return super.execute([barrier.opensExit ?? barrier.name]);
|
|
519
519
|
}
|
|
520
520
|
// Visible on another plane beats "no one" -- an astral caster CAN
|
|
521
521
|
// see the meat target they're aiming at; name the actual rule
|