@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,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fuzzy name-matching, extracted from Inventory's item resolution so
|
|
3
|
+
* actor targeting (attack.ts, cast.ts) behaves the same way "take" does --
|
|
4
|
+
* found via a real session where "cast manabolt at mr krow" failed against
|
|
5
|
+
* "Mr. Krow" over a missing period. Three tiers, in order of confidence:
|
|
6
|
+
*
|
|
7
|
+
* 1. Exact match on the loosely-normalized strings (case, punctuation,
|
|
8
|
+
* and extra whitespace ignored -- "mr krow" == "Mr. Krow").
|
|
9
|
+
* 2. Substring containment, either direction -- short aliases ("krow"),
|
|
10
|
+
* or extra/missing words at the edges.
|
|
11
|
+
* 3. Word-overlap with light per-word typo tolerance (Levenshtein <= 1
|
|
12
|
+
* on words longer than 3 chars, stopwords ignored) -- misspellings
|
|
13
|
+
* and reordered/inserted words.
|
|
14
|
+
*
|
|
15
|
+
* A tier only matches when it produces exactly ONE candidate (or a clear
|
|
16
|
+
* best score) -- an ambiguous guess deliberately misses rather than
|
|
17
|
+
* picking someone at random.
|
|
18
|
+
*/
|
|
19
|
+
export const STOPWORDS = new Set(['a', 'an', 'the', 'of', 'on', 'in', 'at', 'to', 'with']);
|
|
20
|
+
/** Lowercase, strip punctuation, collapse whitespace. */
|
|
21
|
+
export function normalizeLoose(s) {
|
|
22
|
+
return s.toLowerCase().replace(/[^\w\s]/g, '').replace(/\s+/g, ' ').trim();
|
|
23
|
+
}
|
|
24
|
+
export function significantWords(s) {
|
|
25
|
+
return s.split(/\s+/).filter(w => w.length > 0 && !STOPWORDS.has(w));
|
|
26
|
+
}
|
|
27
|
+
export function wordsAreCloseEnough(a, b) {
|
|
28
|
+
if (a === b)
|
|
29
|
+
return true;
|
|
30
|
+
if (a.length <= 3 || b.length <= 3)
|
|
31
|
+
return false; // too short for typo tolerance to be safe
|
|
32
|
+
return levenshteinDistance(a, b) <= 1;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Damerau-Levenshtein (OSA variant): like plain Levenshtein but a swap of
|
|
36
|
+
* two adjacent letters counts as ONE edit, not two -- transpositions
|
|
37
|
+
* ("jhonson" for "johnson", "teh" for "the") are the single most common
|
|
38
|
+
* typo class, and plain Levenshtein prices them out of the <=1 tolerance.
|
|
39
|
+
*/
|
|
40
|
+
export function levenshteinDistance(a, b) {
|
|
41
|
+
const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
42
|
+
for (let i = 0; i <= a.length; i++)
|
|
43
|
+
dp[i][0] = i;
|
|
44
|
+
for (let j = 0; j <= b.length; j++)
|
|
45
|
+
dp[0][j] = j;
|
|
46
|
+
for (let i = 1; i <= a.length; i++) {
|
|
47
|
+
for (let j = 1; j <= b.length; j++) {
|
|
48
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
49
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
50
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
51
|
+
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + cost);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return dp[a.length][b.length];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Picks the single name from `names` that `input` most plausibly refers
|
|
59
|
+
* to, or undefined when nothing (or more than one thing) qualifies.
|
|
60
|
+
* Returns the ORIGINAL name string, not the normalized form.
|
|
61
|
+
*/
|
|
62
|
+
export function fuzzyPickName(input, names) {
|
|
63
|
+
const normalized = normalizeLoose(input);
|
|
64
|
+
if (normalized.length === 0)
|
|
65
|
+
return undefined;
|
|
66
|
+
const candidates = names.map(name => ({ name, norm: normalizeLoose(name) }));
|
|
67
|
+
const exact = candidates.filter(c => c.norm === normalized);
|
|
68
|
+
if (exact.length === 1)
|
|
69
|
+
return exact[0].name;
|
|
70
|
+
if (exact.length > 1)
|
|
71
|
+
return undefined;
|
|
72
|
+
const substring = candidates.filter(c => c.norm.includes(normalized) || normalized.includes(c.norm));
|
|
73
|
+
if (substring.length === 1)
|
|
74
|
+
return substring[0].name;
|
|
75
|
+
const inputWords = significantWords(normalized);
|
|
76
|
+
if (inputWords.length === 0)
|
|
77
|
+
return undefined;
|
|
78
|
+
const scored = candidates
|
|
79
|
+
.map(c => {
|
|
80
|
+
const nameWords = significantWords(c.norm);
|
|
81
|
+
const matched = inputWords.filter(iw => nameWords.some(nw => wordsAreCloseEnough(iw, nw))).length;
|
|
82
|
+
return { name: c.name, score: matched / inputWords.length };
|
|
83
|
+
})
|
|
84
|
+
.filter(s => s.score >= 0.6)
|
|
85
|
+
.sort((a, b) => b.score - a.score);
|
|
86
|
+
if (scored.length === 1)
|
|
87
|
+
return scored[0].name;
|
|
88
|
+
if (scored.length > 1 && scored[0].score > scored[1].score)
|
|
89
|
+
return scored[0].name; // clear winner
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=fuzzy-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuzzy-match.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/utilities/fuzzy-match.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Player, SimMode, Plane } from '../models/player.js';
|
|
2
|
+
import { Item } from '../models/item.js';
|
|
3
|
+
import { NPC } from '../models/npc.js';
|
|
4
|
+
import { Scene } from '../models/scene.js';
|
|
5
|
+
import { Room } from '../models/room.js';
|
|
6
|
+
/**
|
|
7
|
+
* Plane transitions and their tolls -- the one place that moves an actor
|
|
8
|
+
* between meat, Matrix, and astral (see Player.plane for the model).
|
|
9
|
+
*
|
|
10
|
+
* The invariant every transition preserves: while off the meat plane, the
|
|
11
|
+
* actor's empty body slumps in `bodyRoom`, visible and attackable
|
|
12
|
+
* (resolveBodyAttack); snapping back -- voluntarily or forced -- returns
|
|
13
|
+
* the actor there and settles the toll:
|
|
14
|
+
*
|
|
15
|
+
* Matrix -- voluntary jack-out is clean; a forced dump is DUMPSHOCK,
|
|
16
|
+
* resisted with Willpower: stun in cold-sim, PHYSICAL in
|
|
17
|
+
* hot-sim. The persona's Matrix track resets either way (a
|
|
18
|
+
* rebooted persona comes back clean).
|
|
19
|
+
* Astral -- the longer you were out (astralTicks, one per room-move),
|
|
20
|
+
* the harder the snap-back: resist ceil(ticks/2) stun with
|
|
21
|
+
* Willpower + half Magic; a forced return adds +2 before the
|
|
22
|
+
* resist.
|
|
23
|
+
*/
|
|
24
|
+
export declare function planeLabel(actor: Player): string;
|
|
25
|
+
/** Every off-plane actor whose empty body lies in this room. */
|
|
26
|
+
export declare function shellsInRoom(scene: Scene, room: Room): (Player | NPC)[];
|
|
27
|
+
export declare function enterMatrix(scene: Scene, actor: Player, mode: SimMode): string[];
|
|
28
|
+
export declare function leaveMatrix(scene: Scene, actor: Player, opts?: {
|
|
29
|
+
forced?: boolean;
|
|
30
|
+
reason?: string;
|
|
31
|
+
}): string[];
|
|
32
|
+
export declare function projectAstral(scene: Scene, actor: Player): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Whether this actor's jacked-in persona can touch the host in `room`.
|
|
35
|
+
* A wireless host (the default) is on the grid -- any persona standing in
|
|
36
|
+
* the room's overlay reaches it. A WIRED host is air-gapped: it only
|
|
37
|
+
* opens to a hard line, meaning the actor's BODY is in this room (they
|
|
38
|
+
* jacked in here). Movement is never gated -- the grid overlays every
|
|
39
|
+
* room -- only the host itself (hacking it, and its vaulted files).
|
|
40
|
+
*/
|
|
41
|
+
export declare function canReachHost(actor: Player, room: Room): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* An item name tinted for the plane it lives on -- the game's plane
|
|
44
|
+
* palette (matrix cyan, astral magenta, same as the HUD) applied to item
|
|
45
|
+
* listings so WHAT a thing is reads at a glance. A real session's decker
|
|
46
|
+
* found matrix paydata via a jacked-in search and didn't realize it was
|
|
47
|
+
* takeable right there: the name looked exactly like every meat item, so
|
|
48
|
+
* they jacked out and walked the whole map hunting for it physically.
|
|
49
|
+
* `viewerPlane` drives the astral cross-plane annotation (they see meat
|
|
50
|
+
* items, dimly, but can't carry them -- see take.ts).
|
|
51
|
+
*/
|
|
52
|
+
export declare function planeTintItemName(item: Item, viewerPlane: Plane): string;
|
|
53
|
+
/**
|
|
54
|
+
* The "you can see them, but you can't touch them" refusal. When a target
|
|
55
|
+
* name matches someone in the room on a plane the actor perceives but
|
|
56
|
+
* can't reach (today: an astral projector eyeing a meat body), returns a
|
|
57
|
+
* line naming the actual rule -- because "Present: no one" right after
|
|
58
|
+
* "look" listed that very person's aura reads as a bug, not a boundary
|
|
59
|
+
* (real session: three attempts on a visible Mr. Krow, all told "no one").
|
|
60
|
+
* Returns null when no cross-plane match, so callers fall through to
|
|
61
|
+
* their normal miss handling. `deed` picks the phrasing: a weapon strike,
|
|
62
|
+
* a combat spell, or touch-range support magic.
|
|
63
|
+
*/
|
|
64
|
+
export declare function crossPlaneRefusal(scene: Scene, actor: Player, room: Room, targetName: string, deed: 'strike' | 'spell' | 'touch'): string | null;
|
|
65
|
+
export declare function returnToBody(scene: Scene, actor: Player, opts?: {
|
|
66
|
+
forced?: boolean;
|
|
67
|
+
reason?: string;
|
|
68
|
+
}): string[];
|
|
69
|
+
/**
|
|
70
|
+
* A meat-plane attack on an empty body: one UNANSWERED strike -- no
|
|
71
|
+
* defense roll (nobody's home), meat soak only (Body + worn armor; the
|
|
72
|
+
* plane-aware getSoakPool would wrongly price the persona's firewall) --
|
|
73
|
+
* then the owner is slammed back into a body under attack, paying the
|
|
74
|
+
* forced-return toll on top of whatever got through. Returns the full
|
|
75
|
+
* narration; the caller announces it.
|
|
76
|
+
*/
|
|
77
|
+
export declare function resolveBodyAttack(scene: Scene, attacker: Player | NPC, victim: Player | NPC): string[];
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { Logger } from './logger.js';
|
|
2
|
+
import { rollPool, formatRoll } from './dice.js';
|
|
3
|
+
import { fuzzyPickName } from './fuzzy-match.js';
|
|
4
|
+
/**
|
|
5
|
+
* Plane transitions and their tolls -- the one place that moves an actor
|
|
6
|
+
* between meat, Matrix, and astral (see Player.plane for the model).
|
|
7
|
+
*
|
|
8
|
+
* The invariant every transition preserves: while off the meat plane, the
|
|
9
|
+
* actor's empty body slumps in `bodyRoom`, visible and attackable
|
|
10
|
+
* (resolveBodyAttack); snapping back -- voluntarily or forced -- returns
|
|
11
|
+
* the actor there and settles the toll:
|
|
12
|
+
*
|
|
13
|
+
* Matrix -- voluntary jack-out is clean; a forced dump is DUMPSHOCK,
|
|
14
|
+
* resisted with Willpower: stun in cold-sim, PHYSICAL in
|
|
15
|
+
* hot-sim. The persona's Matrix track resets either way (a
|
|
16
|
+
* rebooted persona comes back clean).
|
|
17
|
+
* Astral -- the longer you were out (astralTicks, one per room-move),
|
|
18
|
+
* the harder the snap-back: resist ceil(ticks/2) stun with
|
|
19
|
+
* Willpower + half Magic; a forced return adds +2 before the
|
|
20
|
+
* resist.
|
|
21
|
+
*/
|
|
22
|
+
export function planeLabel(actor) {
|
|
23
|
+
if (actor.plane === 'matrix')
|
|
24
|
+
return `MATRIX (${actor.simMode ?? 'cold'}-sim)`;
|
|
25
|
+
if (actor.plane === 'astral')
|
|
26
|
+
return 'ASTRAL';
|
|
27
|
+
return 'MEAT';
|
|
28
|
+
}
|
|
29
|
+
/** Every off-plane actor whose empty body lies in this room. */
|
|
30
|
+
export function shellsInRoom(scene, room) {
|
|
31
|
+
return scene.allActors.filter(a => a.plane !== 'meat' && a.bodyRoom === room);
|
|
32
|
+
}
|
|
33
|
+
export function enterMatrix(scene, actor, mode) {
|
|
34
|
+
actor.bodyRoom = actor.currentLocation;
|
|
35
|
+
actor.plane = 'matrix';
|
|
36
|
+
actor.simMode = mode;
|
|
37
|
+
// The deck you ride in on is the deck that takes the damage (see
|
|
38
|
+
// Item's device condition monitor). Its existing dents come with it --
|
|
39
|
+
// hardware doesn't heal between runs.
|
|
40
|
+
actor.activeDeck = actor.getCyberdeck();
|
|
41
|
+
actor.performAction('jacks in', `slumping where they stand, eyes flickering (${mode}-sim)`);
|
|
42
|
+
scene.addWorldEvent(`${actor.name} jacked into the Matrix in ${actor.bodyRoom.name}.`);
|
|
43
|
+
Logger.getInstance().write(`${actor.name} entered the Matrix (${mode}-sim, ${actor.activeDeck?.name ?? (actor.isTechnomancer() ? 'living persona' : 'no deck?!')}) from ${actor.bodyRoom.name}.`);
|
|
44
|
+
const hotWarning = mode === 'hot'
|
|
45
|
+
? ` Hot-sim: everything is faster and hits harder -- including what hits YOU. Ice biofeedback bleeds into your real stun track.`
|
|
46
|
+
: actor.isTechnomancer()
|
|
47
|
+
? ` There is no deck between you and the ice -- every hit out here is YOUR mind taking it.`
|
|
48
|
+
: ` Cold-sim: the ice can only chew on your deck, not your brain.`;
|
|
49
|
+
const deckNote = actor.activeDeck && actor.activeDeck.deckDamage > 0
|
|
50
|
+
? ` Your ${actor.activeDeck.name} is already scarred: ${actor.activeDeck.deckSummary()}.`
|
|
51
|
+
: '';
|
|
52
|
+
const entry = actor.isTechnomancer()
|
|
53
|
+
? `You close your eyes and the Matrix is simply THERE -- no deck, no interface, just the resonance singing. Your body slumps where you stood in ${actor.bodyRoom.name}, empty.`
|
|
54
|
+
: `Your ${actor.activeDeck?.name ?? 'deck'} spins up and the room dissolves into geometry -- you're in. Your body slumps where you stood in ${actor.bodyRoom.name}, empty.`;
|
|
55
|
+
return [
|
|
56
|
+
`${entry}${hotWarning}${deckNote}`,
|
|
57
|
+
`Move with "go" (the grid overlays the world), "hack" inside a host node, "jack out" to return.`,
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
export function leaveMatrix(scene, actor, opts) {
|
|
61
|
+
const lines = [];
|
|
62
|
+
const body = actor.bodyRoom;
|
|
63
|
+
const wasHot = actor.simMode === 'hot';
|
|
64
|
+
if (opts?.forced) {
|
|
65
|
+
// Dumpshock: 4 boxes resisted with Willpower -- stun in cold-sim,
|
|
66
|
+
// physical in hot-sim (your brain was IN there).
|
|
67
|
+
const resist = rollPool(actor.willpower);
|
|
68
|
+
const dealt = Math.max(0, 4 - resist.hits);
|
|
69
|
+
lines.push(opts.reason ?? `The connection is severed from the other side.`);
|
|
70
|
+
lines.push(` DUMPSHOCK -- resist: ${formatRoll(resist)}`);
|
|
71
|
+
if (dealt > 0) {
|
|
72
|
+
if (wasHot) {
|
|
73
|
+
actor.takeDamage(dealt);
|
|
74
|
+
lines.push(` The world tears loose -- ${dealt} box${dealt === 1 ? '' : 'es'} PHYSICAL (hot-sim): ${actor.conditionSummary()}`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
actor.takeStun(dealt);
|
|
78
|
+
lines.push(` Your head rings like a struck bell -- ${dealt} stun: ${actor.stunSummary()}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
lines.push(` You ride it out -- no damage.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
lines.push(`You back out clean and unplug.`);
|
|
87
|
+
}
|
|
88
|
+
// Deck damage STAYS -- it's hardware, not a session variable. A bricked
|
|
89
|
+
// deck refuses the next jack-in until repaired (rest.ts).
|
|
90
|
+
if (actor.activeDeck?.isBricked) {
|
|
91
|
+
lines.push(`Your ${actor.activeDeck.name} is BRICKED -- dead boards, burnt trace. It repairs while you rest somewhere safe.`);
|
|
92
|
+
}
|
|
93
|
+
actor.plane = 'meat';
|
|
94
|
+
actor.simMode = undefined;
|
|
95
|
+
actor.activeDeck = undefined;
|
|
96
|
+
if (body) {
|
|
97
|
+
actor.currentLocation = body;
|
|
98
|
+
actor.bodyRoom = undefined;
|
|
99
|
+
lines.push(`You're back in your body in ${body.name}.`);
|
|
100
|
+
}
|
|
101
|
+
actor.performAction('jacks out', 'stirring awake');
|
|
102
|
+
scene.addWorldEvent(`${actor.name} jacked out of the Matrix.`);
|
|
103
|
+
scene.updateStatus();
|
|
104
|
+
return lines;
|
|
105
|
+
}
|
|
106
|
+
export function projectAstral(scene, actor) {
|
|
107
|
+
actor.bodyRoom = actor.currentLocation;
|
|
108
|
+
actor.plane = 'astral';
|
|
109
|
+
actor.astralTicks = 0;
|
|
110
|
+
// Projection IS full astral senses -- the sustained half-sight toggle
|
|
111
|
+
// (and its -2) makes no sense out here.
|
|
112
|
+
actor.astralPerceiving = false;
|
|
113
|
+
actor.performAction('projects', 'body going slack, something vast leaving their eyes');
|
|
114
|
+
scene.addWorldEvent(`${actor.name} projected astrally from ${actor.bodyRoom.name}.`);
|
|
115
|
+
Logger.getInstance().write(`${actor.name} projected astrally from ${actor.bodyRoom.name}.`);
|
|
116
|
+
return [
|
|
117
|
+
`You slip free of your flesh. The world stays -- shifted: auras where faces were, glass-bright emotion, the meat world dim and untouchable below. Your body slumps in ${actor.bodyRoom.name}, empty.`,
|
|
118
|
+
`Move with "go" (locked doors mean nothing; wards do), "assense" what you see, "return" to your body. Every move out here is a debt the snap-back collects.`,
|
|
119
|
+
`The meat world is READ-ONLY from out here: you can watch and assense it, nothing more. No touching, no taking, no talking to the living, no spells or strikes across the veil -- astral combat only finds spirits and other projections. To act on flesh, go back to yours.`,
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Whether this actor's jacked-in persona can touch the host in `room`.
|
|
124
|
+
* A wireless host (the default) is on the grid -- any persona standing in
|
|
125
|
+
* the room's overlay reaches it. A WIRED host is air-gapped: it only
|
|
126
|
+
* opens to a hard line, meaning the actor's BODY is in this room (they
|
|
127
|
+
* jacked in here). Movement is never gated -- the grid overlays every
|
|
128
|
+
* room -- only the host itself (hacking it, and its vaulted files).
|
|
129
|
+
*/
|
|
130
|
+
export function canReachHost(actor, room) {
|
|
131
|
+
if (!room.hasNode)
|
|
132
|
+
return false;
|
|
133
|
+
if (room.nodeAccess !== 'wired')
|
|
134
|
+
return true;
|
|
135
|
+
return actor.bodyRoom === room;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* An item name tinted for the plane it lives on -- the game's plane
|
|
139
|
+
* palette (matrix cyan, astral magenta, same as the HUD) applied to item
|
|
140
|
+
* listings so WHAT a thing is reads at a glance. A real session's decker
|
|
141
|
+
* found matrix paydata via a jacked-in search and didn't realize it was
|
|
142
|
+
* takeable right there: the name looked exactly like every meat item, so
|
|
143
|
+
* they jacked out and walked the whole map hunting for it physically.
|
|
144
|
+
* `viewerPlane` drives the astral cross-plane annotation (they see meat
|
|
145
|
+
* items, dimly, but can't carry them -- see take.ts).
|
|
146
|
+
*/
|
|
147
|
+
export function planeTintItemName(item, viewerPlane) {
|
|
148
|
+
if (item.plane === 'matrix') {
|
|
149
|
+
return `{cyan-fg}${item.name} [data]{/cyan-fg}`;
|
|
150
|
+
}
|
|
151
|
+
if (item.plane === 'astral') {
|
|
152
|
+
return `{magenta-fg}${item.name} [astral]{/magenta-fg}`;
|
|
153
|
+
}
|
|
154
|
+
if (viewerPlane === 'astral') {
|
|
155
|
+
return `${item.name} (physical -- look, don't touch)`;
|
|
156
|
+
}
|
|
157
|
+
return item.name;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The "you can see them, but you can't touch them" refusal. When a target
|
|
161
|
+
* name matches someone in the room on a plane the actor perceives but
|
|
162
|
+
* can't reach (today: an astral projector eyeing a meat body), returns a
|
|
163
|
+
* line naming the actual rule -- because "Present: no one" right after
|
|
164
|
+
* "look" listed that very person's aura reads as a bug, not a boundary
|
|
165
|
+
* (real session: three attempts on a visible Mr. Krow, all told "no one").
|
|
166
|
+
* Returns null when no cross-plane match, so callers fall through to
|
|
167
|
+
* their normal miss handling. `deed` picks the phrasing: a weapon strike,
|
|
168
|
+
* a combat spell, or touch-range support magic.
|
|
169
|
+
*/
|
|
170
|
+
export function crossPlaneRefusal(scene, actor, room, targetName, deed) {
|
|
171
|
+
const visible = scene.getActorsInRoom(room)
|
|
172
|
+
.filter(a => a !== actor && a.plane !== actor.plane && actor.canPerceive(a));
|
|
173
|
+
const picked = fuzzyPickName(targetName, visible.map(a => a.name));
|
|
174
|
+
if (!picked)
|
|
175
|
+
return null;
|
|
176
|
+
const other = visible.find(a => a.name === picked);
|
|
177
|
+
if (actor.plane === 'astral' && other.plane === 'meat') {
|
|
178
|
+
switch (deed) {
|
|
179
|
+
case 'strike':
|
|
180
|
+
return `${other.name} is right there -- but only in the meat. From the astral you're a ghost: watch them, "assense" them, and that's all. Astral strikes only land on astral things (spirits, other projections). "return" to your body if you want to lay hands on them.`;
|
|
181
|
+
case 'spell':
|
|
182
|
+
return `${other.name}'s aura burns right below you, but mana can't cross back into the meat from out here. Spells cast while projecting only touch astral targets (spirits, projections). "return" to your body, then cast with flesh behind it.`;
|
|
183
|
+
case 'touch':
|
|
184
|
+
return `${other.name} is meat and right now you're not -- touch-range magic needs your hands. "return" to your body first.`;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return `${other.name} is on a plane your hands can't reach -- you can see them, not touch them.`;
|
|
188
|
+
}
|
|
189
|
+
export function returnToBody(scene, actor, opts) {
|
|
190
|
+
const lines = [];
|
|
191
|
+
const body = actor.bodyRoom;
|
|
192
|
+
const debt = Math.ceil(actor.astralTicks / 2) + (opts?.forced ? 2 : 0);
|
|
193
|
+
if (opts?.reason)
|
|
194
|
+
lines.push(opts.reason);
|
|
195
|
+
if (debt > 0) {
|
|
196
|
+
const resist = rollPool(actor.willpower + Math.floor(actor.magic / 2));
|
|
197
|
+
const dealt = Math.max(0, debt - resist.hits);
|
|
198
|
+
lines.push(` The snap-back (${debt}) -- resist: ${formatRoll(resist)}`);
|
|
199
|
+
if (dealt > 0) {
|
|
200
|
+
actor.takeStun(dealt);
|
|
201
|
+
lines.push(` Re-entry burns -- ${dealt} stun: ${actor.stunSummary()}`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
lines.push(` You settle back into your flesh without a tremor.`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
lines.push(`You flow back down the silver thread.`);
|
|
209
|
+
}
|
|
210
|
+
actor.plane = 'meat';
|
|
211
|
+
actor.astralTicks = 0;
|
|
212
|
+
if (body) {
|
|
213
|
+
actor.currentLocation = body;
|
|
214
|
+
actor.bodyRoom = undefined;
|
|
215
|
+
lines.push(`You open your own eyes in ${body.name}.`);
|
|
216
|
+
}
|
|
217
|
+
actor.performAction('returns to their body', 'drawing a sharp breath');
|
|
218
|
+
scene.addWorldEvent(`${actor.name} returned to their body.`);
|
|
219
|
+
scene.updateStatus();
|
|
220
|
+
return lines;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* A meat-plane attack on an empty body: one UNANSWERED strike -- no
|
|
224
|
+
* defense roll (nobody's home), meat soak only (Body + worn armor; the
|
|
225
|
+
* plane-aware getSoakPool would wrongly price the persona's firewall) --
|
|
226
|
+
* then the owner is slammed back into a body under attack, paying the
|
|
227
|
+
* forced-return toll on top of whatever got through. Returns the full
|
|
228
|
+
* narration; the caller announces it.
|
|
229
|
+
*/
|
|
230
|
+
export function resolveBodyAttack(scene, attacker, victim) {
|
|
231
|
+
const logger = Logger.getInstance();
|
|
232
|
+
const lines = [];
|
|
233
|
+
const wasPlane = victim.plane;
|
|
234
|
+
const weapon = attacker.getWeaponProfile();
|
|
235
|
+
const attack = rollPool(attacker.getAttackPool());
|
|
236
|
+
const soakPool = Math.max(0, victim.body + victim.getArmorValue());
|
|
237
|
+
lines.push(`${attacker.name} attacks ${victim.name}'s empty body with ${weapon.name} -- nobody's home to dodge:`);
|
|
238
|
+
lines.push(` Attack: ${formatRoll(attack)}`);
|
|
239
|
+
if (attack.hits === 0) {
|
|
240
|
+
lines.push(` Somehow, the blow goes wide of even a motionless target.`);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
const incoming = weapon.damage + attack.hits;
|
|
244
|
+
const soak = rollPool(soakPool);
|
|
245
|
+
const dealt = Math.max(0, incoming - soak.hits);
|
|
246
|
+
lines.push(` Hit! ${incoming} damage incoming.`);
|
|
247
|
+
lines.push(` Soak: ${formatRoll(soak)}`);
|
|
248
|
+
if (dealt === 0) {
|
|
249
|
+
lines.push(` The armor holds -- the body doesn't even rock.`);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
victim.takeDamage(dealt);
|
|
253
|
+
lines.push(` ${dealt} box${dealt === 1 ? '' : 'es'} into the defenseless flesh.`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
logger.write(`${attacker.name} attacked ${victim.name}'s empty body (${wasPlane}).`);
|
|
257
|
+
// The owner feels it and snaps back -- into a room with an attacker.
|
|
258
|
+
const reason = `Something is happening to your BODY --`;
|
|
259
|
+
const back = wasPlane === 'matrix'
|
|
260
|
+
? leaveMatrix(scene, victim, { forced: true, reason })
|
|
261
|
+
: returnToBody(scene, victim, { forced: true, reason });
|
|
262
|
+
lines.push('', ...back);
|
|
263
|
+
return lines;
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=planes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planes.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/utilities/planes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Scene } from '../models/scene.js';
|
|
2
|
+
import { Room } from '../models/room.js';
|
|
3
|
+
/**
|
|
4
|
+
* When a combat target isn't in the room, work out where they actually are
|
|
5
|
+
* and phrase it as something actionable -- found via a real session where a
|
|
6
|
+
* player ping-ponged between two rooms and the hostile NPC's AI loop just
|
|
7
|
+
* spammed dozens of failed "attack" commands at the empty room instead of
|
|
8
|
+
* chasing. The failed command's RESULT string is the one thing guaranteed
|
|
9
|
+
* to land in an NPC's AI history, so putting the literal next command in it
|
|
10
|
+
* ("go west" to chase) is what converts flailing into pursuit. Works for
|
|
11
|
+
* the player too: attacking someone who just fled tells you where they ran.
|
|
12
|
+
*
|
|
13
|
+
* Returns undefined when the name matches nobody in the scene at all --
|
|
14
|
+
* the caller keeps its ordinary "no such person" message for that.
|
|
15
|
+
*/
|
|
16
|
+
export declare function describeFledTarget(scene: Scene, fromRoom: Room, targetName: string): string | undefined;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Door } from '../models/door.js';
|
|
2
|
+
import { fuzzyPickName } from './fuzzy-match.js';
|
|
3
|
+
/**
|
|
4
|
+
* When a combat target isn't in the room, work out where they actually are
|
|
5
|
+
* and phrase it as something actionable -- found via a real session where a
|
|
6
|
+
* player ping-ponged between two rooms and the hostile NPC's AI loop just
|
|
7
|
+
* spammed dozens of failed "attack" commands at the empty room instead of
|
|
8
|
+
* chasing. The failed command's RESULT string is the one thing guaranteed
|
|
9
|
+
* to land in an NPC's AI history, so putting the literal next command in it
|
|
10
|
+
* ("go west" to chase) is what converts flailing into pursuit. Works for
|
|
11
|
+
* the player too: attacking someone who just fled tells you where they ran.
|
|
12
|
+
*
|
|
13
|
+
* Returns undefined when the name matches nobody in the scene at all --
|
|
14
|
+
* the caller keeps its ordinary "no such person" message for that.
|
|
15
|
+
*/
|
|
16
|
+
export function describeFledTarget(scene, fromRoom, targetName) {
|
|
17
|
+
const elsewhere = scene.allActors.filter(a => a.currentLocation !== fromRoom);
|
|
18
|
+
const pickedName = fuzzyPickName(targetName, elsewhere.map(a => a.name));
|
|
19
|
+
if (!pickedName)
|
|
20
|
+
return undefined;
|
|
21
|
+
const target = elsewhere.find(a => a.name === pickedName);
|
|
22
|
+
if (!target || !target.currentLocation)
|
|
23
|
+
return undefined;
|
|
24
|
+
const theirRoom = target.currentLocation;
|
|
25
|
+
// Adjacent? Name the direction and whether the way is open.
|
|
26
|
+
for (const [direction, exit] of fromRoom.exits.entries()) {
|
|
27
|
+
if (exit instanceof Door && exit.getOtherSide(fromRoom) === theirRoom) {
|
|
28
|
+
if (exit.checkIfLocked()) {
|
|
29
|
+
return `${target.name} isn't here -- they're through the ${direction} door in ${theirRoom.name}, but it's locked.`;
|
|
30
|
+
}
|
|
31
|
+
return `${target.name} isn't here -- they went ${direction}, to ${theirRoom.name}. ("go ${direction}" to chase them.)`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return `${target.name} isn't here -- they're somewhere else (${theirRoom.name}), not within reach of this room.`;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=pursuit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pursuit.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/utilities/pursuit.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAY,EAAE,QAAc,EAAE,UAAkB;IACjF,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAElC,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe;QAAE,OAAO,SAAS,CAAC;IAEzD,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,CAAC;IAEzC,4DAA4D;IAC5D,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QACzD,IAAI,IAAI,YAAY,IAAI,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,SAAS,EAAE,CAAC;YACtE,IAAI,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;gBACzB,OAAO,GAAG,MAAM,CAAC,IAAI,sCAAsC,SAAS,YAAY,SAAS,CAAC,IAAI,oBAAoB,CAAC;YACrH,CAAC;YACD,OAAO,GAAG,MAAM,CAAC,IAAI,4BAA4B,SAAS,QAAQ,SAAS,CAAC,IAAI,UAAU,SAAS,mBAAmB,CAAC;QACzH,CAAC;IACH,CAAC;IAED,OAAO,GAAG,MAAM,CAAC,IAAI,0CAA0C,SAAS,CAAC,IAAI,mCAAmC,CAAC;AACnH,CAAC"}
|
|
@@ -6,6 +6,12 @@ import PlayCommand from './play.command.js';
|
|
|
6
6
|
import { Command } from '../../command.js';
|
|
7
7
|
import { Actions } from '../../tools/actions/actions.class.js';
|
|
8
8
|
import { Log } from '../../tools/log/log.class.js';
|
|
9
|
+
import { SceneSeedGenerator } from './sideQuest/factories/scene-seed-generator.js';
|
|
10
|
+
import { ARCHETYPES, getArchetype, outfitArchetype } from './sideQuest/archetypes.js';
|
|
11
|
+
import { Player } from './sideQuest/models/player.js';
|
|
12
|
+
import { Room } from './sideQuest/models/room.js';
|
|
13
|
+
import { Logger } from './sideQuest/utilities/logger.js';
|
|
14
|
+
import { AI } from '../../tools/ai/ai.class.js';
|
|
9
15
|
const shadowrunConfig1 = require('./sideQuest/scenes/scene1.json');
|
|
10
16
|
const shadowrunConfig2 = require('./sideQuest/scenes/scene2.json');
|
|
11
17
|
import { fileURLToPath } from 'url';
|
|
@@ -24,20 +30,85 @@ Command.create({
|
|
|
24
30
|
}, async function (args, opts) {
|
|
25
31
|
try {
|
|
26
32
|
const makaDir = this.fsi.getGlobalMakaDir();
|
|
27
|
-
|
|
33
|
+
// Each play session gets its own directory (timestamp-named) instead of
|
|
34
|
+
// appending to one ever-growing log file. Generated AI scenes are saved
|
|
35
|
+
// alongside the log for that same session -- see SceneSeedGenerator.
|
|
36
|
+
const sessionId = new Date().toISOString().replace(/[:.]/g, '-');
|
|
37
|
+
const sessionDir = path.join(makaDir, 'side-quest-sessions', sessionId);
|
|
38
|
+
const logFilePath = path.join(sessionDir, 'game.log');
|
|
28
39
|
const playerName = (opts.player) ? opts.player : await Actions.ask('What is your name? [Maka]') || 'Maka';
|
|
40
|
+
// Pick who you are before what the job is -- the archetype spread
|
|
41
|
+
// becomes the player's full attribute sheet (see archetypes.ts), and
|
|
42
|
+
// the mage archetypes are the road to playing Awakened.
|
|
43
|
+
const archetypeName = await Actions.select({
|
|
44
|
+
message: 'Who are you on the street?',
|
|
45
|
+
name: 'archetypeName',
|
|
46
|
+
choices: ARCHETYPES.map(a => ({ name: `${a.name} -- ${a.description}`, value: a.name })),
|
|
47
|
+
});
|
|
29
48
|
const gameOptions = {
|
|
30
49
|
...opts,
|
|
31
|
-
logFilePath
|
|
50
|
+
logFilePath,
|
|
51
|
+
// Matches the convention NPC dialogue already uses elsewhere in this
|
|
52
|
+
// game: AI features turn on automatically whenever a key is
|
|
53
|
+
// configured, with no separate flag for the player to remember.
|
|
54
|
+
// AI.isConfigured() picks whichever provider is set up (OpenAI or
|
|
55
|
+
// Anthropic -- see tools/ai/ai.class.ts).
|
|
56
|
+
useAi: AI.isConfigured(),
|
|
57
|
+
archetype: archetypeName
|
|
32
58
|
};
|
|
33
|
-
const
|
|
59
|
+
const GENERATE_NEW_RUN = 'GENERATE_NEW_RUN';
|
|
60
|
+
// The hub (Tacoma Purple Haze) leads the list -- it's the intended
|
|
61
|
+
// starting ground: live there, call the fixer for runs, come home
|
|
62
|
+
// between them. Seattle Underground stays as the compact smoke-test
|
|
63
|
+
// scene (and the generation-failure fallback below).
|
|
64
|
+
const choices = [
|
|
65
|
+
{ name: shadowrunConfig2.name, value: shadowrunConfig2 },
|
|
66
|
+
{ name: shadowrunConfig1.name, value: shadowrunConfig1 },
|
|
67
|
+
];
|
|
68
|
+
if (gameOptions.useAi) {
|
|
69
|
+
choices.push({ name: 'Generate a new Run (AI)', value: GENERATE_NEW_RUN });
|
|
70
|
+
}
|
|
71
|
+
let sideQuestAnswer = await Actions.select({
|
|
34
72
|
message: 'Which Run would you like to play?',
|
|
35
73
|
name: 'sideQuestAnswer',
|
|
36
|
-
choices
|
|
37
|
-
{ name: shadowrunConfig1.name, value: shadowrunConfig1 },
|
|
38
|
-
{ name: shadowrunConfig2.name, value: shadowrunConfig2 },
|
|
39
|
-
]
|
|
74
|
+
choices
|
|
40
75
|
});
|
|
76
|
+
if (sideQuestAnswer === GENERATE_NEW_RUN) {
|
|
77
|
+
const spinner = Log.withSpinner();
|
|
78
|
+
spinner.start('Generating a new Run');
|
|
79
|
+
// A throwaway player built from the picked archetype, purely as the
|
|
80
|
+
// generation dossier source (see buildPlayerDossier) -- the REAL
|
|
81
|
+
// player is constructed in the spawned ui.js child from the same
|
|
82
|
+
// archetype options, so this one is never played. Without it, the
|
|
83
|
+
// first run of a session was archetype-blind: a real Decker session
|
|
84
|
+
// drew a job with no Matrix content anywhere. The Logger singleton
|
|
85
|
+
// must be primed first -- Player's constructor grabs it, and its
|
|
86
|
+
// first getInstance() call demands a screen (the same headless stub
|
|
87
|
+
// SceneSeedGenerator itself uses).
|
|
88
|
+
Logger.getInstance(logFilePath, { children: [], render: () => { } });
|
|
89
|
+
const archetype = getArchetype(gameOptions.archetype);
|
|
90
|
+
const dossierPlayer = new Player({
|
|
91
|
+
playerName,
|
|
92
|
+
startLocation: new Room({ name: 'Nexus', description: 'A featureless room where lost souls drift.', roomType: 'void' }),
|
|
93
|
+
combat: archetype.combat,
|
|
94
|
+
});
|
|
95
|
+
outfitArchetype(dossierPlayer, archetype);
|
|
96
|
+
const generated = await SceneSeedGenerator.generate(logFilePath, 1, undefined, dossierPlayer);
|
|
97
|
+
if (generated) {
|
|
98
|
+
spinner.success(`-- "${generated.name}" is ready.`);
|
|
99
|
+
sideQuestAnswer = generated;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// The blessed UI takes over the terminal's alternate screen buffer
|
|
103
|
+
// as soon as the child process starts, so a spinner message printed
|
|
104
|
+
// here would be swallowed before the player ever sees it -- carry
|
|
105
|
+
// it into the game itself instead.
|
|
106
|
+
const notice = 'Could not generate a new AI run this time (after 3 attempts) -- starting "Seattle Underground" instead.';
|
|
107
|
+
spinner.failure(notice);
|
|
108
|
+
sideQuestAnswer = shadowrunConfig1;
|
|
109
|
+
gameOptions.generationNotice = notice;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
41
112
|
if (playerName && sideQuestAnswer) {
|
|
42
113
|
// Spawn a new process to run your main function
|
|
43
114
|
const child = spawn('node', [path.resolve(__dirname, './sideQuest/ui.js'), playerName, JSON.stringify(sideQuestAnswer), JSON.stringify(gameOptions)], {
|