@maka/maka-cli 5.3.17 → 5.5.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.d.ts +62 -0
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +196 -0
- package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/advance.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js +65 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/advance.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js +122 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/assense.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.d.ts +42 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js +243 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/attack.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +39 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/brandish.d.ts +21 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/brandish.js +82 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/brandish.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.d.ts +28 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +101 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.d.ts +19 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js +219 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/call.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +58 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +397 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/command-registry.d.ts +11 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/command-registry.js +8 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/command-registry.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/command.d.ts +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/command.js +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/command.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/drone.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js +72 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drop.js +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/drop.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/edge.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/edge.js +31 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/edge.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-call.d.ts +6 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-call.js +27 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/end-call.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.d.ts +4 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js +59 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/equip.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js +50 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/give.js +7 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/give.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +147 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.d.ts +25 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +199 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js +75 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/haggle.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js +43 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/inv.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jack.d.ts +17 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js +64 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/jack.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/kill.d.ts +10 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/kill.js +11 -33
- package/bundle/typescript/src/commands/game/sideQuest/commands/kill.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +47 -4
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +46 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pan.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pan.js +55 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pan.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.d.ts +18 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js +100 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/persuade.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js +33 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.d.ts +19 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.js +54 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/project.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/read.js +6 -5
- package/bundle/typescript/src/commands/game/sideQuest/commands/read.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js +52 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reboot.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reload.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reload.js +40 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/reload.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +59 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +31 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +71 -7
- package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +58 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/silent.d.ts +13 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js +50 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/silent.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js +35 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/sneak.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js +32 -11
- package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.d.ts +14 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js +86 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/spells.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/stance.d.ts +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js +41 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/stance.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/subdue.d.ts +21 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/subdue.js +43 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/subdue.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.d.ts +20 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js +45 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/surrender.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.d.ts +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +138 -2
- package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/talk.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/unequip.js +6 -3
- package/bundle/typescript/src/commands/game/sideQuest/commands/unequip.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.d.ts +24 -0
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +102 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/item-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/npc-factory.js +7 -2
- package/bundle/typescript/src/commands/game/sideQuest/factories/npc-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js +2 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/room-factory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +175 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +673 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.d.ts +8 -1
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +139 -21
- 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 +174 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +647 -0
- package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +138 -9
- package/bundle/typescript/src/commands/game/sideQuest/game.js +757 -197
- package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/minigames/hack-minigame.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/hack-minigame.js +101 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/hack-minigame.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/lockpick-minigame.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/lockpick-minigame.js +95 -0
- package/bundle/typescript/src/commands/game/sideQuest/minigames/lockpick-minigame.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/door.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/door.js +15 -2
- package/bundle/typescript/src/commands/game/sideQuest/models/door.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.d.ts +10 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.js +54 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/inventory.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/item.d.ts +41 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js +106 -3
- package/bundle/typescript/src/commands/game/sideQuest/models/item.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +27 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +312 -50
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +278 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js +661 -9
- package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +75 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +199 -6
- package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +24 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js +53 -18
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +73 -0
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +208 -3
- package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +319 -70
- package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +661 -227
- package/bundle/typescript/src/commands/game/sideQuest/types/player-equipment.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/exit-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/item-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/npc-seed.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/player-seed.d.ts +42 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.d.ts +15 -1
- 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 +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/win-condition-seed.d.ts +15 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/win-condition-seed.js +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/seed/win-condition-seed.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js +5 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.d.ts +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/shared/item-enum.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/ui.d.ts +3 -0
- package/bundle/typescript/src/commands/game/sideQuest/ui.js +95 -6
- package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.d.ts +60 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js +138 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/auto-fight.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.d.ts +145 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +354 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/comm-style.d.ts +2 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/comm-style.js +12 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/comm-style.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.d.ts +22 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js +34 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/dice.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.d.ts +36 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js +92 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.d.ts +77 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js +265 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/planes.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/pursuit.d.ts +16 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/pursuit.js +36 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/pursuit.js.map +1 -0
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +78 -7
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/commands/set/global.sub.cmd.js +59 -5
- package/bundle/typescript/src/commands/set/global.sub.cmd.js.map +1 -1
- package/bundle/typescript/src/tools/ai/ai.class.d.ts +42 -0
- package/bundle/typescript/src/tools/ai/ai.class.js +117 -0
- package/bundle/typescript/src/tools/ai/ai.class.js.map +1 -0
- package/bundle/typescript/src/tools/fsi/fsi.interfaces.d.ts +1 -0
- package/bundle/typescript/src/tools/fsi/fsi.interfaces.js.map +1 -1
- package/bundle/typescript/src/tools/https/https.class.d.ts +3 -2
- package/bundle/typescript/src/tools/https/https.class.js +51 -2
- package/bundle/typescript/src/tools/https/https.class.js.map +1 -1
- package/bundle/typescript/src/tools/https/https.interface.d.ts +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { BypassCommand } from './bypass-command.js';
|
|
2
|
+
import { rollPool, formatRoll } from '../utilities/dice.js';
|
|
3
|
+
import { Door } from '../models/door.js';
|
|
4
|
+
import { leaveMatrix, canReachHost } from '../utilities/planes.js';
|
|
5
|
+
import { fuzzyPickName } from '../utilities/fuzzy-match.js';
|
|
6
|
+
export class HackCommand extends BypassCommand {
|
|
7
|
+
static verb = 'hack';
|
|
8
|
+
static description = 'Hack a terminal or system (failed terminal hacks are fatal). From inside the Matrix, "hack" cracks a host node: the room\'s maglocks unlock for real. "hack <name>" cracks someone\'s PAN (needs a cyberdeck or being jacked in): jams their smartgun or kills their AR; against a silent target it forces their link loud first.';
|
|
9
|
+
bypassType = 'hack';
|
|
10
|
+
noPuzzleMessage = "There's nothing here to hack.";
|
|
11
|
+
async execute(args = []) {
|
|
12
|
+
// PAN warfare: "hack <name>" cracks a meat actor's personal area
|
|
13
|
+
// network -- from inside the Matrix, or right here in AR with a
|
|
14
|
+
// working deck in hand (which is also how enemy deckers get YOU).
|
|
15
|
+
// Tried first so a name never falls through to the barrier-bypass
|
|
16
|
+
// path; misses fall through untouched.
|
|
17
|
+
if (args && args.length > 0) {
|
|
18
|
+
const panResult = await this.tryPanHack(args);
|
|
19
|
+
if (panResult)
|
|
20
|
+
return panResult;
|
|
21
|
+
}
|
|
22
|
+
// The Matrix branch: hacking FROM INSIDE a host node reaches the
|
|
23
|
+
// room's physical systems -- the meatspace payoff of jacking in. A
|
|
24
|
+
// failed crack means the host bites back (Matrix damage, routed by
|
|
25
|
+
// sim mode like ice hits), not the meat-world instant-fatal of
|
|
26
|
+
// hardwiring into a puzzle terminal.
|
|
27
|
+
if (this.actor.plane === 'matrix') {
|
|
28
|
+
const room = this.actor.currentLocation;
|
|
29
|
+
if (!room.hasNode) {
|
|
30
|
+
return `Thin grid out here -- no host node to crack. Find a room whose systems actually live on the Matrix.`;
|
|
31
|
+
}
|
|
32
|
+
// An air-gapped host doesn't answer personas that rode in over the
|
|
33
|
+
// wireless grid -- it only opens to a hard line, i.e. a body jacked
|
|
34
|
+
// in INSIDE this room (see planes.canReachHost). This is what makes
|
|
35
|
+
// physical infiltration matter to a datarun.
|
|
36
|
+
if (!canReachHost(this.actor, room)) {
|
|
37
|
+
return `The host here is AIR-GAPPED -- dark to the wireless grid, no matter how close your persona drifts. It only opens to a hard line: get your body into ${room.name} and "jack in" from there.`;
|
|
38
|
+
}
|
|
39
|
+
if (room.hostCracked) {
|
|
40
|
+
return `This host is already cracked open -- its security hangs in shreds. Whatever it guarded is yours to take.`;
|
|
41
|
+
}
|
|
42
|
+
const lockedDoors = Array.from(room.exits.values())
|
|
43
|
+
.filter((exit) => exit instanceof Door && exit.checkIfLocked());
|
|
44
|
+
this.actor.performAction('hacks the host', room.name);
|
|
45
|
+
const success = await this.determineOutcome();
|
|
46
|
+
if (!success) {
|
|
47
|
+
const bite = rollPool(this.actor.getSoakPool());
|
|
48
|
+
const dealt = Math.max(0, 3 - bite.hits);
|
|
49
|
+
const lines = [`The host's defenses snap shut around your intrusion --`, ` Firewall: ${formatRoll(bite)}`];
|
|
50
|
+
if (dealt > 0) {
|
|
51
|
+
// Same routing as ice hits: hardware eats it when there's
|
|
52
|
+
// hardware; a technomancer's living persona (and any hot-sim
|
|
53
|
+
// rider) takes it in the meat of the mind.
|
|
54
|
+
if (this.actor.simMode === 'hot' || this.actor.isTechnomancer()) {
|
|
55
|
+
this.actor.takeStun(dealt);
|
|
56
|
+
lines.push(` Feedback burns -- ${dealt} stun (biofeedback): ${this.actor.stunSummary()}`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.actor.takeMatrixDamage(dealt);
|
|
60
|
+
lines.push(` Your deck's boards sizzle -- ${dealt} box${dealt === 1 ? '' : 'es'}: ${this.actor.matrixSummary()}`);
|
|
61
|
+
if (this.actor.matrixCrashed) {
|
|
62
|
+
lines.push(` The deck BRICKS under the counterstrike --`);
|
|
63
|
+
lines.push(...leaveMatrix(this.scene, this.actor, { forced: true, reason: `The host burns your deck out from under you.` }));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
lines.push(` You shed the counterstrike and back off clean.`);
|
|
69
|
+
}
|
|
70
|
+
this.scene.updateStatus();
|
|
71
|
+
return lines.join('\n');
|
|
72
|
+
}
|
|
73
|
+
// Cracking the host buys everything it guards at once: the room's
|
|
74
|
+
// physical maglocks AND its data vault (matrix items were sealed
|
|
75
|
+
// until now -- see take.ts). The host is the vault, not the door.
|
|
76
|
+
room.hostCracked = true;
|
|
77
|
+
const vaultedFiles = room.inventory.getAllItems().filter(i => i.plane === 'matrix');
|
|
78
|
+
const lines = [`You're in. The host's sculpted security folds back layer by layer --`];
|
|
79
|
+
if (lockedDoors.length > 0) {
|
|
80
|
+
for (const door of lockedDoors) {
|
|
81
|
+
door.isLocked = false;
|
|
82
|
+
}
|
|
83
|
+
lines.push(` ${lockedDoors.length} maglock${lockedDoors.length === 1 ? '' : 's'} release${lockedDoors.length === 1 ? 's' : ''} in the MEAT world. The way is open, out there.`);
|
|
84
|
+
}
|
|
85
|
+
if (vaultedFiles.length > 0) {
|
|
86
|
+
lines.push(` {cyan-fg}The data vault unseals -- ${vaultedFiles.map(i => i.name).join(', ')} ${vaultedFiles.length === 1 ? 'is' : 'are'} yours to "take".{/cyan-fg}`);
|
|
87
|
+
}
|
|
88
|
+
if (lockedDoors.length === 0 && vaultedFiles.length === 0) {
|
|
89
|
+
lines.push(` Nothing here is locked down, but the node's systems are yours to read.`);
|
|
90
|
+
}
|
|
91
|
+
this.scene.addWorldEvent(`${this.actor.name} hacked the ${room.name} host -- its security is down.`);
|
|
92
|
+
this.logger.write(`${this.actor.name} hacked ${room.name}'s node: ${lockedDoors.length} door(s) unlocked, ${vaultedFiles.length} file(s) unsealed.`);
|
|
93
|
+
return lines.join('\n');
|
|
94
|
+
}
|
|
95
|
+
return super.execute(args);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The PAN duel: opposed roll, attacker's Logic + deck quality (a
|
|
99
|
+
* technomancer uses half resonance instead of a deck) vs the target's
|
|
100
|
+
* Logic + master-device firewall -- slaving, simplified -- with +2 to
|
|
101
|
+
* the defense while running silent (a hidden PAN must be FOUND).
|
|
102
|
+
*
|
|
103
|
+
* Effects, in priority order on a LOUD target: jam the equipped smart
|
|
104
|
+
* firearm; else cut the AR overlay (initiative bonus gone, calls
|
|
105
|
+
* dropped). Against a SILENT target, a win only forces the PAN loud --
|
|
106
|
+
* the follow-up crack is its own attempt. "reboot" clears everything.
|
|
107
|
+
*
|
|
108
|
+
* Returns null when the args don't name a hackable actor here, so
|
|
109
|
+
* barrier names and directions fall through to the puzzle path.
|
|
110
|
+
*/
|
|
111
|
+
async tryPanHack(args) {
|
|
112
|
+
const actor = this.actor;
|
|
113
|
+
const room = actor.currentLocation;
|
|
114
|
+
const candidates = this.scene.getActorsInRoom(room)
|
|
115
|
+
.filter(a => a !== actor && a.plane === 'meat' && !a.isIncapacitated());
|
|
116
|
+
const picked = fuzzyPickName(args.join(' '), candidates.map(a => a.name));
|
|
117
|
+
const target = picked ? candidates.find(a => a.name === picked) : undefined;
|
|
118
|
+
if (!target)
|
|
119
|
+
return null;
|
|
120
|
+
// Reach: riding the Matrix, a living persona, or AR with a working
|
|
121
|
+
// deck in hand. A bare commlink can't crack anything.
|
|
122
|
+
const arDeck = actor.getCyberdeck();
|
|
123
|
+
if (actor.plane !== 'matrix' && !actor.isTechnomancer() && !arDeck) {
|
|
124
|
+
return `You'd need to be jacked in, or have a working cyberdeck in hand, to crack ${target.name}'s PAN.`;
|
|
125
|
+
}
|
|
126
|
+
if (actor.plane === 'astral') {
|
|
127
|
+
return `The Matrix doesn't answer to spirits -- no PAN hacking from the astral.`;
|
|
128
|
+
}
|
|
129
|
+
const master = target.getPanMaster();
|
|
130
|
+
if (!master) {
|
|
131
|
+
return `${target.name} runs no devices at all -- nothing to crack. Respect, almost.`;
|
|
132
|
+
}
|
|
133
|
+
const atkQuality = actor.isTechnomancer()
|
|
134
|
+
? Math.floor(actor.resonance / 2)
|
|
135
|
+
: (actor.activeDeck ?? arDeck)?.qualityBonus ?? 0;
|
|
136
|
+
// Logic + HACKING + gear: the skill joined the duel with the rest of
|
|
137
|
+
// the skill system -- a trained decker now out-fences a smart amateur
|
|
138
|
+
// holding the same deck.
|
|
139
|
+
const attack = rollPool(Math.max(0, actor.logic + actor.skillRating('hacking') + atkQuality + actor.woundModifier));
|
|
140
|
+
const defense = rollPool(target.getPanDefensePool());
|
|
141
|
+
actor.performAction('runs a PAN intrusion', target.name);
|
|
142
|
+
this.logger.write(`${actor.name} PAN-hacks ${target.name}: ${attack.hits} vs ${defense.hits} (silent=${target.runningSilent}).`);
|
|
143
|
+
const lines = [
|
|
144
|
+
`You reach into ${target.name}'s PAN${target.runningSilent ? ' -- or where it SHOULD be; they\'re running silent' : ''}:`,
|
|
145
|
+
` Intrusion: ${formatRoll(attack)}`,
|
|
146
|
+
` Firewall: ${formatRoll(defense)}`,
|
|
147
|
+
];
|
|
148
|
+
if (attack.hits <= defense.hits) {
|
|
149
|
+
lines.push(` The master ${master.name} slams the door -- and ${target.name}'s link just pinged them a probe warning.`);
|
|
150
|
+
target.performAction('notices a Matrix probe', 'their link flashing an intrusion warning');
|
|
151
|
+
return lines.join('\n');
|
|
152
|
+
}
|
|
153
|
+
if (target.runningSilent) {
|
|
154
|
+
target.runningSilent = false;
|
|
155
|
+
lines.push(` FOUND. You wrench the hidden PAN into the open -- ${target.name}'s link screams back to life, broadcasting for everyone.`);
|
|
156
|
+
target.performAction('is forced loud', 'their silent commlink lighting up against their will');
|
|
157
|
+
this.scene.addWorldEvent(`${actor.name} forced ${target.name}'s PAN loud.`);
|
|
158
|
+
this.scene.updateStatus();
|
|
159
|
+
return lines.join('\n');
|
|
160
|
+
}
|
|
161
|
+
const gun = target.equipment.rightHand?.weapon ?? target.equipment.leftHand?.weapon;
|
|
162
|
+
if (gun?.isFirearm() && !gun.jammed) {
|
|
163
|
+
gun.jammed = true;
|
|
164
|
+
lines.push(` IN. The ${gun.name}'s smartlink seizes mid-handshake -- LOCKED. ${target.name} is down to fists and harsh language until they reboot.`);
|
|
165
|
+
target.performAction('suffers a PAN crack', `their ${gun.name} smartlink locking dead`);
|
|
166
|
+
this.scene.addWorldEvent(`${actor.name} jammed ${target.name}'s ${gun.name} through their PAN.`);
|
|
167
|
+
}
|
|
168
|
+
else if (!target.arOffline && target.equipment.head?.commlink) {
|
|
169
|
+
target.arOffline = true;
|
|
170
|
+
const partner = target.activeCallPartner;
|
|
171
|
+
if (partner) {
|
|
172
|
+
target.activeCallPartner = undefined;
|
|
173
|
+
partner.activeCallPartner = undefined;
|
|
174
|
+
lines.push(` Their call drops mid-word.`);
|
|
175
|
+
}
|
|
176
|
+
lines.push(` IN. You cut ${target.name}'s AR overlay dead -- no tactical feed, no calls, until they reboot.`);
|
|
177
|
+
target.performAction('suffers a PAN crack', 'their AR overlay dying around them');
|
|
178
|
+
this.scene.addWorldEvent(`${actor.name} cut ${target.name}'s AR through their PAN.`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
lines.push(` IN -- but there's nothing left to crack: their gear is already dark or dead.`);
|
|
182
|
+
}
|
|
183
|
+
this.scene.updateStatus();
|
|
184
|
+
return lines.join('\n');
|
|
185
|
+
}
|
|
186
|
+
async determineOutcome() {
|
|
187
|
+
// A HACKING test (Logic + hacking, 2 hits) -- the skill replaced the
|
|
188
|
+
// old flat Logic + 3 and the minigame both; hacking falls back to
|
|
189
|
+
// combatSkill for skill-less NPCs (see Player.skillRating), so an old
|
|
190
|
+
// seed's enforcer still fumbles terminals about like it used to.
|
|
191
|
+
const pool = Math.max(1, this.actor.logic + this.actor.skillRating('hacking') + this.actor.woundModifier - this.actor.sustainingPenalty);
|
|
192
|
+
const roll = rollPool(pool);
|
|
193
|
+
if (this.actor === this.scene.getPlayer()) {
|
|
194
|
+
this.logger.log(`Hacking: ${formatRoll(roll)} (2+ to crack)`);
|
|
195
|
+
}
|
|
196
|
+
return roll.hits >= 2;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=hack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hack.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/hack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command, ICommand } from './command.js';
|
|
2
|
+
/**
|
|
3
|
+
* Negotiation's mechanic: haggle the job's payout, ONCE per job, over a
|
|
4
|
+
* live call with whoever's paying (the fixer, or the win condition's
|
|
5
|
+
* recipient). Opposed Charisma + negotiation. Each net hit is +5% on the
|
|
6
|
+
* agreed figure (capped +25%); losing means the offer stands; a critical
|
|
7
|
+
* glitch INSULTS them -- 10% off. The engine pays whatever number wins
|
|
8
|
+
* here, and every NPC quotes it (their prompt reads the live payout), so
|
|
9
|
+
* the negotiation is mechanically real, not flavor.
|
|
10
|
+
*/
|
|
11
|
+
export declare class HaggleCommand extends Command implements ICommand {
|
|
12
|
+
protected static verb: string;
|
|
13
|
+
protected static description: string;
|
|
14
|
+
execute(_args?: string[]): Promise<string>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Command } from './command.js';
|
|
2
|
+
import { rollPool, formatRoll } from '../utilities/dice.js';
|
|
3
|
+
import { CALL_ICON, CALL_COLOR } from '../utilities/comm-style.js';
|
|
4
|
+
/**
|
|
5
|
+
* Negotiation's mechanic: haggle the job's payout, ONCE per job, over a
|
|
6
|
+
* live call with whoever's paying (the fixer, or the win condition's
|
|
7
|
+
* recipient). Opposed Charisma + negotiation. Each net hit is +5% on the
|
|
8
|
+
* agreed figure (capped +25%); losing means the offer stands; a critical
|
|
9
|
+
* glitch INSULTS them -- 10% off. The engine pays whatever number wins
|
|
10
|
+
* here, and every NPC quotes it (their prompt reads the live payout), so
|
|
11
|
+
* the negotiation is mechanically real, not flavor.
|
|
12
|
+
*/
|
|
13
|
+
export class HaggleCommand extends Command {
|
|
14
|
+
static verb = 'haggle';
|
|
15
|
+
static description = 'Negotiate the job\'s payout over a live call -- opposed Charisma + negotiation, once per job. A bad enough roll worsens the offer.';
|
|
16
|
+
async execute(_args = []) {
|
|
17
|
+
const actor = this.actor;
|
|
18
|
+
const partner = actor.activeCallPartner;
|
|
19
|
+
// A PENDING offer (hub flow: Johnson pitched, nothing accepted yet)
|
|
20
|
+
// is the prime negotiating moment -- haggling mutates the pending
|
|
21
|
+
// seed's payout, which flows into the run when "travel" installs it.
|
|
22
|
+
// Otherwise the live scene's win condition is the table.
|
|
23
|
+
const pending = this.game?.pendingRun;
|
|
24
|
+
const wc = pending?.winCondition ?? this.scene.getWinCondition();
|
|
25
|
+
if (!wc || typeof wc.payout !== 'number' || wc.payout <= 0) {
|
|
26
|
+
return `There's no pay on the table to argue about.`;
|
|
27
|
+
}
|
|
28
|
+
if (!pending && this.scene.isCompleted()) {
|
|
29
|
+
return `The job's done and the cred's transferred -- that ship has sailed.`;
|
|
30
|
+
}
|
|
31
|
+
if (!partner) {
|
|
32
|
+
return `Haggling happens on a live call -- "call" whoever's paying first.`;
|
|
33
|
+
}
|
|
34
|
+
if (actor.haggledThisJob) {
|
|
35
|
+
return `You already pushed on the number once this job. Push again and you're negotiating yourself out of work.`;
|
|
36
|
+
}
|
|
37
|
+
actor.haggledThisJob = true;
|
|
38
|
+
const negotiation = actor.skillRating('negotiation');
|
|
39
|
+
const pool = Math.max(1, actor.charisma + (negotiation > 0 ? negotiation : -1) + actor.woundModifier - actor.sustainingPenalty);
|
|
40
|
+
const theirs = Math.max(1, partner.charisma + partner.skillRating('negotiation'));
|
|
41
|
+
const mine = rollPool(pool);
|
|
42
|
+
const resistance = rollPool(theirs);
|
|
43
|
+
actor.performAction('haggles over the pay with', partner.name);
|
|
44
|
+
this.logger.write(`${actor.name} haggles with ${partner.name}: ${mine.hits} vs ${resistance.hits} on ${wc.payout}.`);
|
|
45
|
+
const lines = [
|
|
46
|
+
`You lean on the number:`,
|
|
47
|
+
` Negotiation: ${formatRoll(mine)}`,
|
|
48
|
+
` ${partner.name}: ${formatRoll(resistance)}`,
|
|
49
|
+
];
|
|
50
|
+
const oldPayout = wc.payout;
|
|
51
|
+
const net = mine.hits - resistance.hits;
|
|
52
|
+
let note;
|
|
53
|
+
if (mine.criticalGlitch) {
|
|
54
|
+
wc.payout = Math.max(500, Math.round(oldPayout * 0.9 / 100) * 100);
|
|
55
|
+
note = `You overplay it badly -- ${partner.name} goes cold. The offer DROPS: ${oldPayout} -> ${wc.payout} nuyen.`;
|
|
56
|
+
}
|
|
57
|
+
else if (net <= 0) {
|
|
58
|
+
note = `${partner.name} doesn't blink. The number stands: ${wc.payout} nuyen.`;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const bump = Math.min(0.25, net * 0.05);
|
|
62
|
+
wc.payout = Math.round(oldPayout * (1 + bump) / 100) * 100;
|
|
63
|
+
note = `${partner.name} sighs -- and the number moves: ${oldPayout} -> ${wc.payout} nuyen.`;
|
|
64
|
+
}
|
|
65
|
+
lines.push(` ${note}`);
|
|
66
|
+
// The partner's AI hears the renegotiation as part of the call, with
|
|
67
|
+
// the binding figure spelled out -- their payoutSummary reads the live
|
|
68
|
+
// win condition, so from here on they QUOTE the new number too.
|
|
69
|
+
void Promise.resolve(partner.hear(actor, `The rate needs another look. [System note: after negotiation, the agreed payout is now EXACTLY ${wc.payout} nuyen -- this figure is binding.]`)).catch(err => {
|
|
70
|
+
this.logger.error(`${partner.name} failed to hear the haggle: ${err}`);
|
|
71
|
+
});
|
|
72
|
+
return `{${CALL_COLOR}-fg}${CALL_ICON} ${lines.join('\n')}{/${CALL_COLOR}-fg}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=haggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"haggle.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/haggle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command, ICommand } from './command.js';
|
|
2
|
+
/**
|
|
3
|
+
* On-demand inventory panel, replacing the always-on blessed sidebar box
|
|
4
|
+
* (same freeing-up-the-log treatment as equipment.ts and map.ts -- the
|
|
5
|
+
* spells command set the pattern). Registered as both "inv" and
|
|
6
|
+
* "inventory".
|
|
7
|
+
*/
|
|
8
|
+
export declare class InventoryCommand extends Command implements ICommand {
|
|
9
|
+
protected static verb: string;
|
|
10
|
+
protected static description: string;
|
|
11
|
+
execute(_args?: string[]): Promise<string>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Command } from './command.js';
|
|
2
|
+
import { capitalCase } from 'change-case';
|
|
3
|
+
import { planeTintItemName } from '../utilities/planes.js';
|
|
4
|
+
/**
|
|
5
|
+
* On-demand inventory panel, replacing the always-on blessed sidebar box
|
|
6
|
+
* (same freeing-up-the-log treatment as equipment.ts and map.ts -- the
|
|
7
|
+
* spells command set the pattern). Registered as both "inv" and
|
|
8
|
+
* "inventory".
|
|
9
|
+
*/
|
|
10
|
+
export class InventoryCommand extends Command {
|
|
11
|
+
static verb = 'inv';
|
|
12
|
+
static description = 'Your pack: everything you carry, the weight of it, and your nuyen.';
|
|
13
|
+
async execute(_args = []) {
|
|
14
|
+
const actor = this.actor;
|
|
15
|
+
const items = actor.inventory.getAllItems();
|
|
16
|
+
const currencyName = capitalCase(this.scene.getCurrencyType());
|
|
17
|
+
const lines = ['=== INVENTORY ==='];
|
|
18
|
+
let currencyTotal = 0;
|
|
19
|
+
const itemLines = [];
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
// Carried data keeps its plane tint (cyan [data]) so the pack view
|
|
22
|
+
// matches how search showed it. 'meat' as viewer: the astral
|
|
23
|
+
// annotation is for room floors, not your own pack.
|
|
24
|
+
let line = ` • ${planeTintItemName(item, 'meat')} -- ${item.rating} ${item.category}`;
|
|
25
|
+
if (item.isCurrencyCarrier?.() && item.currencyAmount > 0) {
|
|
26
|
+
line += ` [${item.currencyAmount} ${currencyName.toLowerCase()}]`;
|
|
27
|
+
currencyTotal += item.currencyAmount;
|
|
28
|
+
}
|
|
29
|
+
itemLines.push(line);
|
|
30
|
+
}
|
|
31
|
+
lines.push(`Total ${currencyName}: ${currencyTotal}`);
|
|
32
|
+
lines.push(`Carrying: ${actor.currentWeight.toFixed(1)} / ${actor.maxCarryingWeight} kg (equipped gear included)`);
|
|
33
|
+
lines.push('');
|
|
34
|
+
if (itemLines.length === 0) {
|
|
35
|
+
lines.push(`Your pack is empty. Whatever you're wearing or holding is under "equipment".`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
lines.push(...itemLines);
|
|
39
|
+
}
|
|
40
|
+
return lines.join('\n');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=inv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inv.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/inv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,OAAO,gBAAiB,SAAQ,OAAO;IACjC,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC;IACpB,MAAM,CAAC,WAAW,GAAG,oEAAoE,CAAC;IAEpG,KAAK,CAAC,OAAO,CAAC,QAAkB,EAAE;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;QAE/D,MAAM,KAAK,GAAa,CAAC,mBAAmB,CAAC,CAAC;QAE9C,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,mEAAmE;YACnE,6DAA6D;YAC7D,oDAAoD;YACpD,IAAI,IAAI,GAAG,OAAO,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvF,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,IAAI,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;gBAC1D,IAAI,IAAI,KAAK,IAAI,CAAC,cAAc,IAAI,YAAY,CAAC,WAAW,EAAE,GAAG,CAAC;gBAClE,aAAa,IAAI,IAAI,CAAC,cAAc,CAAC;YACvC,CAAC;YACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,SAAS,YAAY,KAAK,aAAa,EAAE,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,iBAAiB,8BAA8B,CAAC,CAAC;QACnH,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;QAC7F,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command, ICommand } from './command.js';
|
|
2
|
+
/**
|
|
3
|
+
* The Matrix door, both directions: "jack in [hot|cold]" and "jack out".
|
|
4
|
+
* Needs a working CYBERDECK carried (a commlink is calls and AR only) --
|
|
5
|
+
* unless you're a technomancer, whose brain is the deck. Matrix combat
|
|
6
|
+
* damage lands on the deck's hardware track (bricked = forced dump, rest
|
|
7
|
+
* to repair); hot-sim adds real stun biofeedback on top and dumps out as
|
|
8
|
+
* PHYSICAL. A technomancer skips the hardware entirely and takes every
|
|
9
|
+
* box as real stun. Your body slumps where you jacked in -- pick the room
|
|
10
|
+
* like your life depends on it, because it does (see planes.ts
|
|
11
|
+
* resolveBodyAttack).
|
|
12
|
+
*/
|
|
13
|
+
export declare class JackCommand extends Command implements ICommand {
|
|
14
|
+
protected static verb: string;
|
|
15
|
+
protected static description: string;
|
|
16
|
+
execute(args?: string[]): Promise<string>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Command } from './command.js';
|
|
2
|
+
import { enterMatrix, leaveMatrix } from '../utilities/planes.js';
|
|
3
|
+
/**
|
|
4
|
+
* The Matrix door, both directions: "jack in [hot|cold]" and "jack out".
|
|
5
|
+
* Needs a working CYBERDECK carried (a commlink is calls and AR only) --
|
|
6
|
+
* unless you're a technomancer, whose brain is the deck. Matrix combat
|
|
7
|
+
* damage lands on the deck's hardware track (bricked = forced dump, rest
|
|
8
|
+
* to repair); hot-sim adds real stun biofeedback on top and dumps out as
|
|
9
|
+
* PHYSICAL. A technomancer skips the hardware entirely and takes every
|
|
10
|
+
* box as real stun. Your body slumps where you jacked in -- pick the room
|
|
11
|
+
* like your life depends on it, because it does (see planes.ts
|
|
12
|
+
* resolveBodyAttack).
|
|
13
|
+
*/
|
|
14
|
+
export class JackCommand extends Command {
|
|
15
|
+
static verb = 'jack';
|
|
16
|
+
static description = 'Enter or leave the Matrix: "jack in" (cold-sim), "jack in hot" (faster, harder, biofeedback is real), "jack out". Takes a working cyberdeck -- or being a technomancer. Your body slumps where you leave it.';
|
|
17
|
+
async execute(args = []) {
|
|
18
|
+
const actor = this.actor;
|
|
19
|
+
const sub = args[0]?.toLowerCase();
|
|
20
|
+
if (sub === 'out') {
|
|
21
|
+
if (actor.plane !== 'matrix') {
|
|
22
|
+
return `You're not jacked in.`;
|
|
23
|
+
}
|
|
24
|
+
if (actor.inExchange) {
|
|
25
|
+
return `The ice has your persona locked in the exchange -- survive it first, then jack out in the lull.`;
|
|
26
|
+
}
|
|
27
|
+
const lines = leaveMatrix(this.scene, actor);
|
|
28
|
+
return lines.join('\n');
|
|
29
|
+
}
|
|
30
|
+
if (sub === 'in' || sub === undefined || sub === 'hot' || sub === 'cold') {
|
|
31
|
+
if (actor.plane === 'matrix') {
|
|
32
|
+
return `You're already in. "jack out" to leave.`;
|
|
33
|
+
}
|
|
34
|
+
if (actor.plane === 'astral') {
|
|
35
|
+
return `You're projecting -- an astral form can't touch a deck. "return" to your body first.`;
|
|
36
|
+
}
|
|
37
|
+
if (actor.isIncapacitated()) {
|
|
38
|
+
return `You're down. Nobody jacks in from the floor.`;
|
|
39
|
+
}
|
|
40
|
+
// A technomancer IS the hardware: no deck, no commlink, no
|
|
41
|
+
// broadcast to silence -- their brain touches the Matrix bare. The
|
|
42
|
+
// price is paid on the other side: every point of Matrix damage
|
|
43
|
+
// lands on their REAL stun track (see combat-exchange routing).
|
|
44
|
+
if (!actor.isTechnomancer()) {
|
|
45
|
+
const deck = actor.getCyberdeck();
|
|
46
|
+
if (!deck) {
|
|
47
|
+
const bricked = actor.getAnyCyberdeck();
|
|
48
|
+
return bricked
|
|
49
|
+
? `Your ${bricked.name} is BRICKED (${bricked.deckSummary()}) -- dead boards don't jack in. Rest somewhere safe to repair it.`
|
|
50
|
+
: `No cyberdeck, no Matrix. A commlink gets you calls and AR -- riding the grid takes a real deck.`;
|
|
51
|
+
}
|
|
52
|
+
if (actor.runningSilent) {
|
|
53
|
+
return `You're running silent -- no broadcast, no Matrix. Go loud first ("silent").`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const mode = (sub === 'hot' || args[1]?.toLowerCase() === 'hot') ? 'hot' : 'cold';
|
|
57
|
+
const lines = enterMatrix(this.scene, actor, mode);
|
|
58
|
+
this.scene.updateStatus();
|
|
59
|
+
return lines.join('\n');
|
|
60
|
+
}
|
|
61
|
+
return `Try "jack in", "jack in hot", or "jack out".`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=jack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jack.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/jack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAElE;;;;;;;;;;GAUG;AACH,MAAM,OAAO,WAAY,SAAQ,OAAO;IAC5B,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC;IACrB,MAAM,CAAC,WAAW,GAAG,8MAA8M,CAAC;IAE9O,KAAK,CAAC,OAAO,CAAC,OAAiB,EAAE;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QAEnC,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC7B,OAAO,uBAAuB,CAAC;YACjC,CAAC;YACD,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBACrB,OAAO,iGAAiG,CAAC;YAC3G,CAAC;YACD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAED,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACzE,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC7B,OAAO,yCAAyC,CAAC;YACnD,CAAC;YACD,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC7B,OAAO,sFAAsF,CAAC;YAChG,CAAC;YACD,IAAI,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC5B,OAAO,8CAA8C,CAAC;YACxD,CAAC;YAED,2DAA2D;YAC3D,mEAAmE;YACnE,gEAAgE;YAChE,gEAAgE;YAChE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;gBAClC,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,EAAE,CAAC;oBACxC,OAAO,OAAO;wBACZ,CAAC,CAAC,QAAQ,OAAO,CAAC,IAAI,gBAAgB,OAAO,CAAC,WAAW,EAAE,mEAAmE;wBAC9H,CAAC,CAAC,iGAAiG,CAAC;gBACxG,CAAC;gBACD,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;oBACxB,OAAO,6EAA6E,CAAC;gBACvF,CAAC;YACH,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YAClF,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;YACnD,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAED,OAAO,8CAA8C,CAAC;IACxD,CAAC"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { AttackCommand } from './attack.js';
|
|
2
|
+
/**
|
|
3
|
+
* "kill" used to be an instant, unconditional delete-from-scene -- no roll,
|
|
4
|
+
* no counterplay, which also meant a hostile NPC issuing "kill <player>"
|
|
5
|
+
* (e.g. a "will kill on sight" description) ended the game on the spot with
|
|
6
|
+
* nothing the player could do about it. Now it's just an alias for the real
|
|
7
|
+
* dice-pool combat in attack.ts: same verb NPC prompts already know, actual
|
|
8
|
+
* fight underneath.
|
|
9
|
+
*/
|
|
10
|
+
export declare class KillCommand extends AttackCommand {
|
|
3
11
|
protected static verb: string;
|
|
4
12
|
protected static description: string;
|
|
5
|
-
execute(args?: string[]): Promise<string>;
|
|
6
13
|
}
|
|
@@ -1,36 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { AttackCommand } from './attack.js';
|
|
2
|
+
/**
|
|
3
|
+
* "kill" used to be an instant, unconditional delete-from-scene -- no roll,
|
|
4
|
+
* no counterplay, which also meant a hostile NPC issuing "kill <player>"
|
|
5
|
+
* (e.g. a "will kill on sight" description) ended the game on the spot with
|
|
6
|
+
* nothing the player could do about it. Now it's just an alias for the real
|
|
7
|
+
* dice-pool combat in attack.ts: same verb NPC prompts already know, actual
|
|
8
|
+
* fight underneath.
|
|
9
|
+
*/
|
|
10
|
+
export class KillCommand extends AttackCommand {
|
|
3
11
|
static verb = 'kill';
|
|
4
|
-
static description = '
|
|
5
|
-
async execute(args = []) {
|
|
6
|
-
if (!args)
|
|
7
|
-
return '';
|
|
8
|
-
if (args.length === 0) {
|
|
9
|
-
return 'Who do you want to kill? Try: `kill [character name]`';
|
|
10
|
-
}
|
|
11
|
-
const targetName = args.join(' ').toLowerCase();
|
|
12
|
-
const currentRoom = this.actor.currentLocation;
|
|
13
|
-
const scene = this.scene;
|
|
14
|
-
if (!currentRoom || !scene) {
|
|
15
|
-
return 'You are not in a valid location to perform this action.';
|
|
16
|
-
}
|
|
17
|
-
const possibleTargets = scene.getActorsInRoom(currentRoom).filter(actor => actor !== this.actor);
|
|
18
|
-
const target = possibleTargets.find(actor => actor.name.toLowerCase() === targetName);
|
|
19
|
-
const possibleTargetNames = possibleTargets.map(a => a.name).join(', ');
|
|
20
|
-
this.logger.write(`KillCommand: Possible targets in ${currentRoom.name}: ${possibleTargetNames}`);
|
|
21
|
-
if (!target) {
|
|
22
|
-
return `You don’t see anyone named "${targetName}" here.`;
|
|
23
|
-
}
|
|
24
|
-
const isPlayer = target === scene.getPlayer();
|
|
25
|
-
scene.removeActor(target.name);
|
|
26
|
-
this.logger.write(`KillCommand: ${this.actor.name} killed ${target.name} in ${currentRoom.name}`);
|
|
27
|
-
this.actor.performAction('kills', target.name);
|
|
28
|
-
if (isPlayer) {
|
|
29
|
-
await this.scene.endGame();
|
|
30
|
-
this.logger.logWithColor(`Game Over: ${target.name} (the player) was killed by ${this.actor.name}.`, 'red');
|
|
31
|
-
this.logger.logWithColor(`Press ctrl-c to quit`, 'red');
|
|
32
|
-
}
|
|
33
|
-
return `You swiftly end ${target.name}'s life. Their body collapses in ${currentRoom.name}.`;
|
|
34
|
-
}
|
|
12
|
+
static description = 'Alias for "attack" -- resolves as real dice-pool combat, not an instant kill.';
|
|
35
13
|
}
|
|
36
14
|
//# sourceMappingURL=kill.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kill.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/kill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"kill.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/kill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;GAOG;AACH,MAAM,OAAO,WAAY,SAAQ,aAAa;IAClC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC;IACrB,MAAM,CAAC,WAAW,GAAG,+EAA+E,CAAC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Door } from '../models/door.js';
|
|
2
|
+
import { NPC } from '../models/npc.js';
|
|
2
3
|
import { Category } from '../types/shared/item-enum.js';
|
|
3
4
|
import { Command } from './command.js';
|
|
5
|
+
import { shellsInRoom, canReachHost } from '../utilities/planes.js';
|
|
4
6
|
export class LookCommand extends Command {
|
|
5
7
|
static verb = 'look';
|
|
6
8
|
static description = 'Examine your surroundings';
|
|
@@ -24,10 +26,11 @@ export class LookCommand extends Command {
|
|
|
24
26
|
return this.lookOutside(target);
|
|
25
27
|
}
|
|
26
28
|
else {
|
|
27
|
-
// Check if it's another player or NPC in the room
|
|
29
|
+
// Check if it's another player or NPC in the room (that this
|
|
30
|
+
// actor's plane can perceive -- see Player.canPerceive)
|
|
28
31
|
const actorMatch = this.scene
|
|
29
32
|
.getActorsInRoom(this.actor.currentLocation)
|
|
30
|
-
.find(a => a.name.toLowerCase() === target.toLowerCase());
|
|
33
|
+
.find(a => a.name.toLowerCase() === target.toLowerCase() && this.actor.canPerceive(a));
|
|
31
34
|
if (actorMatch) {
|
|
32
35
|
return this.inspectActor(actorMatch.name);
|
|
33
36
|
}
|
|
@@ -46,13 +49,50 @@ export class LookCommand extends Command {
|
|
|
46
49
|
return "You're not in a valid room.";
|
|
47
50
|
}
|
|
48
51
|
let description = (room.details) ? room.details : room.description;
|
|
52
|
+
// Plane dressing: what a room IS depends on where you're standing.
|
|
53
|
+
// A node reads differently by state: burning (hackable), dark
|
|
54
|
+
// (air-gapped, hard line only -- see planes.canReachHost), or cracked
|
|
55
|
+
// open (already yours).
|
|
56
|
+
if (this.actor.plane === 'matrix') {
|
|
57
|
+
if (!room.hasNode) {
|
|
58
|
+
description += `\n\n{cyan-fg}Thin grid out here -- ambient AR clutter, nothing worth cracking.{/cyan-fg}`;
|
|
59
|
+
}
|
|
60
|
+
else if (!canReachHost(this.actor, room)) {
|
|
61
|
+
description += `\n\n{cyan-fg}A host squats here, DARK -- air-gapped, no wireless presence at all. It only opens to a hard line: jack in from inside this room.{/cyan-fg}`;
|
|
62
|
+
}
|
|
63
|
+
else if (room.hostCracked) {
|
|
64
|
+
description += `\n\n{cyan-fg}The host here hangs cracked open, security in shreds -- whatever it guarded is unsealed.{/cyan-fg}`;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
description += `\n\n{cyan-fg}A host node burns here -- sculpted ice geometry over the room's systems. This is hackable ground.{/cyan-fg}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (this.actor.plane === 'astral') {
|
|
71
|
+
description += room.warded
|
|
72
|
+
? `\n\n{magenta-fg}The walls here are SEALED -- a ward's lattice hums across every surface.{/magenta-fg}`
|
|
73
|
+
: `\n\n{magenta-fg}The meat world glows dim below you; emotions stain the walls like old smoke.{/magenta-fg}`;
|
|
74
|
+
}
|
|
75
|
+
// Only what this plane perceives (see Player.canPerceive); an astral
|
|
76
|
+
// viewer sees meat actors as auras.
|
|
49
77
|
const otherActors = this.scene
|
|
50
78
|
.getActorsInRoom(room)
|
|
51
|
-
.filter(a => a.name !== this.actor.name);
|
|
79
|
+
.filter(a => a.name !== this.actor.name && this.actor.canPerceive(a));
|
|
52
80
|
if (otherActors.length > 0) {
|
|
53
|
-
const actorList = otherActors.map(a =>
|
|
81
|
+
const actorList = otherActors.map(a => {
|
|
82
|
+
const aura = this.actor.plane === 'astral' && a.plane === 'meat' ? ' (an aura, dim and physical)' : '';
|
|
83
|
+
return `• ${a.name}${aura}`;
|
|
84
|
+
}).join('\n');
|
|
54
85
|
description += `\n\nYou see:\n${actorList}`;
|
|
55
86
|
}
|
|
87
|
+
// Empty bodies slump in plain sight of the meat plane -- and the
|
|
88
|
+
// astral, which watches over it.
|
|
89
|
+
if (this.actor.plane !== 'matrix') {
|
|
90
|
+
const shells = shellsInRoom(this.scene, room).filter(s => s !== this.actor);
|
|
91
|
+
if (shells.length > 0) {
|
|
92
|
+
const shellList = shells.map(s => `• ${s.name}'s body -- slumped and vacant, nobody home`).join('\n');
|
|
93
|
+
description += `\n\n${shellList}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
56
96
|
// --- Add exits here ---
|
|
57
97
|
const exits = Array.from(room.exits.entries())
|
|
58
98
|
.map(([direction, exit]) => {
|
|
@@ -116,6 +156,9 @@ export class LookCommand extends Command {
|
|
|
116
156
|
}
|
|
117
157
|
const lines = [];
|
|
118
158
|
lines.push(`${target.name} is here.`);
|
|
159
|
+
if (target instanceof NPC && target.appearance) {
|
|
160
|
+
lines.push(target.appearance);
|
|
161
|
+
}
|
|
119
162
|
const equipment = target.equipment;
|
|
120
163
|
for (const groupKey of Object.keys(equipment)) {
|
|
121
164
|
const group = equipment[groupKey];
|