@zhin.js/plugin-text-adventure 1.0.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.
Files changed (98) hide show
  1. package/README.md +92 -0
  2. package/lib/achievements.d.ts +24 -0
  3. package/lib/achievements.d.ts.map +1 -0
  4. package/lib/achievements.js +62 -0
  5. package/lib/achievements.js.map +1 -0
  6. package/lib/adv-command.d.ts +5 -0
  7. package/lib/adv-command.d.ts.map +1 -0
  8. package/lib/adv-command.js +55 -0
  9. package/lib/adv-command.js.map +1 -0
  10. package/lib/commands.d.ts +6 -0
  11. package/lib/commands.d.ts.map +1 -0
  12. package/lib/commands.js +96 -0
  13. package/lib/commands.js.map +1 -0
  14. package/lib/game-flow.d.ts +9 -0
  15. package/lib/game-flow.d.ts.map +1 -0
  16. package/lib/game-flow.js +134 -0
  17. package/lib/game-flow.js.map +1 -0
  18. package/lib/hub-register.d.ts +5 -0
  19. package/lib/hub-register.d.ts.map +1 -0
  20. package/lib/hub-register.js +25 -0
  21. package/lib/hub-register.js.map +1 -0
  22. package/lib/index.d.ts +2 -0
  23. package/lib/index.d.ts.map +1 -0
  24. package/lib/index.js +37 -0
  25. package/lib/index.js.map +1 -0
  26. package/lib/models.d.ts +45 -0
  27. package/lib/models.d.ts.map +1 -0
  28. package/lib/models.js +37 -0
  29. package/lib/models.js.map +1 -0
  30. package/lib/profile-format.d.ts +6 -0
  31. package/lib/profile-format.d.ts.map +1 -0
  32. package/lib/profile-format.js +64 -0
  33. package/lib/profile-format.js.map +1 -0
  34. package/lib/profile-parse.d.ts +4 -0
  35. package/lib/profile-parse.d.ts.map +1 -0
  36. package/lib/profile-parse.js +21 -0
  37. package/lib/profile-parse.js.map +1 -0
  38. package/lib/profile-service.d.ts +17 -0
  39. package/lib/profile-service.d.ts.map +1 -0
  40. package/lib/profile-service.js +124 -0
  41. package/lib/profile-service.js.map +1 -0
  42. package/lib/scene-view.d.ts +4 -0
  43. package/lib/scene-view.d.ts.map +1 -0
  44. package/lib/scene-view.js +32 -0
  45. package/lib/scene-view.js.map +1 -0
  46. package/lib/session-service.d.ts +22 -0
  47. package/lib/session-service.d.ts.map +1 -0
  48. package/lib/session-service.js +100 -0
  49. package/lib/session-service.js.map +1 -0
  50. package/lib/story-catalog.d.ts +13 -0
  51. package/lib/story-catalog.d.ts.map +1 -0
  52. package/lib/story-catalog.js +104 -0
  53. package/lib/story-catalog.js.map +1 -0
  54. package/lib/story-items.d.ts +5 -0
  55. package/lib/story-items.d.ts.map +1 -0
  56. package/lib/story-items.js +23 -0
  57. package/lib/story-items.js.map +1 -0
  58. package/lib/story-path.d.ts +3 -0
  59. package/lib/story-path.d.ts.map +1 -0
  60. package/lib/story-path.js +66 -0
  61. package/lib/story-path.js.map +1 -0
  62. package/lib/story-scenes.d.ts +9 -0
  63. package/lib/story-scenes.d.ts.map +1 -0
  64. package/lib/story-scenes.js +478 -0
  65. package/lib/story-scenes.js.map +1 -0
  66. package/lib/story-transitions.d.ts +3 -0
  67. package/lib/story-transitions.d.ts.map +1 -0
  68. package/lib/story-transitions.js +193 -0
  69. package/lib/story-transitions.js.map +1 -0
  70. package/lib/story-types.d.ts +30 -0
  71. package/lib/story-types.d.ts.map +1 -0
  72. package/lib/story-types.js +3 -0
  73. package/lib/story-types.js.map +1 -0
  74. package/lib/story.d.ts +21 -0
  75. package/lib/story.d.ts.map +1 -0
  76. package/lib/story.js +91 -0
  77. package/lib/story.js.map +1 -0
  78. package/package.json +54 -0
  79. package/plugin.yml +2 -0
  80. package/src/achievements.ts +90 -0
  81. package/src/adv-command.ts +72 -0
  82. package/src/commands.ts +133 -0
  83. package/src/game-flow.ts +196 -0
  84. package/src/hub-register.ts +32 -0
  85. package/src/index.ts +47 -0
  86. package/src/models.ts +84 -0
  87. package/src/profile-format.ts +76 -0
  88. package/src/profile-parse.ts +19 -0
  89. package/src/profile-service.ts +151 -0
  90. package/src/scene-view.ts +46 -0
  91. package/src/session-service.ts +113 -0
  92. package/src/story-catalog.ts +110 -0
  93. package/src/story-items.ts +24 -0
  94. package/src/story-path.ts +75 -0
  95. package/src/story-scenes.ts +508 -0
  96. package/src/story-transitions.ts +198 -0
  97. package/src/story-types.ts +33 -0
  98. package/src/story.ts +104 -0
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @zhin.js/plugin-text-adventure
2
+
3
+ 交互式**文字冒险** — 31 个可探索区域、15 种道具、15 种结局,完整流程 50+ 步。
4
+
5
+ ## 规模
6
+
7
+ | 维度 | 数量 |
8
+ |------|------|
9
+ | 可探索区域 | 31 |
10
+ | 终局结局 | 15 |
11
+ | 可收集道具 | 15 |
12
+ | 预估最长流程 | 50+ 步(含往返探索) |
13
+
14
+ ## 命令
15
+
16
+ `冒险` · `冒险 开始` · `冒险 继续` · `冒险 地图` · `冒险 成就` · `冒险 放弃`
17
+
18
+ (英文别名 `adv` 仍可用;也可发送 **`游戏`** 从大厅进入)
19
+
20
+ ## 进度与成就(用户绑定)
21
+
22
+ 探索进度与成就按 **玩家 ID** 持久化(`adv_profiles` 表),跨频道、跨局累计:
23
+
24
+ - 场景内顶部显示:`🗺️ 区域进度 · 🎬 结局图鉴 · 🏅 成就`
25
+ - `adv map` — 分区地图解锁(✅/⬜)+ 结局图鉴
26
+ - `adv achievements` — 15 项生涯成就
27
+ - 达成新成就时,终局场景会弹出解锁提示
28
+
29
+ ## 道具(15)
30
+
31
+ | 道具 | 用途 |
32
+ |------|------|
33
+ | 火把 | 照明、驱散幽影、烧蛛网 |
34
+ | 生锈短剑 | 淬炼后斩网/战斗 |
35
+ | 荧光草药 | 麻痹幽影、萃取灵药 |
36
+ | 治愈灵药 | 御寒涉水 |
37
+ | 幽蓝宝石 | 献祭换护符 / 换银币 |
38
+ | 残破卷轴 | 破解机关、星辉结局 |
39
+ | 锈蚀钥匙 | 书库铁门、回音巨门 |
40
+ | 精灵护符 | 升华后星辉归位 |
41
+ | 古银币 | 市集交易、冥桥通行费 |
42
+ | 仪式面具 | 面具加冕结局 |
43
+ | 月华碎片 | 护符升华、星守仪式 |
44
+ | 星图残页 | 月池共鸣、星守仪式 |
45
+ | 星象罗盘 | 暗影迷宫、回音巨门 |
46
+ | 回音铃 | 开启回音巨门捷径 |
47
+ | 银羽 | 涉水护体、冥桥/释魂 |
48
+
49
+ ## 区域地图(简)
50
+
51
+ ```
52
+ 入口 ─┬─ 石塔/星象台
53
+ ├─ 碎石堆(火把/短剑)
54
+ ├─ 秘径 → 庭院 ─┬─ 温室
55
+ │ └─ 书库 → 锻炉
56
+ └─ 走廊 ─┬─ 精灵泉
57
+ ├─ 壁画 → 低语画廊
58
+ ├─ 积水厅 → 废墟市集 → 冥桥 → 晶洞 → 月池 → 水下沉殿
59
+ ├─ 钟楼
60
+ └─ 墓穴 → 岔口 ─┬─ 封印密室 → 王座厅
61
+ ├─ 骸骨甬道 → 囚室 / 龛室
62
+ ├─ 蛛巢 → 暗影迷宫
63
+ └─ 回音巨门(捷径)
64
+ ```
65
+
66
+ ## 结局(15)
67
+
68
+ | 结局 | 触发 |
69
+ |------|------|
70
+ | 🌟 星辉归位 | 低语画廊 + 升华护符 + 泉灵 + 卷轴 → 王座献身 |
71
+ | 🌙 星守誓约 | 月池仪式(月华+星图+泉心) |
72
+ | 🎭 面具之王 | 王座厅面具加冕 |
73
+ | 🏆 遗迹宝藏 | 解封后取宝离开 |
74
+ | 📜 智者归途 | 封印错序「门→光→影」等 |
75
+ | 🕊️ 幽魂解脱 | 囚室银羽释魂 |
76
+ | 🌳 庭院隐者 | 庭院定居 |
77
+ | ☠️ 贪婪代价 | 贸然坐上王座 |
78
+ | 🌊 寒水沉眠 | 涉水失温 |
79
+ | 🕷️ 茧中亡魂 | 蛛巢败退 |
80
+ | 🌑 迷宫迷失 | 暗影迷宫乱走 |
81
+ | 🌅 生还 / 💀 陨落 / 🏠 怯懦 | 经典分支 |
82
+
83
+ ## 完美结局路线(剧透)
84
+
85
+ 1. 碎石:火把 + 短剑 → 石塔测绘 + 星图 + 星象台罗盘
86
+ 2. 走廊:壁画 → 低语画廊聆听 → 精灵泉对话 + 饮水
87
+ 3. 秘径庭院:草药 → 温室灵药 → 书库卷轴/钥匙 → 锻炉淬剑
88
+ 4. 钟楼取铃 → 墓穴驱影 → 骸骨甬道卷轴破机关 → 龛室答「星辰坠落」
89
+ 5. 积水厅 → 市集 → 冥桥 → 晶洞月华/银羽 → 月池星图共鸣 → 沉殿(可选星守)
90
+ 6. 岔口回音巨门或封印密室 → 光影门 → 王座厅星辉归位
91
+
92
+ 改 `src/story*.ts` 后执行 `pnpm --filter @zhin.js/plugin-text-adventure build`。
@@ -0,0 +1,24 @@
1
+ export interface AchievementDef {
2
+ id: string;
3
+ icon: string;
4
+ title: string;
5
+ description: string;
6
+ hidden?: boolean;
7
+ }
8
+ export interface AchievementContext {
9
+ visited: Set<string>;
10
+ endings: Set<string>;
11
+ itemsFound: Set<string>;
12
+ runsCompleted: number;
13
+ bestStepCount: number;
14
+ lastRun?: {
15
+ stepCount: number;
16
+ inventory: string[];
17
+ endingId: string;
18
+ };
19
+ }
20
+ export declare const ACHIEVEMENTS: AchievementDef[];
21
+ export declare function evaluateAchievements(unlocked: string[], ctx: AchievementContext): string[];
22
+ export declare function achievementById(id: string): AchievementDef | undefined;
23
+ export declare function totalAchievements(): number;
24
+ //# sourceMappingURL=achievements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"achievements.d.ts","sourceRoot":"","sources":["../src/achievements.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,eAAO,MAAM,YAAY,EAAE,cAAc,EAgBxC,CAAC;AA0BF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,EAAE,kBAAkB,GACtB,MAAM,EAAE,CAYV;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEtE;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
@@ -0,0 +1,62 @@
1
+ import { ITEM_IDS } from './story-items.js';
2
+ import { endingIds, playableSceneIds } from './story-catalog.js';
3
+ export const ACHIEVEMENTS = [
4
+ { id: 'first_footprint', icon: '👣', title: '初探者', description: '累计探索 5 个区域' },
5
+ { id: 'cartographer', icon: '🗺️', title: '制图师', description: '解锁半数区域(50%+)' },
6
+ { id: 'master_explorer', icon: '🧭', title: '全图行者', description: '解锁全部可探索区域' },
7
+ { id: 'ending_hunter', icon: '🎬', title: '结局猎人', description: '见证 3 种不同结局' },
8
+ { id: 'ending_collector', icon: '📚', title: '结局收藏家', description: '见证 8 种不同结局' },
9
+ { id: 'completionist', icon: '👑', title: '秘境通晓者', description: '见证全部 15 种结局' },
10
+ { id: 'star_ascendant', icon: '🌟', title: '星辉守忆', description: '达成「星辉归位」结局' },
11
+ { id: 'moon_guardian', icon: '🌙', title: '月池星守', description: '达成「星守誓约」结局' },
12
+ { id: 'mask_monarch', icon: '🎭', title: '面具之王', description: '达成「面具之王」结局' },
13
+ { id: 'item_hoarder', icon: '🎒', title: '拾荒大师', description: '生涯累计发现 10 种道具' },
14
+ { id: 'full_satchel', icon: '💎', title: '万象行囊', description: '生涯集齐全部 15 种道具' },
15
+ { id: 'marathon_runner', icon: '🏃', title: '马拉松探险', description: '单局步数达到 50+' },
16
+ { id: 'veteran', icon: '⚔️', title: '遗迹老兵', description: '完成 5 局冒险' },
17
+ { id: 'whisper_listener', icon: '👂', title: '低语聆听者', description: '进入低语画廊' },
18
+ { id: 'deep_diver', icon: '🌊', title: '沉殿潜行者', description: '抵达水下沉殿' },
19
+ ];
20
+ const PLAYABLE_TOTAL = playableSceneIds().length;
21
+ const ENDING_TOTAL = endingIds().length;
22
+ const HALF_REGIONS = Math.ceil(PLAYABLE_TOTAL / 2);
23
+ function checks() {
24
+ return {
25
+ first_footprint: (ctx) => ctx.visited.size >= 5,
26
+ cartographer: (ctx) => ctx.visited.size >= HALF_REGIONS,
27
+ master_explorer: (ctx) => ctx.visited.size >= PLAYABLE_TOTAL,
28
+ ending_hunter: (ctx) => ctx.endings.size >= 3,
29
+ ending_collector: (ctx) => ctx.endings.size >= 8,
30
+ completionist: (ctx) => ctx.endings.size >= ENDING_TOTAL,
31
+ star_ascendant: (ctx) => ctx.endings.has('ascension'),
32
+ moon_guardian: (ctx) => ctx.endings.has('star_keeper'),
33
+ mask_monarch: (ctx) => ctx.endings.has('mask_king'),
34
+ item_hoarder: (ctx) => ctx.itemsFound.size >= 10,
35
+ full_satchel: (ctx) => ITEM_IDS.every((id) => ctx.itemsFound.has(id)),
36
+ marathon_runner: (ctx) => (ctx.lastRun?.stepCount ?? ctx.bestStepCount) >= 50,
37
+ veteran: (ctx) => ctx.runsCompleted >= 5,
38
+ whisper_listener: (ctx) => ctx.visited.has('whisper_gallery'),
39
+ deep_diver: (ctx) => ctx.visited.has('sunken_shrine'),
40
+ };
41
+ }
42
+ export function evaluateAchievements(unlocked, ctx) {
43
+ const have = new Set(unlocked);
44
+ const run = checks();
45
+ const newly = [];
46
+ for (const def of ACHIEVEMENTS) {
47
+ if (have.has(def.id))
48
+ continue;
49
+ if (run[def.id]?.(ctx)) {
50
+ have.add(def.id);
51
+ newly.push(def.id);
52
+ }
53
+ }
54
+ return newly;
55
+ }
56
+ export function achievementById(id) {
57
+ return ACHIEVEMENTS.find((a) => a.id === id);
58
+ }
59
+ export function totalAchievements() {
60
+ return ACHIEVEMENTS.length;
61
+ }
62
+ //# sourceMappingURL=achievements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"achievements.js","sourceRoot":"","sources":["../src/achievements.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Message, Plugin } from 'zhin.js';
2
+ import type { GameServices } from './session-service.js';
3
+ export declare const ADV_HELP: string;
4
+ export declare function runAdvCommand(plugin: Plugin, services: GameServices, message: Message<any>, action: string): Promise<string | undefined>;
5
+ //# sourceMappingURL=adv-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adv-command.d.ts","sourceRoot":"","sources":["../src/adv-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAQ/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,eAAO,MAAM,QAAQ,QAUT,CAAC;AAEb,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA4C7B"}
@@ -0,0 +1,55 @@
1
+ import { channelKey } from '@zhin.js/game-shared';
2
+ import { continueAdventure, sessionSummary, startAdventure, } from './game-flow.js';
3
+ import { formatAchievements, formatMapProgress, formatProgressCompact } from './profile-format.js';
4
+ export const ADV_HELP = [
5
+ '秘境探险 · 文字冒险',
6
+ '冒险 / adv — 帮助与状态',
7
+ '冒险 开始 — 开始新冒险',
8
+ '冒险 继续 — 刷新当前场景',
9
+ '冒险 地图 — 全图探索进度',
10
+ '冒险 成就 — 成就列表',
11
+ '冒险 放弃 — 放弃当前冒险',
12
+ '',
13
+ '推进方式:点击选项按钮,或回复数字(1、2…)',
14
+ ].join('\n');
15
+ export async function runAdvCommand(plugin, services, message, action) {
16
+ const ch = channelKey(message);
17
+ const userId = message.$sender.id;
18
+ const userName = message.$sender.name?.trim() || userId;
19
+ if (!action || action === 'help') {
20
+ const active = await services.sessions.getActiveByChannel(ch);
21
+ const profile = await services.profiles.getOrCreate(userId, userName);
22
+ const lines = [ADV_HELP, '', formatProgressCompact(profile), ''];
23
+ if (active) {
24
+ lines.push('— 当前频道 —');
25
+ lines.push(sessionSummary(active));
26
+ }
27
+ else {
28
+ lines.push('暂无冒险,发送「冒险 开始」或从游戏大厅进入。');
29
+ }
30
+ return lines.join('\n');
31
+ }
32
+ if (action === 'start') {
33
+ return startAdventure(plugin, services, message);
34
+ }
35
+ if (action === 'continue') {
36
+ return continueAdventure(plugin, services, message);
37
+ }
38
+ if (action === 'map') {
39
+ const profile = await services.profiles.getOrCreate(userId, userName);
40
+ return formatMapProgress(profile);
41
+ }
42
+ if (action === 'achievements' || action === 'achievement') {
43
+ const profile = await services.profiles.getOrCreate(userId, userName);
44
+ return formatAchievements(profile);
45
+ }
46
+ if (action === 'quit') {
47
+ const row = await services.sessions.getActiveForUser(ch, userId);
48
+ if (!row)
49
+ return '你没有进行中的冒险。';
50
+ await services.sessions.updateSession(row.id, { status: 'aborted' });
51
+ return '你已放弃冒险。';
52
+ }
53
+ return `未知子命令:${action}\n\n${ADV_HELP}`;
54
+ }
55
+ //# sourceMappingURL=adv-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adv-command.js","sourceRoot":"","sources":["../src/adv-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type Plugin } from 'zhin.js';
2
+ import type { GameServices } from './session-service.js';
3
+ export declare function registerCommands(plugin: Plugin, getServices: () => GameServices | null): void;
4
+ export declare function registerInteractive(plugin: Plugin, getServices: () => GameServices | null): void;
5
+ export declare function registerTextFallback(plugin: Plugin, getServices: () => GameServices | null): void;
6
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAUjB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAqDzD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,YAAY,GAAG,IAAI,GACrC,IAAI,CAIN;AAgBD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,YAAY,GAAG,IAAI,GACrC,IAAI,CAIN;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,YAAY,GAAG,IAAI,GACrC,IAAI,CA4BN"}
@@ -0,0 +1,96 @@
1
+ import { MessageCommand, getActionFromMessage, } from 'zhin.js';
2
+ import { buildChoiceFallbackMap, channelKey, normalizeAdvAction, parseChoicePayload, } from '@zhin.js/game-shared';
3
+ import { runAdvCommand } from './adv-command.js';
4
+ import { handleChoice } from './game-flow.js';
5
+ import { ADV_PREFIX, getScene, stateFromSession, visibleChoices } from './story.js';
6
+ function registerAdvPattern(plugin, pattern, desc, getServices) {
7
+ plugin.addCommand(new MessageCommand(pattern)
8
+ .desc(desc)
9
+ .action(async (message, result) => {
10
+ const services = getServices();
11
+ if (!services)
12
+ return '文字冒险需要启用 database 配置。';
13
+ const raw = result.params.action ?? '';
14
+ const action = normalizeAdvAction(raw);
15
+ return runAdvCommand(plugin, services, message, action);
16
+ }));
17
+ }
18
+ async function resolveAdvChoice(message, services) {
19
+ const action = getActionFromMessage(message);
20
+ if (!action)
21
+ return null;
22
+ const fromPayload = parseChoicePayload(action.payload, ADV_PREFIX);
23
+ if (fromPayload) {
24
+ return { sessionId: fromPayload.sessionId, choiceId: fromPayload.choiceId };
25
+ }
26
+ const choiceId = action.id || action.payload;
27
+ if (!choiceId)
28
+ return null;
29
+ const ch = channelKey(message);
30
+ const session = await services.sessions.getActiveForUser(ch, message.$sender.id)
31
+ ?? (action.sourceMessageId
32
+ ? await services.sessions.getActiveByBoardMessageId(action.sourceMessageId)
33
+ : null);
34
+ if (!session || session.channel_key !== ch)
35
+ return null;
36
+ const scene = getScene(session.scene_id);
37
+ if (!scene)
38
+ return null;
39
+ const state = stateFromSession(session);
40
+ const choices = visibleChoices(scene, state);
41
+ if (!choices.some((c) => c.id === choiceId))
42
+ return null;
43
+ return { sessionId: session.id, choiceId };
44
+ }
45
+ export function registerCommands(plugin, getServices) {
46
+ registerAdvPattern(plugin, 'adv [action:word]', '秘境探险(adv)', getServices);
47
+ registerAdvPattern(plugin, '冒险 [action:word]', '秘境探险(中文)', getServices);
48
+ registerAdvPattern(plugin, '秘境 [action:word]', '秘境探险(简称)', getServices);
49
+ }
50
+ async function handleAdvAction(plugin, getServices, message) {
51
+ const services = getServices();
52
+ if (!services)
53
+ return false;
54
+ const choice = await resolveAdvChoice(message, services);
55
+ if (!choice)
56
+ return false;
57
+ const err = await handleChoice(plugin, services, message, choice.sessionId, choice.choiceId);
58
+ if (err)
59
+ await message.$reply?.(err);
60
+ return true;
61
+ }
62
+ export function registerInteractive(plugin, getServices) {
63
+ plugin.registerInteractiveHandler(`${ADV_PREFIX}:`, (message) => handleAdvAction(plugin, getServices, message));
64
+ }
65
+ export function registerTextFallback(plugin, getServices) {
66
+ plugin.addMiddleware(async (message, next) => {
67
+ const services = getServices();
68
+ if (!services)
69
+ return next();
70
+ const action = getActionFromMessage(message);
71
+ if (action?.payload.startsWith(`${ADV_PREFIX}:`))
72
+ return next();
73
+ const ch = channelKey(message);
74
+ const session = await services.sessions.getActiveForUser(ch, message.$sender.id);
75
+ if (!session)
76
+ return next();
77
+ const raw = message.$raw?.trim() ?? '';
78
+ const n = /^(\d+)$/.exec(raw);
79
+ if (!n)
80
+ return next();
81
+ const scene = getScene(session.scene_id);
82
+ if (!scene)
83
+ return next();
84
+ const state = stateFromSession(session);
85
+ const choices = visibleChoices(scene, state);
86
+ const map = buildChoiceFallbackMap(ADV_PREFIX, session.id, choices);
87
+ const payload = map[n[1]];
88
+ const parsed = payload ? parseChoicePayload(payload, ADV_PREFIX) : null;
89
+ if (!parsed || parsed.sessionId !== session.id)
90
+ return next();
91
+ const err = await handleChoice(plugin, services, message, session.id, parsed.choiceId);
92
+ if (err)
93
+ await message.$reply?.(err);
94
+ });
95
+ }
96
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Message, Plugin } from 'zhin.js';
2
+ import type { AdvProfileRow, AdvSessionRow } from './models.js';
3
+ import type { GameServices } from './session-service.js';
4
+ export declare function sendOrEditScene(plugin: Plugin, services: GameServices, message: Message<any>, session: AdvSessionRow, extraNarrative?: string, profile?: AdvProfileRow): Promise<string>;
5
+ export declare function startAdventure(plugin: Plugin, services: GameServices, message: Message<any>): Promise<string | undefined>;
6
+ export declare function handleChoice(plugin: Plugin, services: GameServices, message: Message<any>, sessionId: string, choiceId: string): Promise<string | null>;
7
+ export declare function continueAdventure(plugin: Plugin, services: GameServices, message: Message<any>): Promise<string>;
8
+ export declare function sessionSummary(session: AdvSessionRow): string;
9
+ //# sourceMappingURL=game-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-flow.d.ts","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAShE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,OAAO,EAAE,aAAa,EACtB,cAAc,SAAK,EACnB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoB7B;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiFxB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GACpB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAe7D"}
@@ -0,0 +1,134 @@
1
+ import { formatNewAchievements } from './profile-format.js';
2
+ import { buildSceneInteractive } from './scene-view.js';
3
+ import { applyChoiceResult, getScene, resolveChoice, stateFromSession, } from './story.js';
4
+ export async function sendOrEditScene(plugin, services, message, session, extraNarrative = '', profile) {
5
+ const prof = profile
6
+ ?? await services.profiles.getOrCreate(session.player_id, session.player_name);
7
+ const content = buildSceneInteractive(session, extraNarrative, prof);
8
+ if (!content) {
9
+ return '场景数据异常,请 adv quit 后重新开始。';
10
+ }
11
+ const adapter = plugin.root.inject(message.$adapter);
12
+ if (session.board_message_id) {
13
+ const msgId = await adapter.editMessage({
14
+ messageId: session.board_message_id,
15
+ context: String(message.$adapter),
16
+ endpoint: message.$endpoint,
17
+ id: message.$channel.id,
18
+ type: message.$channel.type,
19
+ content,
20
+ });
21
+ if (msgId !== session.board_message_id) {
22
+ await services.sessions.updateSession(session.id, { board_message_id: msgId });
23
+ }
24
+ return msgId;
25
+ }
26
+ const msgId = await message.$reply?.(content);
27
+ if (msgId) {
28
+ await services.sessions.updateSession(session.id, { board_message_id: msgId });
29
+ }
30
+ return msgId ?? '';
31
+ }
32
+ export async function startAdventure(plugin, services, message) {
33
+ const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
34
+ const active = await services.sessions.getActiveByChannel(ch);
35
+ if (active) {
36
+ if (active.player_id === message.$sender.id) {
37
+ return '你已有进行中的冒险。发送 adv continue 继续,或 adv quit 放弃。';
38
+ }
39
+ return `本频道已有玩家 ${active.player_name} 的冒险进行中。`;
40
+ }
41
+ await services.profiles.onRunStarted(message.$sender.id, message.$sender.name?.trim() || message.$sender.id);
42
+ const session = await services.sessions.createSession(message);
43
+ await services.profiles.onStep(message.$sender.id, session.player_name, 'start', []);
44
+ await sendOrEditScene(plugin, services, message, session);
45
+ return undefined;
46
+ }
47
+ export async function handleChoice(plugin, services, message, sessionId, choiceId) {
48
+ const session = await services.sessions.getById(sessionId);
49
+ if (!session || session.status !== 'active') {
50
+ return '冒险不存在或已结束。';
51
+ }
52
+ if (session.player_id !== message.$sender.id) {
53
+ return '这是别人的冒险。';
54
+ }
55
+ const state = stateFromSession(session);
56
+ const scene = getScene(state.sceneId);
57
+ if (!scene)
58
+ return '场景丢失,请 adv quit 后重新开始。';
59
+ if (scene.terminal && choiceId !== 'restart') {
60
+ return '本局已结束,请点击「再玩一次」或发送 adv start。';
61
+ }
62
+ if (choiceId === 'restart') {
63
+ await services.sessions.updateSession(session.id, {
64
+ scene_id: 'start',
65
+ hp: 100,
66
+ inventory: '[]',
67
+ flags: '{}',
68
+ ending_id: '',
69
+ status: 'active',
70
+ step_count: 0,
71
+ board_message_id: '',
72
+ });
73
+ const updated = (await services.sessions.getById(session.id));
74
+ await services.profiles.onStep(updated.player_id, updated.player_name, 'start', []);
75
+ await sendOrEditScene(plugin, services, message, updated);
76
+ return null;
77
+ }
78
+ const result = resolveChoice(state, choiceId);
79
+ if (!result)
80
+ return '该选项不可用。';
81
+ const next = applyChoiceResult(state, result);
82
+ const nextScene = getScene(next.sceneId);
83
+ const completed = !!nextScene?.terminal || next.hp <= 0;
84
+ if (next.hp <= 0 && !nextScene?.terminal) {
85
+ next.sceneId = 'defeat';
86
+ }
87
+ const finalScene = getScene(next.sceneId);
88
+ const status = completed || finalScene?.terminal ? 'completed' : 'active';
89
+ const newStepCount = session.step_count + 1;
90
+ await services.sessions.updateSession(session.id, {
91
+ scene_id: next.sceneId,
92
+ hp: next.hp,
93
+ inventory: JSON.stringify(next.inventory),
94
+ flags: JSON.stringify(next.flags),
95
+ ending_id: next.endingId,
96
+ status,
97
+ step_count: newStepCount,
98
+ });
99
+ const endingId = next.endingId || (finalScene?.terminal ? next.sceneId : '');
100
+ let newAchievements = await services.profiles.onStep(session.player_id, session.player_name, next.sceneId, next.inventory);
101
+ if (status === 'completed') {
102
+ const more = await services.profiles.onRunCompleted(session.player_id, session.player_name, endingId, newStepCount, next.inventory);
103
+ newAchievements = [...new Set([...newAchievements, ...more])];
104
+ }
105
+ const updated = (await services.sessions.getById(session.id));
106
+ const extra = formatNewAchievements(newAchievements);
107
+ await sendOrEditScene(plugin, services, message, updated, extra);
108
+ return null;
109
+ }
110
+ export async function continueAdventure(plugin, services, message) {
111
+ const session = await services.sessions.getActiveForUser(`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`, message.$sender.id);
112
+ if (!session)
113
+ return '你没有进行中的冒险,发送 adv start 开始。';
114
+ await sendOrEditScene(plugin, services, message, session);
115
+ return '已刷新当前场景。';
116
+ }
117
+ export function sessionSummary(session) {
118
+ const scene = getScene(session.scene_id);
119
+ const state = stateFromSession(session);
120
+ const items = state.inventory;
121
+ const lines = [
122
+ `冒险 ${session.id}`,
123
+ `玩家:${session.player_name}`,
124
+ `场景:${scene?.id ?? session.scene_id}`,
125
+ `生命:${session.hp}`,
126
+ `物品:${items.length ? items.join(', ') : '无'}`,
127
+ `步数:${session.step_count}`,
128
+ `状态:${session.status}`,
129
+ ];
130
+ if (session.ending_id)
131
+ lines.push(`结局:${session.ending_id}`);
132
+ return lines.join('\n');
133
+ }
134
+ //# sourceMappingURL=game-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { ADV_HELP } from './adv-command.js';
2
+ import type { GameServices } from './session-service.js';
3
+ export declare function registerAdvHub(getServices: () => GameServices | null): () => void;
4
+ export { ADV_HELP };
5
+ //# sourceMappingURL=hub-register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub-register.d.ts","sourceRoot":"","sources":["../src/hub-register.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,YAAY,GAAG,IAAI,GAAG,MAAM,IAAI,CAmBjF;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { registerGame } from '@zhin.js/game-shared';
2
+ import { runAdvCommand, ADV_HELP } from './adv-command.js';
3
+ export function registerAdvHub(getServices) {
4
+ return registerGame({
5
+ id: 'adv',
6
+ title: '秘境探险',
7
+ icon: '🗺️',
8
+ description: '文字冒险,31 区域 · 15 结局 · 成就收集',
9
+ menus: [
10
+ { id: 'start', label: '🚪 开始冒险', style: 'primary' },
11
+ { id: 'continue', label: '🔄 继续冒险' },
12
+ { id: 'map', label: '🗺️ 探索进度' },
13
+ { id: 'achievements', label: '🏅 成就' },
14
+ { id: 'help', label: '📖 玩法说明' },
15
+ ],
16
+ runAction: async (actionId, ctx) => {
17
+ const services = getServices();
18
+ if (!services)
19
+ return '文字冒险需要启用 database 配置。';
20
+ return runAdvCommand(ctx.plugin, services, ctx.message, actionId);
21
+ },
22
+ });
23
+ }
24
+ export { ADV_HELP };
25
+ //# sourceMappingURL=hub-register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub-register.js","sourceRoot":"","sources":["../src/hub-register.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG3D,MAAM,UAAU,cAAc,CAAC,WAAsC;IACnE,OAAO,YAAY,CAAC;QAClB,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,KAAK;QACX,WAAW,EAAE,2BAA2B;QACxC,KAAK,EAAE;YACL,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;YACnD,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE;YACpC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE;YAChC,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE;YACtC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE;SACjC;QACD,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,QAAQ;gBAAE,OAAO,uBAAuB,CAAC;YAC9C,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACpE,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/lib/index.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @zhin.js/plugin-text-adventure — 交互式文字冒险
3
+ *
4
+ * ```yaml
5
+ * plugins:
6
+ * - "@zhin.js/plugin-text-adventure"
7
+ * database:
8
+ * dialect: sqlite
9
+ * storage: ./data/zhin.db
10
+ * ```
11
+ */
12
+ import { Cron, formatCompact, usePlugin } from 'zhin.js';
13
+ import { registerModels } from './models.js';
14
+ import { createServices, resolveGameDatabase, } from './session-service.js';
15
+ import { registerCommands, registerInteractive, registerTextFallback } from './commands.js';
16
+ import { registerAdvHub } from './hub-register.js';
17
+ const plugin = usePlugin();
18
+ const { logger, useContext, addCron } = plugin;
19
+ registerModels(plugin);
20
+ let services = null;
21
+ useContext('database', (dbFeature) => {
22
+ services = createServices(resolveGameDatabase(dbFeature));
23
+ logger.info(formatCompact({ 模块: '文字冒险', 数据模型: '已就绪' }));
24
+ });
25
+ registerAdvHub(() => services);
26
+ registerCommands(plugin, () => services);
27
+ registerInteractive(plugin, () => services);
28
+ registerTextFallback(plugin, () => services);
29
+ addCron(new Cron('0 */15 * * * *', async () => {
30
+ if (!services)
31
+ return;
32
+ const n = await services.sessions.abortStale(60 * 60 * 1000);
33
+ if (n > 0)
34
+ logger.debug(formatCompact({ 文字冒险: '清理超时局', count: n }));
35
+ }));
36
+ logger.info(formatCompact({ 模块: '文字冒险', 状态: '已加载' }));
37
+ //# sourceMappingURL=index.js.map