@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.
- package/README.md +92 -0
- package/lib/achievements.d.ts +24 -0
- package/lib/achievements.d.ts.map +1 -0
- package/lib/achievements.js +62 -0
- package/lib/achievements.js.map +1 -0
- package/lib/adv-command.d.ts +5 -0
- package/lib/adv-command.d.ts.map +1 -0
- package/lib/adv-command.js +55 -0
- package/lib/adv-command.js.map +1 -0
- package/lib/commands.d.ts +6 -0
- package/lib/commands.d.ts.map +1 -0
- package/lib/commands.js +96 -0
- package/lib/commands.js.map +1 -0
- package/lib/game-flow.d.ts +9 -0
- package/lib/game-flow.d.ts.map +1 -0
- package/lib/game-flow.js +134 -0
- package/lib/game-flow.js.map +1 -0
- package/lib/hub-register.d.ts +5 -0
- package/lib/hub-register.d.ts.map +1 -0
- package/lib/hub-register.js +25 -0
- package/lib/hub-register.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +37 -0
- package/lib/index.js.map +1 -0
- package/lib/models.d.ts +45 -0
- package/lib/models.d.ts.map +1 -0
- package/lib/models.js +37 -0
- package/lib/models.js.map +1 -0
- package/lib/profile-format.d.ts +6 -0
- package/lib/profile-format.d.ts.map +1 -0
- package/lib/profile-format.js +64 -0
- package/lib/profile-format.js.map +1 -0
- package/lib/profile-parse.d.ts +4 -0
- package/lib/profile-parse.d.ts.map +1 -0
- package/lib/profile-parse.js +21 -0
- package/lib/profile-parse.js.map +1 -0
- package/lib/profile-service.d.ts +17 -0
- package/lib/profile-service.d.ts.map +1 -0
- package/lib/profile-service.js +124 -0
- package/lib/profile-service.js.map +1 -0
- package/lib/scene-view.d.ts +4 -0
- package/lib/scene-view.d.ts.map +1 -0
- package/lib/scene-view.js +32 -0
- package/lib/scene-view.js.map +1 -0
- package/lib/session-service.d.ts +22 -0
- package/lib/session-service.d.ts.map +1 -0
- package/lib/session-service.js +100 -0
- package/lib/session-service.js.map +1 -0
- package/lib/story-catalog.d.ts +13 -0
- package/lib/story-catalog.d.ts.map +1 -0
- package/lib/story-catalog.js +104 -0
- package/lib/story-catalog.js.map +1 -0
- package/lib/story-items.d.ts +5 -0
- package/lib/story-items.d.ts.map +1 -0
- package/lib/story-items.js +23 -0
- package/lib/story-items.js.map +1 -0
- package/lib/story-path.d.ts +3 -0
- package/lib/story-path.d.ts.map +1 -0
- package/lib/story-path.js +66 -0
- package/lib/story-path.js.map +1 -0
- package/lib/story-scenes.d.ts +9 -0
- package/lib/story-scenes.d.ts.map +1 -0
- package/lib/story-scenes.js +478 -0
- package/lib/story-scenes.js.map +1 -0
- package/lib/story-transitions.d.ts +3 -0
- package/lib/story-transitions.d.ts.map +1 -0
- package/lib/story-transitions.js +193 -0
- package/lib/story-transitions.js.map +1 -0
- package/lib/story-types.d.ts +30 -0
- package/lib/story-types.d.ts.map +1 -0
- package/lib/story-types.js +3 -0
- package/lib/story-types.js.map +1 -0
- package/lib/story.d.ts +21 -0
- package/lib/story.d.ts.map +1 -0
- package/lib/story.js +91 -0
- package/lib/story.js.map +1 -0
- package/package.json +54 -0
- package/plugin.yml +2 -0
- package/src/achievements.ts +90 -0
- package/src/adv-command.ts +72 -0
- package/src/commands.ts +133 -0
- package/src/game-flow.ts +196 -0
- package/src/hub-register.ts +32 -0
- package/src/index.ts +47 -0
- package/src/models.ts +84 -0
- package/src/profile-format.ts +76 -0
- package/src/profile-parse.ts +19 -0
- package/src/profile-service.ts +151 -0
- package/src/scene-view.ts +46 -0
- package/src/session-service.ts +113 -0
- package/src/story-catalog.ts +110 -0
- package/src/story-items.ts +24 -0
- package/src/story-path.ts +75 -0
- package/src/story-scenes.ts +508 -0
- package/src/story-transitions.ts +198 -0
- package/src/story-types.ts +33 -0
- package/src/story.ts +104 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
import type { GameState, Scene } from './story-types.js';
|
|
2
|
+
|
|
3
|
+
const T = (lines: string[]) => lines.join('\n');
|
|
4
|
+
|
|
5
|
+
/** 非终局场景 28 个 + 终局 12 个 */
|
|
6
|
+
export const SCENES: Record<string, Scene> = {
|
|
7
|
+
start: {
|
|
8
|
+
id: 'start',
|
|
9
|
+
text: (s) => {
|
|
10
|
+
const lines = [
|
|
11
|
+
'🌫️ **秘境入口**',
|
|
12
|
+
'',
|
|
13
|
+
'浓雾散去,你站在古老石门前。左侧是坍塌廊柱,右侧藤蔓小径,远处石塔刺破雾霭。',
|
|
14
|
+
];
|
|
15
|
+
if (s.inventory.includes('torch')) lines.push('', '🔥 火把照亮门缝间的幽蓝微光。');
|
|
16
|
+
if (s.flags.spirit_met) lines.push('', '💧 泉灵低语:「庭院与星塔皆藏路。」');
|
|
17
|
+
if (s.flags.saw_map) lines.push('', '🗺️ 你记得塔顶俯瞰的全貌:泉、庭、墓、门。');
|
|
18
|
+
return lines.join('\n');
|
|
19
|
+
},
|
|
20
|
+
choices: [
|
|
21
|
+
{ id: 'push_door', label: '🚪 推门而入', style: 'primary' },
|
|
22
|
+
{ id: 'search_rubble', label: '🔍 翻找碎石' },
|
|
23
|
+
{ id: 'climb_tower', label: '🗼 攀登石塔' },
|
|
24
|
+
{ id: 'vine_path', label: '🌿 拨开藤蔓', requires: (s) => s.flags.spirit_met },
|
|
25
|
+
{ id: 'leave', label: '🏃 转身离开', style: 'danger' },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
rubble: {
|
|
30
|
+
id: 'rubble',
|
|
31
|
+
text: T([
|
|
32
|
+
'你在坍塌廊柱间翻找,指尖触到冰冷金属。',
|
|
33
|
+
'一把**火把**躺在尘土里;石缝深处还有生锈短剑。',
|
|
34
|
+
]),
|
|
35
|
+
choices: [
|
|
36
|
+
{ id: 'take_torch', label: '🔥 拾取火把', requires: (s) => !s.inventory.includes('torch') },
|
|
37
|
+
{ id: 'take_blade', label: '⚔️ 拾取短剑', requires: (s) => !s.inventory.includes('rusted_blade') },
|
|
38
|
+
{ id: 'back_entrance', label: '↩️ 返回入口' },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
watchtower: {
|
|
43
|
+
id: 'watchtower',
|
|
44
|
+
text: (s) => T([
|
|
45
|
+
'🗼 **瞭望石塔**',
|
|
46
|
+
'',
|
|
47
|
+
s.flags.saw_map
|
|
48
|
+
? '塔顶风声呼啸,你已记下遗迹布局。'
|
|
49
|
+
: '螺旋阶梯通向塔顶,视野开阔处或能看清秘境结构。',
|
|
50
|
+
'塔身裂缝中卡着一页星图残角。',
|
|
51
|
+
]),
|
|
52
|
+
choices: [
|
|
53
|
+
{ id: 'survey_map', label: '🗺️ 登高测绘', requires: (s) => !s.flags.saw_map },
|
|
54
|
+
{ id: 'take_chart_fragment', label: '📄 取星图残页', requires: (s) => !s.inventory.includes('star_chart') },
|
|
55
|
+
{ id: 'enter_observatory', label: '🔭 进入星象台', requires: (s) => s.flags.saw_map },
|
|
56
|
+
{ id: 'descend_tower', label: '↩️ 下塔' },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
vine_path: {
|
|
61
|
+
id: 'vine_path',
|
|
62
|
+
text: T(['🌿 **秘径**', '', '青苔石阶向下,药香与湿土气息交织。']),
|
|
63
|
+
choices: [
|
|
64
|
+
{ id: 'enter_garden', label: '🏡 进入庭院' },
|
|
65
|
+
{ id: 'back_entrance', label: '↩️ 返回入口' },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
corridor: {
|
|
70
|
+
id: 'corridor',
|
|
71
|
+
text: (s) => {
|
|
72
|
+
const lit = s.inventory.includes('torch');
|
|
73
|
+
const lines = [
|
|
74
|
+
lit ? '🕯️ 火把照亮狭长走廊。' : '🌑 走廊漆黑,你贴墙摸索。',
|
|
75
|
+
'左侧蓝光(泉)、右侧石阶(墓)、前方积水(淹)、壁画需火光细读。',
|
|
76
|
+
];
|
|
77
|
+
if (s.flags.knows_sequence) lines.push('', '📜 壁画铭文:光 → 影 → 门。');
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
},
|
|
80
|
+
choices: [
|
|
81
|
+
{ id: 'go_blue', label: '💧 走向蓝光' },
|
|
82
|
+
{ id: 'go_stairs', label: '⬇️ 走下石阶' },
|
|
83
|
+
{ id: 'go_flooded', label: '🌊 踏入积水厅' },
|
|
84
|
+
{ id: 'study_mural', label: '🖼️ 研读壁画', requires: (s) => s.inventory.includes('torch') },
|
|
85
|
+
{ id: 'enter_bell_tower', label: '🔔 进入钟楼', requires: (s) => s.flags.spirit_met },
|
|
86
|
+
{ id: 'go_back', label: '↩️ 退回入口' },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
mural_room: {
|
|
91
|
+
id: 'mural_room',
|
|
92
|
+
text: T([
|
|
93
|
+
'🖼️ **铭文壁画**',
|
|
94
|
+
'',
|
|
95
|
+
'三联画:光、影、门。下角刻序:**光 → 影 → 门**。',
|
|
96
|
+
'壁画后有一条狭窄画廊,低语声如风穿隙。',
|
|
97
|
+
]),
|
|
98
|
+
choices: [
|
|
99
|
+
{ id: 'memorize', label: '📝 记下顺序' },
|
|
100
|
+
{ id: 'enter_whisper', label: '👂 进入低语画廊', requires: (s) => s.flags.knows_sequence },
|
|
101
|
+
{ id: 'back_corridor', label: '↩️ 返回走廊' },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
whisper_gallery: {
|
|
106
|
+
id: 'whisper_gallery',
|
|
107
|
+
text: T([
|
|
108
|
+
'👂 **低语画廊**',
|
|
109
|
+
'',
|
|
110
|
+
'亿万精灵名讳在墙间回响。你听见:「星坠之日,门为封印,非为宝藏。」',
|
|
111
|
+
'「守忆者需集月华、星图、泉心三证。」',
|
|
112
|
+
]),
|
|
113
|
+
choices: [
|
|
114
|
+
{ id: 'listen_deep', label: '🧘 静心聆听', requires: (s) => !s.flags.whisper_lore },
|
|
115
|
+
{ id: 'back_mural', label: '↩️ 返回壁画室' },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
spring: {
|
|
120
|
+
id: 'spring',
|
|
121
|
+
text: (s) => T([
|
|
122
|
+
'💧 **精灵泉**',
|
|
123
|
+
'',
|
|
124
|
+
s.flags.spirit_met ? '泉灵光影朝你颔首。' : '水光凝聚为精灵形貌,注视着你。',
|
|
125
|
+
!s.flags.healed ? '泉水可治愈一次。' : '泉水已无先前效力。',
|
|
126
|
+
]),
|
|
127
|
+
choices: [
|
|
128
|
+
{ id: 'drink', label: '🍶 饮用泉水', requires: (s) => !s.flags.healed },
|
|
129
|
+
{ id: 'take_gem', label: '💎 取走宝石', requires: (s) => !s.inventory.includes('gem') },
|
|
130
|
+
{ id: 'talk_spirit', label: '💬 与泉灵交谈', requires: (s) => !s.flags.spirit_met },
|
|
131
|
+
{ id: 'offer_gem', label: '🙏 献宝石换护符', requires: (s) => s.inventory.includes('gem') && !s.inventory.includes('amulet') },
|
|
132
|
+
{ id: 'leave_spring', label: '↩️ 离开' },
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
flooded_hall: {
|
|
137
|
+
id: 'flooded_hall',
|
|
138
|
+
text: (s) => T([
|
|
139
|
+
'🌊 **积水前厅**',
|
|
140
|
+
'',
|
|
141
|
+
'及膝冷水没过石板,远处有坍塌市集拱门。',
|
|
142
|
+
s.inventory.includes('elf_feather') ? '银羽护体,寒气难侵。' : '寒意刺骨,久留恐失温。',
|
|
143
|
+
]),
|
|
144
|
+
choices: [
|
|
145
|
+
{ id: 'wade_market', label: '🏚️ 前往废墟市集' },
|
|
146
|
+
{ id: 'use_feather_wade', label: '🪶 银羽护体涉水', requires: (s) => s.inventory.includes('elf_feather') },
|
|
147
|
+
{ id: 'drink_elixir_warm', label: '🍶 饮灵药暖身', requires: (s) => s.inventory.includes('elixir') },
|
|
148
|
+
{ id: 'back_corridor', label: '↩️ 返回走廊' },
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
market_ruins: {
|
|
153
|
+
id: 'market_ruins',
|
|
154
|
+
text: T([
|
|
155
|
+
'🏚️ **废墟市集**',
|
|
156
|
+
'',
|
|
157
|
+
'幽灵摊主陈列面具与古币。「一物换一物,或银币亦可。」',
|
|
158
|
+
]),
|
|
159
|
+
choices: [
|
|
160
|
+
{ id: 'buy_mask_coin', label: '🎭 银币换面具', requires: (s) => s.inventory.includes('silver_coin') && !s.inventory.includes('masquerade_mask') },
|
|
161
|
+
{ id: 'trade_gem_coin', label: '💎 宝石换银币', requires: (s) => s.inventory.includes('gem') },
|
|
162
|
+
{ id: 'go_bridge', label: '🌉 前往冥河石桥' },
|
|
163
|
+
{ id: 'leave_market', label: '↩️ 离开市集' },
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
garden: {
|
|
168
|
+
id: 'garden',
|
|
169
|
+
text: (s) => T([
|
|
170
|
+
'🏡 **遗忘庭院**',
|
|
171
|
+
'',
|
|
172
|
+
'银叶古树、紧锁铁门、侧翼温室玻璃碎裂。',
|
|
173
|
+
s.inventory.includes('herb') ? '花坛已采过药草。' : '花坛闪烁荧光草药。',
|
|
174
|
+
]),
|
|
175
|
+
choices: [
|
|
176
|
+
{ id: 'pick_herb', label: '🌿 采集草药', requires: (s) => !s.inventory.includes('herb') },
|
|
177
|
+
{ id: 'rest_tree', label: '🌳 树下休息', requires: (s) => !s.flags.rested },
|
|
178
|
+
{ id: 'enter_greenhouse', label: '🪴 进入温室' },
|
|
179
|
+
{ id: 'open_iron_door', label: '🔑 打开铁门', requires: (s) => s.inventory.includes('key') },
|
|
180
|
+
{ id: 'stay_forever', label: '🏠 定居庭院', requires: (s) => s.flags.rested && s.flags.spirit_met },
|
|
181
|
+
{ id: 'back_corridor', label: '↩️ 返回走廊' },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
greenhouse: {
|
|
186
|
+
id: 'greenhouse',
|
|
187
|
+
text: T([
|
|
188
|
+
'🪴 **破碎温室**',
|
|
189
|
+
'',
|
|
190
|
+
'枯藤缠绕蒸馏器,可萃取草药为灵药。',
|
|
191
|
+
]),
|
|
192
|
+
choices: [
|
|
193
|
+
{ id: 'brew_elixir', label: '⚗️ 萃取灵药', requires: (s) => s.inventory.includes('herb') && !s.inventory.includes('elixir') },
|
|
194
|
+
{ id: 'back_garden', label: '↩️ 返回庭院' },
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
library: {
|
|
199
|
+
id: 'library',
|
|
200
|
+
text: (s) => T([
|
|
201
|
+
'📚 **尘封书库**',
|
|
202
|
+
'',
|
|
203
|
+
'残卷、墙钥、暗门后传来锻炉热气。',
|
|
204
|
+
s.flags.read_archive ? '你已读过秘档。' : '古籍区尚有未读档案。',
|
|
205
|
+
]),
|
|
206
|
+
choices: [
|
|
207
|
+
{ id: 'take_scroll', label: '📜 取卷轴', requires: (s) => !s.inventory.includes('scroll') },
|
|
208
|
+
{ id: 'take_key', label: '🔑 取钥匙', requires: (s) => !s.inventory.includes('key') },
|
|
209
|
+
{ id: 'read_archive', label: '📖 研读秘档', requires: (s) => !s.flags.read_archive },
|
|
210
|
+
{ id: 'enter_forge', label: '🔥 进入锻炉间', requires: (s) => s.inventory.includes('scroll') },
|
|
211
|
+
{ id: 'back_garden', label: '↩️ 返回庭院' },
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
ember_forge: {
|
|
216
|
+
id: 'ember_forge',
|
|
217
|
+
text: (s) => T([
|
|
218
|
+
'🔥 **余烬锻炉**',
|
|
219
|
+
'',
|
|
220
|
+
'精灵余火仍燃。可淬炼短剑,或升华护符。',
|
|
221
|
+
s.flags.forged_blade ? '短剑已淬炼。' : '短剑可淬炼。',
|
|
222
|
+
]),
|
|
223
|
+
choices: [
|
|
224
|
+
{ id: 'forge_blade', label: '⚔️ 淬炼短剑', requires: (s) => s.inventory.includes('rusted_blade') && !s.flags.forged_blade },
|
|
225
|
+
{ id: 'upgrade_amulet', label: '✨ 升华护符', requires: (s) => s.inventory.includes('amulet') && s.inventory.includes('moon_shard') && !s.flags.amulet_upgraded },
|
|
226
|
+
{ id: 'back_library', label: '↩️ 返回书库' },
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
bell_tower: {
|
|
231
|
+
id: 'bell_tower',
|
|
232
|
+
text: T([
|
|
233
|
+
'🔔 **回音钟楼**',
|
|
234
|
+
'',
|
|
235
|
+
'悬空铜钟布满符纹。钟舌缺失,旁有铸台。',
|
|
236
|
+
]),
|
|
237
|
+
choices: [
|
|
238
|
+
{ id: 'ring_bell', label: '🔔 敲响铜钟', requires: (s) => !s.inventory.includes('spirit_bell') },
|
|
239
|
+
{ id: 'back_corridor', label: '↩️ 返回走廊' },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
depths: {
|
|
244
|
+
id: 'depths',
|
|
245
|
+
text: T([
|
|
246
|
+
'⬇️ **地下墓穴**',
|
|
247
|
+
'',
|
|
248
|
+
'石棺半开,**守墓幽影**凝形扑来!',
|
|
249
|
+
]),
|
|
250
|
+
choices: [
|
|
251
|
+
{ id: 'fight', label: '⚔️ 正面迎战', style: 'danger' },
|
|
252
|
+
{ id: 'fight_blade', label: '🗡️ 挥剑迎击', requires: (s) => s.flags.forged_blade },
|
|
253
|
+
{ id: 'use_torch', label: '🔥 火把驱散', requires: (s) => s.inventory.includes('torch') },
|
|
254
|
+
{ id: 'use_herb', label: '🌿 草药麻痹', requires: (s) => s.inventory.includes('herb') },
|
|
255
|
+
{ id: 'flee', label: '🏃 逃跑' },
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
crypt_entry: {
|
|
260
|
+
id: 'crypt_entry',
|
|
261
|
+
text: T([
|
|
262
|
+
'⚰️ **墓道岔口**',
|
|
263
|
+
'',
|
|
264
|
+
'左:封印密室|右:骸骨甬道|下:蛛巢暗道',
|
|
265
|
+
]),
|
|
266
|
+
choices: [
|
|
267
|
+
{ id: 'go_vault', label: '🔐 封印密室' },
|
|
268
|
+
{ id: 'go_catacombs', label: '💀 骸骨甬道' },
|
|
269
|
+
{ id: 'go_spider', label: '🕷️ 蛛巢暗道' },
|
|
270
|
+
{ id: 'go_sealed', label: '🚪 回音巨门', requires: (s) => s.inventory.includes('spirit_bell') && s.inventory.includes('compass') },
|
|
271
|
+
{ id: 'back_corridor', label: '↩️ 返回走廊' },
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
spider_nest: {
|
|
276
|
+
id: 'spider_nest',
|
|
277
|
+
text: T([
|
|
278
|
+
'🕷️ **蛛巢暗道**',
|
|
279
|
+
'',
|
|
280
|
+
'巨网横亘,蛛眼如灯笼。后方似通暗影迷宫。',
|
|
281
|
+
]),
|
|
282
|
+
choices: [
|
|
283
|
+
{ id: 'cut_web_blade', label: '🗡️ 斩网前行', requires: (s) => s.flags.forged_blade },
|
|
284
|
+
{ id: 'burn_web', label: '🔥 火烧蛛网', requires: (s) => s.inventory.includes('torch') },
|
|
285
|
+
{ id: 'flee_spider', label: '🏃 撤退', style: 'danger' },
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
shadow_maze: {
|
|
290
|
+
id: 'shadow_maze',
|
|
291
|
+
text: T([
|
|
292
|
+
'🌑 **暗影迷宫**',
|
|
293
|
+
'',
|
|
294
|
+
'墙壁流动如墨,路径每秒变幻。',
|
|
295
|
+
]),
|
|
296
|
+
choices: [
|
|
297
|
+
{ id: 'navigate_compass', label: '🧭 罗盘定向', requires: (s) => s.inventory.includes('compass') },
|
|
298
|
+
{ id: 'wander_maze', label: '🚶 随意乱走', style: 'danger' },
|
|
299
|
+
{ id: 'back_spider', label: '↩️ 返回蛛巢' },
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
catacombs: {
|
|
304
|
+
id: 'catacombs',
|
|
305
|
+
text: T([
|
|
306
|
+
'💀 **骸骨甬道**',
|
|
307
|
+
'',
|
|
308
|
+
'压力机关铭刻:诵卷轴真言。侧室铁链锁着幽魂。',
|
|
309
|
+
'骸骨手中铜钥闪光。',
|
|
310
|
+
]),
|
|
311
|
+
choices: [
|
|
312
|
+
{ id: 'read_scroll', label: '📜 展开卷轴', requires: (s) => s.inventory.includes('scroll') },
|
|
313
|
+
{ id: 'dash_trap', label: '🏃 硬闯机关', style: 'danger' },
|
|
314
|
+
{ id: 'enter_prison', label: '⛓️ 进入囚室' },
|
|
315
|
+
{ id: 'take_key_skeleton', label: '🔑 取铜钥', requires: (s) => !s.inventory.includes('key') },
|
|
316
|
+
{ id: 'back_crypt', label: '↩️ 返回岔口' },
|
|
317
|
+
],
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
prison_cell: {
|
|
321
|
+
id: 'prison_cell',
|
|
322
|
+
text: T([
|
|
323
|
+
'⛓️ **幽囚石室**',
|
|
324
|
+
'',
|
|
325
|
+
'幽魂哀求:「银币可买通行,羽毛可渡冥河。」',
|
|
326
|
+
'墙角埋着古银币。',
|
|
327
|
+
]),
|
|
328
|
+
choices: [
|
|
329
|
+
{ id: 'take_coin', label: '🪙 取古银币', requires: (s) => !s.inventory.includes('silver_coin') },
|
|
330
|
+
{ id: 'free_ghost', label: '🕊️ 释放幽魂', requires: (s) => s.inventory.includes('elf_feather') },
|
|
331
|
+
{ id: 'back_catacombs', label: '↩️ 返回甬道' },
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
ossuary: {
|
|
336
|
+
id: 'ossuary',
|
|
337
|
+
text: T([
|
|
338
|
+
'🪦 **骨灰龛室**',
|
|
339
|
+
'',
|
|
340
|
+
'悬浮护符。幽魂问:「精灵遗迹为何陨落?」',
|
|
341
|
+
]),
|
|
342
|
+
choices: [
|
|
343
|
+
{ id: 'answer_greed', label: '「贪婪与战争」' },
|
|
344
|
+
{ id: 'answer_flood', label: '「大洪水」' },
|
|
345
|
+
{ id: 'answer_star', label: '「星辰坠落」', style: 'primary' },
|
|
346
|
+
{ id: 'back_catacombs', label: '↩️ 返回甬道' },
|
|
347
|
+
],
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
ghost_bridge: {
|
|
351
|
+
id: 'ghost_bridge',
|
|
352
|
+
text: T([
|
|
353
|
+
'🌉 **冥河石桥**',
|
|
354
|
+
'',
|
|
355
|
+
'无栏石桥横跨黑水。守桥者伸手要通行费。',
|
|
356
|
+
]),
|
|
357
|
+
choices: [
|
|
358
|
+
{ id: 'pay_coin', label: '🪙 付银币过桥', requires: (s) => s.inventory.includes('silver_coin') },
|
|
359
|
+
{ id: 'cross_feather', label: '🪶 银羽渡桥', requires: (s) => s.inventory.includes('elf_feather') },
|
|
360
|
+
{ id: 'back_market', label: '↩️ 返回市集' },
|
|
361
|
+
],
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
crystal_cave: {
|
|
365
|
+
id: 'crystal_cave',
|
|
366
|
+
text: T([
|
|
367
|
+
'💎 **晶洞**',
|
|
368
|
+
'',
|
|
369
|
+
'月华凝于石笋。洞顶飘落银羽。',
|
|
370
|
+
]),
|
|
371
|
+
choices: [
|
|
372
|
+
{ id: 'take_moon_shard', label: '🌙 取月华碎片', requires: (s) => !s.inventory.includes('moon_shard') },
|
|
373
|
+
{ id: 'take_feather', label: '🪶 拾银羽', requires: (s) => !s.inventory.includes('elf_feather') },
|
|
374
|
+
{ id: 'enter_moon_pool', label: '💧 前往月池' },
|
|
375
|
+
{ id: 'back_bridge', label: '↩️ 返回石桥' },
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
moon_pool: {
|
|
380
|
+
id: 'moon_pool',
|
|
381
|
+
text: (s) => T([
|
|
382
|
+
'🌙 **月影池**',
|
|
383
|
+
'',
|
|
384
|
+
'池水映月。星图残页可与之共鸣。',
|
|
385
|
+
s.flags.moon_blessed ? '月华已赐福。' : '池底似有水下沉殿。',
|
|
386
|
+
]),
|
|
387
|
+
choices: [
|
|
388
|
+
{ id: 'align_chart', label: '📄 星图共鸣', requires: (s) => s.inventory.includes('star_chart') && !s.flags.moon_blessed },
|
|
389
|
+
{ id: 'dive_shrine', label: '🤿 潜入沉殿', requires: (s) => s.flags.moon_blessed },
|
|
390
|
+
{ id: 'back_crystal', label: '↩️ 返回晶洞' },
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
|
|
394
|
+
sunken_shrine: {
|
|
395
|
+
id: 'sunken_shrine',
|
|
396
|
+
text: T([
|
|
397
|
+
'🛕 **水下沉殿**',
|
|
398
|
+
'',
|
|
399
|
+
'三证石台:月华、星图、泉心印记。缺一则门不开。',
|
|
400
|
+
]),
|
|
401
|
+
choices: [
|
|
402
|
+
{ id: 'ritual_star_keeper', label: '✨ 完成星守仪式', requires: (s) => s.inventory.includes('moon_shard') && s.inventory.includes('star_chart') && s.flags.spirit_met },
|
|
403
|
+
{ id: 'back_moon_pool', label: '↩️ 浮上水面' },
|
|
404
|
+
],
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
observatory: {
|
|
408
|
+
id: 'observatory',
|
|
409
|
+
text: T([
|
|
410
|
+
'🔭 **星象台**',
|
|
411
|
+
'',
|
|
412
|
+
'铜制浑天仪缺损。可校准罗盘。',
|
|
413
|
+
]),
|
|
414
|
+
choices: [
|
|
415
|
+
{ id: 'craft_compass', label: '🧭 校准罗盘', requires: (s) => !s.inventory.includes('compass') },
|
|
416
|
+
{ id: 'back_tower', label: '↩️ 下塔' },
|
|
417
|
+
],
|
|
418
|
+
},
|
|
419
|
+
|
|
420
|
+
sealed_gates: {
|
|
421
|
+
id: 'sealed_gates',
|
|
422
|
+
text: T([
|
|
423
|
+
'🚪 **回音巨门**',
|
|
424
|
+
'',
|
|
425
|
+
'铃、罗盘、钥三缺一时铭文闪烁。门后是封印内庭捷径。',
|
|
426
|
+
]),
|
|
427
|
+
choices: [
|
|
428
|
+
{ id: 'open_gates', label: '🔓 开启巨门', requires: (s) => s.inventory.includes('spirit_bell') && s.inventory.includes('compass') && s.inventory.includes('key') },
|
|
429
|
+
{ id: 'back_crypt', label: '↩️ 返回岔口' },
|
|
430
|
+
],
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
vault: {
|
|
434
|
+
id: 'vault',
|
|
435
|
+
text: (s) => T([
|
|
436
|
+
'🔐 **封印密室**',
|
|
437
|
+
'',
|
|
438
|
+
'三雕像:光、影、门。',
|
|
439
|
+
s.flags.knows_sequence ? '顺序:光 → 影 → 门。' : '顺序未知。',
|
|
440
|
+
]),
|
|
441
|
+
choices: [
|
|
442
|
+
{ id: 'seq_light_shadow_door', label: '光→影→门', style: 'primary' },
|
|
443
|
+
{ id: 'seq_door_light_shadow', label: '门→光→影' },
|
|
444
|
+
{ id: 'seq_shadow_door_light', label: '影→门→光' },
|
|
445
|
+
{ id: 'back_crypt', label: '↩️ 返回岔口' },
|
|
446
|
+
],
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
vault_open: {
|
|
450
|
+
id: 'vault_open',
|
|
451
|
+
text: (s) => T([
|
|
452
|
+
'✨ **封印已解**',
|
|
453
|
+
'',
|
|
454
|
+
s.flags.amulet_upgraded ? '升华护符引路,王座厅近在咫尺。' : '螺旋阶梯通向王座厅。',
|
|
455
|
+
]),
|
|
456
|
+
choices: [
|
|
457
|
+
{ id: 'descend_throne', label: '⬇️ 王座厅' },
|
|
458
|
+
{ id: 'take_treasure_now', label: '💰 取宝离开' },
|
|
459
|
+
{ id: 'back_crypt', label: '↩️ 返回岔口' },
|
|
460
|
+
],
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
throne_room: {
|
|
464
|
+
id: 'throne_room',
|
|
465
|
+
text: (s) => {
|
|
466
|
+
const ready = s.flags.whisper_lore && s.flags.amulet_upgraded && s.flags.spirit_met;
|
|
467
|
+
return T([
|
|
468
|
+
'👑 **精灵王座厅**',
|
|
469
|
+
'',
|
|
470
|
+
ready ? '三证齐聚,王座虚影恭迎守忆者。' : '王座冷光闪烁,贸然落座恐遭反噬。',
|
|
471
|
+
]);
|
|
472
|
+
},
|
|
473
|
+
choices: [
|
|
474
|
+
{
|
|
475
|
+
id: 'ascend',
|
|
476
|
+
label: '✨ 星辉归位',
|
|
477
|
+
style: 'primary',
|
|
478
|
+
requires: (s) => s.flags.whisper_lore && s.flags.amulet_upgraded && s.flags.spirit_met && s.inventory.includes('scroll'),
|
|
479
|
+
},
|
|
480
|
+
{ id: 'ascend_mask', label: '🎭 面具加冕', requires: (s) => s.inventory.includes('masquerade_mask') },
|
|
481
|
+
{ id: 'claim_crown', label: '👑 坐上王座', style: 'danger' },
|
|
482
|
+
{ id: 'retreat_throne', label: '🏃 取宝离开' },
|
|
483
|
+
],
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
// —— 终局 ——
|
|
487
|
+
treasure: { id: 'treasure', terminal: true, text: T(['🏆 **遗迹宝藏**', '', '琥珀星光入袋,你成为传说。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
488
|
+
ascension: { id: 'ascension', terminal: true, text: T(['🌟 **星辉归位**', '', '你成为守忆者,历史因你而不朽。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
489
|
+
wisdom: { id: 'wisdom', terminal: true, text: T(['📜 **智者归途**', '', '你携记忆离去,无宝亦足。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
490
|
+
curse: { id: 'curse', terminal: true, text: T(['☠️ **贪婪代价**', '', '王座吞噬了你。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
491
|
+
escape: { id: 'escape', terminal: true, text: T(['🌅 **生还**', '', '遗迹坍塌,你活着出来。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
492
|
+
defeat: { id: 'defeat', terminal: true, text: T(['💀 **陨落**', '', '秘境再添无名骸骨。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
493
|
+
coward: { id: 'coward', terminal: true, text: T(['🏠 **怯懦归途**', '', '你从未入门。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
494
|
+
hermit: { id: 'hermit', terminal: true, text: T(['🌳 **庭院隐者**', '', '你在古树下定居,与遗迹共生。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
495
|
+
redemption: { id: 'redemption', terminal: true, text: T(['🕊️ **幽魂解脱**', '', '你渡魂过桥,获冥府祝福。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
496
|
+
drowned: { id: 'drowned', terminal: true, text: T(['🌊 **寒水沉眠**', '', '积水厅吞没意识。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
497
|
+
spider: { id: 'spider', terminal: true, text: T(['🕷️ **茧中亡魂**', '', '巨蛛将你制成茧。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
498
|
+
labyrinth: { id: 'labyrinth', terminal: true, text: T(['🌑 **迷宫迷失**', '', '暗影吞没方向感。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
499
|
+
star_keeper: { id: 'star_keeper', terminal: true, text: T(['🌙 **星守誓约**', '', '你镇守月池,成为精灵最后的灯塔。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
500
|
+
mask_king: { id: 'mask_king', terminal: true, text: T(['🎭 **面具之王**', '', '面具与王座融合,你统治空城。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
501
|
+
merchant: { id: 'merchant', terminal: true, text: T(['💰 **行商结局**', '', '你满载银币离开,遗迹不过集市。']), choices: [{ id: 'restart', label: '🔄 再玩一次', style: 'primary' }] },
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
export function countScenes(): { total: number; playable: number; terminals: number } {
|
|
505
|
+
const all = Object.values(SCENES);
|
|
506
|
+
const terminals = all.filter((s) => s.terminal).length;
|
|
507
|
+
return { total: all.length, playable: all.length - terminals, terminals };
|
|
508
|
+
}
|