steamsheep-ts-game-engine 1.1.0 → 3.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 CHANGED
@@ -1,1087 +1,495 @@
1
- # 游戏引擎框架使用指南
1
+ # SteamSheep TypeScript Game Engine - 完整 API 参考
2
2
 
3
- ## 📋 更新日志
3
+ 一个功能强大、类型安全的文字冒险/RPG 游戏引擎框架。
4
4
 
5
- ### 最新更新 (2024)
5
+ ## 📚 目录
6
6
 
7
- #### ✨ 新增功能
8
-
9
- 1. **事件系统 (Event System)**
10
- - 新增 `EventBus` 类,实现发布-订阅模式
11
- - 新增 `EngineEvents` 常量,预定义系统事件
12
- - 支持类型安全的事件监听和发射
13
- - 自动发射状态变更事件(属性、物品、标记等)
14
-
15
- 2. **历史管理 (History Manager)**
16
- - 新增 `HistoryManager` 类,支持撤销/重做
17
- - Store 新增 `saveSnapshot()` 和 `undo()` 方法
18
- - 支持最多保存 20 个历史快照
19
- - 深拷贝状态,防止引用污染
20
-
21
- 3. **覆盖层系统 (Overlay System)**
22
- - 新增 `OverlaySystem` 组件,处理瞬时通知
23
- - 支持飘字 (Toast) 和弹窗 (Modal)
24
- - 完全分离持久化日志和瞬时通知
25
- - 自动动画和样式
26
-
27
- 4. **消息通知系统**
28
- - Store 新增 `showToast()` 方法 - 显示飘字通知
29
- - Store 新增 `showModal()` 方法 - 显示弹窗通知
30
- - 新增 `NotificationPayload` 类型
31
- - 新增 `NOTIFICATION` 事件
32
-
33
- #### 🔧 重要修改
34
-
35
- 1. **架构优化 - 分离记录与通知**
36
- - ⚠️ **重要**:`LogEntry` 现在仅用于持久化的历史记录
37
- - 移除了 `LogEntry.display` 字段(之前的错误设计)
38
- - 飘字和弹窗不再存入日志,避免持久化灾难
39
- - 通过事件系统实现通知,不污染历史记录
40
-
41
- 2. **Store 方法更新**
42
- - `addLog()` - 移除 `display` 参数,仅用于添加历史记录
43
- - 所有状态变更方法自动发射对应事件
44
- - 新增历史管理相关方法
45
-
46
- 3. **Flow 系统集成**
47
- - 动作执行时自动发射 `ACTION_EXECUTED` 事件
48
- - 支持 `effects.triggerEvent` 触发自定义事件
49
- - 可选的自动快照功能(注释中)
50
-
51
- #### 📚 文档更新
52
-
53
- - 新增完整的中文注释
54
- - 新增架构说明和最佳实践
55
- - 新增事件系统使用指南
56
- - 新增消息通知系统说明
57
- - 更新完整示例代码
58
-
59
- ---
60
-
61
- ## 目录
62
-
63
- - [更新日志](#-更新日志)
64
- - [概述](#概述)
65
- - [核心概念](#核心概念)
66
7
  - [快速开始](#快速开始)
67
- - [核心系统](#核心系统)
68
- - [状态管理 (Store)](#状态管理-store)
69
- - [事件系统 (Events)](#事件系统-events)
70
- - [历史管理 (History)](#历史管理-history)
71
- - [流程系统 (Flow)](#流程系统-flow)
72
- - [查询系统 (Query)](#查询系统-query)
73
- - [消息通知系统](#消息通知系统)
8
+ - [核心 API 列表](#核心-api-列表)
9
+ - [详细用法](#详细用法)
74
10
  - [完整示例](#完整示例)
75
- - [最佳实践](#最佳实践)
76
11
 
77
12
  ---
78
13
 
79
- ## 概述
80
-
81
- 这是一个基于 TypeScript 和 Zustand 构建的通用游戏引擎框架,专为文字冒险、RPG 等回合制游戏设计。
82
-
83
- ### 核心特性
84
-
85
- - ✅ **完全类型安全** - 使用 TypeScript 泛型实现类型安全
86
- - ✅ **状态持久化** - 自动保存到 localStorage
87
- - ✅ **事件驱动** - 解耦的事件系统
88
- - ✅ **撤销/重做** - 内置历史管理
89
- - ✅ **多级消息** - 支持日志/飘字/弹窗
90
- - ✅ **灵活扩展** - 易于扩展自定义功能
14
+ ## 🚀 快速开始
91
15
 
92
- ### 架构图
93
-
94
- ```
95
- ┌─────────────────────────────────────────────────────────┐
96
- │ UI Layer │
97
- │ (React Components, Event Listeners) │
98
- └────────────────┬────────────────────────────────────────┘
99
-
100
- ┌────────────────▼────────────────────────────────────────┐
101
- │ Engine Layer │
102
- │ │
103
- │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
104
- │ │ Store │ │ Events │ │ History │ │
105
- │ │ (Zustand)│◄─┤ (Bus) │ │(Manager) │ │
106
- │ └────┬─────┘ └──────────┘ └──────────┘ │
107
- │ │ │
108
- │ ┌────▼─────┐ ┌──────────┐ ┌──────────-┐ │
109
- │ │ Flow │ │ Query │ │ Messages │ │
110
- │ │ (System) │ │ (System) │ │(Constants)│ │
111
- │ └──────────┘ └──────────┘ └──────────—┘ │
112
- └─────────────────────────────────────────────────────────┘
16
+ ```bash
17
+ npm install steamsheep-ts-game-engine
113
18
  ```
114
19
 
115
- ---
116
-
117
- ## 核心概念
118
-
119
- ### 1. 游戏状态 (GameState)
120
-
121
- 游戏状态包含所有游戏数据:
122
-
123
20
  ```typescript
124
- interface GameState<S, I, F, X> {
125
- stats: Record<S, number>; // 数值属性(生命值、金币等)
126
- inventory: I[]; // 物品清单
127
- flags: Record<F, boolean>; // 布尔标记(任务进度等)
128
- world: WorldState; // 世界状态(位置、时间)
129
- logs: LogEntry[]; // 日志记录
130
- extra: X; // 扩展数据
131
- }
132
- ```
133
-
134
- ### 2. 动作 (Action)
21
+ import { createGameEngineStore } from 'steamsheep-ts-game-engine';
135
22
 
136
- 动作是玩家可以执行的操作:
23
+ // 定义游戏类型
24
+ type Stats = 'hp' | 'mp' | 'gold';
25
+ type Items = 'sword' | 'potion';
26
+ type Flags = 'quest_done';
137
27
 
138
- ```typescript
139
- interface ActionDef<S, I, F, X> {
140
- id: string; // 唯一标识
141
- label: string; // 显示名称
142
- costs?: Partial<Record<S, number>>; // 执行成本
143
- requirements?: RequirementDef; // 执行条件
144
- effects?: EffectDef; // 执行效果
145
- resultText: string; // 结果文本
146
- }
28
+ // 创建 Store
29
+ const useGameStore = createGameEngineStore(initialState, 'my-game');
147
30
  ```
148
31
 
149
- ### 3. 事件 (Event)
150
-
151
- 事件用于系统间通信,解耦组件:
32
+ ---
152
33
 
153
- ```typescript
154
- // 发射事件
155
- gameEvents.emit(EngineEvents.STAT_CHANGE, { stat: 'hp', delta: -10 });
34
+ ## 📋 核心 API 列表
35
+
36
+ ### 1. 类型定义 (core/types.ts)
37
+
38
+ #### 核心接口
39
+ - `GameState<S, I, F, X>` - 游戏状态
40
+ - `ActionDef<S, I, F, X>` - 动作定义
41
+ - `EffectDef<S, I, F, X>` - 效果定义
42
+ - `RequirementDef<S, I, F, X>` - 需求定义
43
+ - `LocationDef<S, I, F, X>` - 地点定义
44
+ - `RequirementCheckResult` - 需求检查结果
45
+ - `FlagsBatchOperation<F>` - 批量标记操作
46
+ - `GameStoreActions<S, I, F, X>` - Store 操作方法
47
+ - `SnapshotInfo<S, I, F, X>` - 快照信息
48
+
49
+ ### 2. Store API (state/store.ts)
50
+
51
+ #### 状态操作
52
+ - `updateStat(stat, delta)` - 更新单个属性
53
+ - `setStats(stats)` - 批量更新属性
54
+ - `setExtra(updater)` - 更新扩展数据
55
+ - `addItem(item)` - 添加物品
56
+ - `removeItem(item)` - 移除物品
57
+ - `setFlag(flag, value)` - 设置标记
58
+
59
+ #### 系统操作
60
+ - `addLog(text, type?)` - 添加日志(持久化)
61
+ - `showToast(text, type?)` - 显示飘字(瞬时)
62
+ - `showModal(text, type?)` - 显示弹窗(瞬时)
63
+ - `advanceTime(amount?)` - 推进时间
64
+ - `teleport(locationId)` - 传送
65
+ - `reset()` - 重置游戏
66
+
67
+ #### 快照操作
68
+ - `saveSnapshot(description?)` - 保存匿名快照
69
+ - `saveNamedSnapshot(name, description?)` - 保存命名快照
70
+ - `undo()` - 撤销到上一个状态
71
+ - `restoreSnapshot(name)` - 恢复到命名快照
72
+ - `deleteSnapshot(name)` - 删除命名快照
73
+ - `listSnapshots()` - 列出所有快照
74
+ - `hasSnapshot(name)` - 检查快照是否存在
75
+
76
+ ### 3. 效果系统 (EffectDef)
77
+
78
+ #### 静态效果
79
+ - `statsChange` - 数值属性变更(支持函数)
80
+ - `itemsAdd` - 添加物品
81
+ - `itemsRemove` - 移除物品
82
+ - `flagsSet` - 设置标记(支持函数)
83
+ - `flagsBatch` - 批量标记操作(支持函数)
84
+ - `teleport` - 传送
85
+ - `timeAdvance` - 时间推进(支持函数)
86
+ - `onTimeAdvance` - 时间推进钩子
87
+ - `triggerEvent` - 触发自定义事件
88
+
89
+ #### 动态效果
90
+ - `conditionalEffects(state)` - 条件性效果
91
+ - `custom(draft, state)` - 修改 extra 数据
92
+ - `customFull(draft, originalState)` - 修改完整状态
93
+
94
+ #### 后置钩子
95
+ - `afterEffects(state, originalState, actions)` - 后置效果钩子
96
+
97
+ ### 4. 需求系统 (RequirementDef)
98
+
99
+ - `stats` - 数值属性需求
100
+ - `hasItems` - 必须拥有的物品
101
+ - `noItems` - 不能拥有的物品
102
+ - `hasFlags` - 必须为 true 的标记
103
+ - `noFlags` - 必须为 false 的标记
104
+ - `custom(state)` - 自定义需求(支持返回失败原因)
105
+
106
+ ### 5. 查询系统 (systems/query.ts)
107
+
108
+ - `QuerySystem.checkRequirements(state, reqs)` - 检查需求(boolean)
109
+ - `QuerySystem.checkRequirementsWithReason(state, reqs)` - 检查需求(带原因)
110
+ - `QuerySystem.canAfford(state, costs)` - 检查成本
111
+
112
+ ### 6. 流程系统 (systems/flow.ts)
113
+
114
+ - `FlowSystem.executeAction(store, action)` - 执行动作
115
+
116
+ ### 7. 事件系统 (systems/events.ts)
117
+
118
+ - `gameEvents.on(event, handler)` - 监听事件
119
+ - `gameEvents.emit(event, data)` - 发射事件
120
+ - `gameEvents.off(event, handler)` - 取消监听
156
121
 
157
- // 监听事件
158
- gameEvents.on(EngineEvents.STAT_CHANGE, (data) => {
159
- console.log(`${data.stat} 变化了 ${data.delta}`);
160
- });
161
- ```
122
+ #### 预定义事件
123
+ - `EngineEvents.STAT_CHANGE` - 属性变化
124
+ - `EngineEvents.ITEM_ADD` - 物品添加
125
+ - `EngineEvents.ITEM_REMOVE` - 物品移除
126
+ - `EngineEvents.FLAG_CHANGE` - 标记变化
127
+ - `EngineEvents.ACTION_EXECUTED` - 动作执行
128
+ - `EngineEvents.NOTIFICATION` - 通知
129
+ - `EngineEvents.CUSTOM` - 自定义事件
130
+
131
+ ### 8. 历史管理 (state/history.ts)
132
+
133
+ - `history.push(state, description?)` - 保存匿名快照
134
+ - `history.pushNamed(state, name, description?)` - 保存命名快照
135
+ - `history.pop()` - 撤销
136
+ - `history.restoreNamed(name)` - 恢复命名快照
137
+ - `history.deleteNamed(name)` - 删除命名快照
138
+ - `history.hasNamed(name)` - 检查快照是否存在
139
+ - `history.listSnapshots()` - 列出所有快照
140
+ - `history.listNamedSnapshots()` - 列出命名快照
141
+ - `history.peek()` - 查看最近快照
142
+ - `history.clear(includeNamed?)` - 清空快照
143
+ - `history.size` - 匿名快照数量
144
+ - `history.namedSize` - 命名快照数量
145
+ - `history.totalSize` - 总快照数量
162
146
 
163
147
  ---
164
148
 
165
- ## 快速开始
149
+ ## 📖 详细用法
166
150
 
167
- ### 步骤 1: 定义游戏类型
151
+ ### 1. 动态 Flag 支持
168
152
 
169
153
  ```typescript
170
- // src/game/types.ts
171
-
172
- // 定义你的游戏属性
173
- type Stats = 'hp' | 'mp' | 'gold' | 'exp';
174
-
175
- // 定义你的物品
176
- type Items = 'sword' | 'potion' | 'key';
177
-
178
- // 定义你的标记
179
- type Flags = 'quest_completed' | 'door_unlocked';
180
-
181
- // 定义扩展数据(可选)
182
- interface ExtraData {
183
- reputation: number;
184
- guild: string;
154
+ effects: {
155
+ // 动态生成 flag 名称
156
+ flagsSet: (state) => {
157
+ const day = state.world.day;
158
+ return {
159
+ [`event_day_${day}`]: true,
160
+ [`gazed_stars_day_${day}`]: true
161
+ };
162
+ }
185
163
  }
186
164
  ```
187
165
 
188
- ### 步骤 2: 创建初始状态
166
+ ### 2. 完整状态访问 (customFull)
189
167
 
190
168
  ```typescript
191
- // src/game/config/init.ts
192
- import type { GameState } from 'steamsheep-ts-game-engine/core';
193
-
194
- export const initialState: GameState<Stats, Items, Flags, ExtraData> = {
195
- stats: {
196
- hp: 100,
197
- mp: 50,
198
- gold: 0,
199
- exp: 0,
200
- },
201
- inventory: [],
202
- flags: {
203
- quest_completed: false,
204
- door_unlocked: false,
205
- },
206
- world: {
207
- currentLocationId: 'town',
208
- day: 1,
209
- time: 0,
210
- },
211
- logs: [],
212
- extra: {
213
- reputation: 0,
214
- guild: 'none',
215
- },
216
- };
169
+ effects: {
170
+ customFull: (draft, original) => {
171
+ // 可以修改所有状态
172
+ draft.stats.sanity -= 10;
173
+ draft.flags.some_flag = true;
174
+ draft.inventory.push('new_item');
175
+ draft.extra.customData = 'value';
176
+ }
177
+ }
217
178
  ```
218
179
 
219
- ### 步骤 3: 创建 Store
180
+ ### 3. 条件性效果
220
181
 
221
182
  ```typescript
222
- // src/game/store.ts
223
- import { createGameEngineStore } from 'steamsheep-ts-game-engine/state';
224
- import { initialState } from './config/init';
225
-
226
- export const useGameStore = createGameEngineStore(
227
- initialState,
228
- 'my-game-save' // localStorage 键名
229
- );
230
-
231
- export type GameStore = ReturnType<typeof useGameStore>;
183
+ effects: {
184
+ conditionalEffects: (state) => {
185
+ if (state.flags.some_condition) {
186
+ return {
187
+ statsChange: { sanity: -10 },
188
+ flagsSet: { flag_a: true }
189
+ };
190
+ } else {
191
+ return {
192
+ statsChange: { sanity: -5 },
193
+ flagsSet: { flag_b: true }
194
+ };
195
+ }
196
+ }
197
+ }
232
198
  ```
233
199
 
234
- ### 步骤 4: 定义动作
200
+ ### 4. 职责分离 (afterEffects)
235
201
 
236
202
  ```typescript
237
- // src/game/content/actions.ts
238
- import type { ActionDef } from 'steamsheep-ts-game-engine/core';
239
-
240
- export const attackAction: ActionDef<Stats, Items, Flags, ExtraData> = {
241
- id: 'attack_goblin',
242
- label: '攻击哥布林',
243
- description: '用你的武器攻击哥布林',
244
-
245
- // 执行成本
246
- costs: {
247
- mp: 10,
248
- },
249
-
250
- // 执行条件
251
- requirements: {
252
- hasItems: ['sword'],
253
- stats: {
254
- hp: { min: 20 }, // 至少需要 20 点生命值
255
- },
256
- },
257
-
258
- // 执行效果
203
+ {
259
204
  effects: {
260
- statsChange: {
261
- exp: 10,
262
- gold: 5,
263
- },
264
- flagsSet: {
265
- quest_completed: true,
266
- },
267
- triggerEvent: 'goblin_defeated', // 触发自定义事件
205
+ statsChange: { sanity: -10 }
268
206
  },
269
207
 
270
- resultText: '你击败了哥布林!获得 10 经验和 5 金币。',
271
- };
272
- ```
273
-
274
- ### 步骤 5: 在 UI 中使用
275
-
276
- ```typescript
277
- // src/components/GameUI.tsx
278
- import { useGameStore } from '@/game/store';
279
- import { FlowSystem } from 'steamsheep-ts-game-engine/systems';
280
- import { QuerySystem } from 'steamsheep-ts-game-engine/systems';
281
- import { attackAction } from '@/game/content/actions';
282
-
283
- function GameUI() {
284
- const hp = useGameStore((state) => state.stats.hp);
285
- const gold = useGameStore((state) => state.stats.gold);
286
- const inventory = useGameStore((state) => state.inventory);
287
-
288
- const handleAttack = () => {
289
- const store = useGameStore.getState();
290
-
291
- // 检查是否可以执行
292
- const canExecute = QuerySystem.canExecuteAction(store, attackAction);
293
-
294
- if (canExecute) {
295
- // 执行动作
296
- FlowSystem.executeAction(store, attackAction);
297
- } else {
298
- store.showToast('无法执行该动作', 'warn');
208
+ // 副作用在这里执行
209
+ afterEffects: (state, original, actions) => {
210
+ if (state.stats.sanity < 20) {
211
+ actions.setFlag('going_mad', true);
212
+ actions.showToast('理智危险!', 'warn');
299
213
  }
300
- };
214
+ },
301
215
 
302
- return (
303
- <div>
304
- <div>生命值: {hp}</div>
305
- <div>金币: {gold}</div>
306
- <div>背包: {inventory.join(', ')}</div>
307
- <button onClick={handleAttack}>攻击哥布林</button>
308
- </div>
309
- );
216
+ // 文本生成在这里
217
+ resultText: (state) => {
218
+ return state.stats.sanity < 20
219
+ ? '你感到理智正在崩溃...'
220
+ : '仪式完成了。';
221
+ }
310
222
  }
311
223
  ```
312
224
 
313
- ---
314
-
315
- ## 核心系统
316
-
317
- ### 状态管理 (Store)
318
-
319
- Store 是游戏状态的中心,提供所有状态操作方法。
320
-
321
- #### 基本操作
225
+ ### 5. 动态 StatsChange
322
226
 
323
227
  ```typescript
324
- const store = useGameStore.getState();
325
-
326
- // 更新属性
327
- store.updateStat('hp', -10); // 减少 10 点生命值
328
- store.updateStat('gold', 50); // 增加 50 金币
329
-
330
- // 批量更新
331
- store.setStats({ hp: 10, mp: -5, gold: 100 });
332
-
333
- // 物品操作
334
- store.addItem('potion');
335
- store.removeItem('potion');
336
-
337
- // 标记操作
338
- store.setFlag('quest_completed', true);
339
-
340
- // 日志操作
341
- store.addLog('你进入了森林', 'info');
342
-
343
- // 时间操作
344
- store.advanceTime(1); // 推进 1 天
345
-
346
- // 传送
347
- store.teleport('dungeon');
348
-
349
- // 重置游戏
350
- store.reset();
351
- ```
352
-
353
- #### 扩展数据操作
354
-
355
- ```typescript
356
- // 直接更新
357
- store.setExtra({ reputation: 100 });
358
-
359
- // 基于当前值更新
360
- store.setExtra((prev) => ({
361
- reputation: prev.reputation + 10,
362
- }));
228
+ effects: {
229
+ statsChange: (state) => {
230
+ const maxAP = state.stats.max_action_point || 4;
231
+ return {
232
+ action_point: maxAP - state.stats.action_point
233
+ };
234
+ }
235
+ }
363
236
  ```
364
237
 
365
- #### 历史记录操作
238
+ ### 6. 批量 Flag 操作
366
239
 
367
240
  ```typescript
368
- // 保存快照
369
- store.saveSnapshot();
370
-
371
- // 撤销到上一个状态
372
- const success = store.undo();
373
- if (success) {
374
- console.log('撤销成功');
241
+ effects: {
242
+ // 清除所有 daily_ 开头的 flags
243
+ flagsBatch: {
244
+ clear: /^daily_/,
245
+ set: { new_day: true }
246
+ }
375
247
  }
376
- ```
377
-
378
- ---
379
-
380
- ### 事件系统 (Events)
381
248
 
382
- 事件系统用于解耦组件,实现发布-订阅模式。
383
-
384
- #### 预定义事件
385
-
386
- ```typescript
387
- export const EngineEvents = {
388
- STAT_CHANGE: 'engine:stat_change', // 属性变化
389
- ITEM_ADD: 'engine:item_add', // 物品添加
390
- ITEM_REMOVE: 'engine:item_remove', // 物品移除
391
- FLAG_CHANGE: 'engine:flag_change', // 标记变化
392
- ACTION_EXECUTED: 'engine:action_exec', // 动作执行
393
- TIME_PASS: 'engine:time_pass', // 时间推进
394
- MESSAGE: 'engine:message', // 消息通知
395
- CUSTOM: 'engine:custom_trigger', // 自定义事件
396
- };
249
+ // 或使用函数
250
+ effects: {
251
+ flagsBatch: (state) => ({
252
+ clear: Object.keys(state.flags).filter(k => k.startsWith('daily_')),
253
+ set: { new_day: true }
254
+ })
255
+ }
397
256
  ```
398
257
 
399
- #### 监听事件
258
+ ### 7. 时间推进副作用
400
259
 
401
260
  ```typescript
402
- import { gameEvents, EngineEvents } from 'steamsheep-ts-game-engine/systems';
403
-
404
- // 监听属性变化
405
- const unsubscribe = gameEvents.on(EngineEvents.STAT_CHANGE, (data) => {
406
- console.log(`${data.stat} 变化了 ${data.delta},当前值: ${data.current}`);
261
+ effects: {
262
+ timeAdvance: 1,
407
263
 
408
- // 生命值过低警告
409
- if (data.stat === 'hp' && data.current < 20) {
410
- playSound('warning');
264
+ onTimeAdvance: (currentDay, previousDay, state, actions) => {
265
+ // 恢复每日资源
266
+ const maxAP = state.stats.max_action_point || 4;
267
+ actions.setStats({
268
+ action_point: maxAP
269
+ });
270
+
271
+ // 清除每日标记
272
+ Object.keys(state.flags)
273
+ .filter(f => f.startsWith('daily_'))
274
+ .forEach(f => actions.setFlag(f as F, false));
411
275
  }
412
- });
413
-
414
- // 取消监听
415
- unsubscribe();
276
+ }
416
277
  ```
417
278
 
418
- #### 自定义事件
279
+ ### 8. 灵活的 Requirements
419
280
 
420
281
  ```typescript
421
- // 在动作中触发
422
- const action: ActionDef = {
423
- id: 'open_chest',
424
- effects: {
425
- triggerEvent: 'chest_opened',
426
- },
427
- // ...
428
- };
429
-
430
- // 监听自定义事件
431
- gameEvents.on(EngineEvents.CUSTOM, (data) => {
432
- if (data.id === 'chest_opened') {
433
- playSound('treasure');
434
- showAnimation('sparkle');
282
+ requirements: {
283
+ custom: (state) => {
284
+ if (!state.inventory.includes('key')) {
285
+ return {
286
+ passed: false,
287
+ reason: "需要钥匙才能打开"
288
+ };
289
+ }
290
+
291
+ if (state.stats.sanity < 20) {
292
+ return {
293
+ passed: false,
294
+ reason: "理智太低,无法集中精神"
295
+ };
296
+ }
297
+
298
+ return { passed: true };
435
299
  }
436
- });
437
- ```
438
-
439
- ---
440
-
441
- ### 历史管理 (History)
442
-
443
- 历史管理器提供撤销/重做功能。
444
-
445
- #### 基本用法
446
-
447
- ```typescript
448
- // 在重要操作前保存快照
449
- store.saveSnapshot();
450
-
451
- // 执行操作
452
- store.updateStat('hp', -50);
453
- store.addItem('cursed_item');
454
-
455
- // 撤销操作
456
- if (store.undo()) {
457
- console.log('已撤销');
458
300
  }
459
301
  ```
460
302
 
461
- #### 自动快照
462
-
463
- 可以在 `flow.ts` 中启用自动快照:
303
+ ### 9. 效果执行顺序
464
304
 
465
305
  ```typescript
466
- // src/engine/systems/flow.ts
467
- static executeAction(...) {
468
- // 取消注释以启用自动快照
469
- store.saveSnapshot();
306
+ effects: {
307
+ // 【阶段 1:静态效果】按以下顺序执行
308
+ statsChange: { sanity: -10 }, // 1
309
+ itemsAdd: ['item'], // 2
310
+ itemsRemove: ['old_item'], // 3
311
+ flagsSet: { flag: true }, // 4
312
+ flagsBatch: { clear: /^temp_/ }, // 5
313
+ teleport: 'new_location', // 6
314
+ timeAdvance: 1, // 7
315
+ onTimeAdvance: (day) => {}, // 7.1
316
+ triggerEvent: 'custom_event', // 8
317
+
318
+ // 【阶段 2:条件效果】
319
+ conditionalEffects: (state) => { // 9
320
+ // 基于阶段1后的状态
321
+ },
470
322
 
471
- // ... 执行动作
323
+ // 【阶段 3:自定义效果】
324
+ custom: (draft, state) => {}, // 10
325
+ customFull: (draft, original) => {}, // 11
472
326
  }
473
- ```
474
327
 
475
- ---
328
+ // 【阶段 4:后置钩子】
329
+ afterEffects: (state, original, actions) => {}, // 12
476
330
 
477
- ### 流程系统 (Flow)
478
-
479
- 流程系统负责执行动作的完整流程。
480
-
481
- #### 执行流程
482
-
483
- 1. **验证条件** - 检查需求和成本
484
- 2. **扣除成本** - 消耗资源
485
- 3. **应用效果** - 修改游戏状态
486
- 4. **记录日志** - 反馈给玩家
487
- 5. **发射事件** - 通知其他系统
488
-
489
- #### 使用方法
490
-
491
- ```typescript
492
- import { FlowSystem } from 'steamsheep-ts-game-engine/systems';
493
-
494
- const success = FlowSystem.executeAction(store, action);
495
-
496
- if (success) {
497
- console.log('动作执行成功');
498
- } else {
499
- console.log('动作执行失败');
500
- }
331
+ // 【阶段 5:结果文本】
332
+ resultText: (state) => {} // 13
501
333
  ```
502
334
 
503
- ---
504
-
505
- ### 查询系统 (Query)
506
-
507
- 查询系统提供各种状态查询方法。
508
-
509
- #### 常用查询
335
+ ### 10. 命名快照系统
510
336
 
511
337
  ```typescript
512
- import { QuerySystem } from 'steamsheep-ts-game-engine/systems';
338
+ // 保存命名快照
339
+ store.saveNamedSnapshot('before_boss_fight', '挑战Boss前');
513
340
 
514
- // 检查是否有物品
515
- const hasKey = QuerySystem.hasItem(state, 'key');
516
-
517
- // 检查是否有足够的资源
518
- const canAfford = QuerySystem.canAfford(state, { gold: 100 });
341
+ // 恢复到指定快照
342
+ if (store.restoreSnapshot('before_boss_fight')) {
343
+ console.log('已恢复到Boss战前');
344
+ }
519
345
 
520
- // 检查需求
521
- const meetsRequirements = QuerySystem.checkRequirements(state, {
522
- hasItems: ['sword'],
523
- stats: { hp: { min: 20 } },
346
+ // 查看所有快照
347
+ const snapshots = store.listSnapshots();
348
+ snapshots.forEach(snap => {
349
+ console.log(`${snap.name || '匿名'}: ${snap.description}`);
350
+ console.log(`时间: ${new Date(snap.timestamp).toLocaleString()}`);
524
351
  });
525
352
 
526
- // 检查是否可以执行动作
527
- const canExecute = QuerySystem.canExecuteAction(state, action);
528
-
529
- // 获取可用动作列表
530
- const availableActions = QuerySystem.getAvailableActions(state, allActions);
531
- ```
532
-
533
- ---
534
-
535
- ## 消息通知系统
536
-
537
- ### ⚠️ 重要架构说明
538
-
539
- 框架严格区分 **持久化记录** 和 **瞬时通知**:
540
-
541
- | 类型 | 持久化 | 刷新后 | 撤销后 | 用途 |
542
- |------|--------|--------|--------|------|
543
- | **Logs (日志)** | ✅ 是 | ✅ 重新加载 | ✅ 保留 | 历史记录,可回顾 |
544
- | **Toasts (飘字)** | ❌ 否 | ❌ 消失 | ❌ 消失 | 轻量级反馈 |
545
- | **Modals (弹窗)** | ❌ 否 | ❌ 消失 | ❌ 消失 | 重要信息,阻塞式 |
546
-
547
- ### 为什么要分离?
548
-
549
- **错误的做法**(将弹窗存入日志):
550
- ```typescript
551
- // ❌ 错误:弹窗会被持久化
552
- store.addLog('打开宝箱', 'info', 'modal');
553
-
554
- // Bug 1: 页面刷新后,弹窗再次出现!
555
- // Bug 2: 撤销操作后,弹窗记录还在,逻辑混乱!
556
- ```
557
-
558
- **正确的做法**(分离记录和通知):
559
- ```typescript
560
- // ✅ 正确:日志用于记录
561
- store.addLog('你打开了宝箱', 'result');
562
-
563
- // ✅ 正确:弹窗用于通知(不持久化)
564
- store.showModal('获得传说武器!', 'success');
565
- ```
566
-
567
- ### 1. 日志 (Logs)
568
-
569
- **持久化的历史记录**,会被保存到 localStorage:
570
-
571
- ```typescript
572
- // 记录游戏事件
573
- store.addLog('你进入了森林', 'info');
574
- store.addLog('击败了哥布林', 'result');
575
- store.addLog('获得了传说武器', 'success');
576
-
577
- // 这些日志会:
578
- // - 保存到 localStorage
579
- // - 页面刷新后重新加载
580
- // - 永久显示在日志面板
581
- // - 可以被玩家回顾
582
- ```
583
-
584
- ### 2. 飘字 (Toasts)
585
-
586
- **瞬时通知**,不会被持久化:
587
-
588
- ```typescript
589
- // 轻量级反馈
590
- store.showToast('获得 10 经验值', 'success');
591
- store.showToast('体力不足', 'warn');
592
-
593
- // 飘字会:
594
- // - 显示 3 秒后自动消失
595
- // - 不保存到 localStorage
596
- // - 页面刷新后不会重新出现
597
- // - 不阻塞用户操作
598
- ```
599
-
600
- ### 3. 弹窗 (Modals)
601
-
602
- **瞬时通知**,不会被持久化:
603
-
604
- ```typescript
605
- // 重要信息
606
- store.showModal('恭喜!你升级了!', 'success');
607
- store.showModal('警告:此操作不可撤销', 'warn');
608
-
609
- // 弹窗会:
610
- // - 需要用户点击确认
611
- // - 不保存到 localStorage
612
- // - 页面刷新后不会重新出现
613
- // - 阻塞用户操作
614
- ```
615
-
616
- ### UI 层实现
617
-
618
- 使用 `OverlaySystem` 组件处理瞬时通知:
619
-
620
- ```typescript
621
- // src/App.tsx
622
- import { OverlaySystem } from 'steamsheep-ts-game-engine/ui';
623
- import { LogStream } from 'steamsheep-ts-game-engine/ui';
624
-
625
- function App() {
626
- return (
627
- <div className="app">
628
- {/* 日志面板(持久化) */}
629
- <LogStream />
630
-
631
- {/* 覆盖层系统(瞬时通知) */}
632
- <OverlaySystem />
633
-
634
- {/* 游戏内容 */}
635
- <GameContent />
636
- </div>
637
- );
353
+ // 检查快照是否存在
354
+ if (store.hasSnapshot('before_boss_fight')) {
355
+ // 可以恢复
638
356
  }
639
- ```
640
357
 
641
- `OverlaySystem` 会自动监听 `NOTIFICATION` 事件并显示对应的 UI。
358
+ // 删除快照
359
+ store.deleteSnapshot('before_boss_fight');
360
+ ```
642
361
 
643
362
  ---
644
363
 
645
- ## 完整示例
646
-
647
- ### 创建一个简单的 RPG 游戏
364
+ ## 🎮 完整示例
648
365
 
649
366
  ```typescript
650
- // 1. 定义类型
651
- type Stats = 'hp' | 'mp' | 'gold' | 'exp' | 'level';
652
- type Items = 'sword' | 'potion' | 'key';
653
- type Flags = 'tutorial_done' | 'boss_defeated';
654
-
655
- // 2. 创建初始状态
656
- const initialState: GameState<Stats, Items, Flags> = {
657
- stats: { hp: 100, mp: 50, gold: 0, exp: 0, level: 1 },
658
- inventory: ['potion'],
659
- flags: { tutorial_done: false, boss_defeated: false },
660
- world: { currentLocationId: 'town', day: 1, time: 0 },
661
- logs: [],
662
- extra: {},
663
- };
664
-
665
- // 3. 创建 Store
666
- export const useGameStore = createGameEngineStore(initialState, 'rpg-save');
667
-
668
- // 4. 定义动作
669
- const buyPotionAction: ActionDef<Stats, Items, Flags> = {
670
- id: 'buy_potion',
671
- label: '购买药水',
672
- costs: { gold: 10 },
673
- effects: {
674
- itemsAdd: ['potion'],
675
- },
676
- resultText: '你购买了一瓶药水。',
677
- };
678
-
679
- const usePotionAction: ActionDef<Stats, Items, Flags> = {
680
- id: 'use_potion',
681
- label: '使用药水',
682
- requirements: {
683
- hasItems: ['potion'],
684
- stats: { hp: { max: 99 } }, // 生命值未满
685
- },
686
- effects: {
687
- statsChange: { hp: 30 },
688
- itemsRemove: ['potion'],
367
+ // 克苏鲁风格的仪式动作
368
+ const performRitualAction: ActionDef<Stats, Items, Flags, Extra> = {
369
+ id: 'perform_ritual',
370
+ label: '进行禁忌仪式',
371
+
372
+ costs: {
373
+ action_point: 2,
374
+ sanity: 5
689
375
  },
690
- resultText: (state) => `你使用了药水,恢复了 30 点生命值。当前生命值: ${state.stats.hp + 30}`,
691
- };
692
-
693
- const attackBossAction: ActionDef<Stats, Items, Flags> = {
694
- id: 'attack_boss',
695
- label: '挑战 Boss',
376
+
696
377
  requirements: {
697
- hasItems: ['sword'],
698
- stats: {
699
- level: { min: 5 },
700
- hp: { min: 50 },
378
+ hasItems: ['ancient_book'],
379
+ stats: {
380
+ knowledge: { min: 10 },
381
+ sanity: { min: 20 }
701
382
  },
383
+ custom: (state) => {
384
+ const time = state.world.time;
385
+ if (time < 18 && time > 6) {
386
+ return {
387
+ passed: false,
388
+ reason: '禁忌仪式只能在夜晚进行'
389
+ };
390
+ }
391
+ return { passed: true };
392
+ }
702
393
  },
703
- costs: {
704
- mp: 20,
705
- },
394
+
706
395
  effects: {
707
- statsChange: { exp: 100, gold: 50 },
708
- flagsSet: { boss_defeated: true },
709
- triggerEvent: 'boss_victory',
710
- },
711
- resultText: '你击败了 Boss!获得大量奖励!',
712
- };
713
-
714
- // 5. 设置事件监听
715
- function setupGameEvents() {
716
- // 升级检查
717
- gameEvents.on(EngineEvents.STAT_CHANGE, (data) => {
718
- if (data.stat === 'exp') {
719
- const store = useGameStore.getState();
720
- const expNeeded = store.stats.level * 100;
721
-
722
- if (store.stats.exp >= expNeeded) {
723
- store.updateStat('level', 1);
724
- store.updateStat('exp', -expNeeded);
725
- store.updateStat('hp', 20);
726
- store.updateStat('mp', 10);
727
- store.showModal('恭喜升级!', 'success');
396
+ // 动态属性变更
397
+ statsChange: (state) => ({
398
+ knowledge: state.world.time === 0 ? 5 : 3,
399
+ sanity: state.world.time === 0 ? -15 : -10
400
+ }),
401
+
402
+ // 动态标记
403
+ flagsSet: (state) => ({
404
+ [`ritual_day_${state.world.day}`]: true,
405
+ 'has_performed_ritual': true
406
+ }),
407
+
408
+ // 条件效果
409
+ conditionalEffects: (state) => {
410
+ if (state.stats.sanity < 30) {
411
+ return {
412
+ statsChange: { sanity: -5 },
413
+ flagsSet: { 'going_mad': true },
414
+ itemsAdd: ['cursed_artifact']
415
+ };
416
+ }
417
+ return null;
418
+ },
419
+
420
+ // 修改扩展数据
421
+ custom: (draft) => {
422
+ draft.lastActionTime = Date.now();
423
+ draft.reputation += 5;
424
+ },
425
+
426
+ // 完整状态修改
427
+ customFull: (draft, original) => {
428
+ if (original.inventory.includes('protective_charm')) {
429
+ const sanityLoss = original.stats.sanity - draft.stats.sanity;
430
+ draft.stats.sanity = original.stats.sanity - Math.floor(sanityLoss * 0.5);
431
+ draft.inventory = draft.inventory.filter(i => i !== 'protective_charm');
432
+ draft.flags.charm_used = true;
728
433
  }
729
434
  }
730
- });
731
-
732
- // Boss 胜利
733
- gameEvents.on(EngineEvents.CUSTOM, (data) => {
734
- if (data.id === 'boss_victory') {
735
- playSound('victory');
736
- showCutscene('ending');
435
+ },
436
+
437
+ afterEffects: (state, original, actions) => {
438
+ if (state.stats.knowledge >= 100 && !state.flags.master_scholar) {
439
+ actions.setFlag('master_scholar', true);
440
+ actions.showModal('成就解锁:博学大师!', 'success');
737
441
  }
738
- });
739
- }
740
-
741
- // 6. UI 组件
742
- function GameUI() {
743
- const stats = useGameStore((state) => state.stats);
744
- const inventory = useGameStore((state) => state.inventory);
745
- const flags = useGameStore((state) => state.flags);
746
-
747
- const handleAction = (action: ActionDef) => {
748
- const store = useGameStore.getState();
749
- const success = FlowSystem.executeAction(store, action);
750
442
 
751
- if (!success) {
752
- store.showToast('无法执行该动作', 'warn');
443
+ if (state.flags.midnight_ritual) {
444
+ actions.saveNamedSnapshot('after_midnight_ritual', '午夜仪式后');
753
445
  }
754
- };
755
-
756
- return (
757
- <div className="game-ui">
758
- {/* 状态栏 */}
759
- <div className="stats">
760
- <div>等级: {stats.level}</div>
761
- <div>生命值: {stats.hp}/100</div>
762
- <div>魔法值: {stats.mp}/50</div>
763
- <div>金币: {stats.gold}</div>
764
- <div>经验值: {stats.exp}</div>
765
- </div>
766
-
767
- {/* 背包 */}
768
- <div className="inventory">
769
- <h3>背包</h3>
770
- {inventory.map((item, i) => (
771
- <div key={i}>{item}</div>
772
- ))}
773
- </div>
774
-
775
- {/* 动作按钮 */}
776
- <div className="actions">
777
- <button onClick={() => handleAction(buyPotionAction)}>
778
- 购买药水 (10 金币)
779
- </button>
780
- <button onClick={() => handleAction(usePotionAction)}>
781
- 使用药水
782
- </button>
783
- {flags.tutorial_done && (
784
- <button onClick={() => handleAction(attackBossAction)}>
785
- 挑战 Boss
786
- </button>
787
- )}
788
- </div>
789
-
790
- {/* 撤销按钮 */}
791
- <button onClick={() => useGameStore.getState().undo()}>
792
- 撤销上一步
793
- </button>
794
- </div>
795
- );
796
- }
797
- ```
798
-
799
- ---
800
-
801
- ## 最佳实践
802
-
803
- ### 1. 类型定义
804
-
805
- ```typescript
806
- // ✅ 好的做法:使用字符串字面量联合类型
807
- type Stats = 'hp' | 'mp' | 'gold';
808
-
809
- // ❌ 避免:使用 string 类型
810
- type Stats = string;
811
- ```
812
-
813
- ### 2. 动作设计
814
-
815
- ```typescript
816
- // ✅ 好的做法:清晰的动作定义
817
- const action: ActionDef = {
818
- id: 'buy_sword',
819
- label: '购买剑',
820
- costs: { gold: 100 },
821
- effects: { itemsAdd: ['sword'] },
822
- resultText: '你购买了一把剑。',
823
- };
824
-
825
- // ❌ 避免:过于复杂的动作
826
- const action: ActionDef = {
827
- id: 'complex_action',
828
- // 太多效果,应该拆分成多个动作
829
- effects: {
830
- statsChange: { hp: 10, mp: -5, gold: -50, exp: 20 },
831
- itemsAdd: ['item1', 'item2', 'item3'],
832
- flagsSet: { flag1: true, flag2: false, flag3: true },
833
446
  },
834
- };
835
- ```
836
-
837
- ### 3. 需求检查
838
-
839
- ```typescript
840
- // ✅ 好的做法:使用 QuerySystem
841
- if (QuerySystem.canExecuteAction(store, action)) {
842
- FlowSystem.executeAction(store, action);
843
- }
844
-
845
- // ❌ 避免:手动检查
846
- if (store.stats.gold >= 100 && store.inventory.includes('key')) {
847
- FlowSystem.executeAction(store, action);
848
- }
849
- ```
850
-
851
- ### 4. 事件监听
852
-
853
- ```typescript
854
- // ✅ 好的做法:在组件卸载时取消监听
855
- useEffect(() => {
856
- const unsubscribe = gameEvents.on(EngineEvents.STAT_CHANGE, handler);
857
- return unsubscribe; // 清理
858
- }, []);
859
-
860
- // ❌ 避免:忘记取消监听(内存泄漏)
861
- useEffect(() => {
862
- gameEvents.on(EngineEvents.STAT_CHANGE, handler);
863
- }, []);
864
- ```
865
-
866
- ### 5. 状态更新
867
-
868
- ```typescript
869
- // ✅ 好的做法:使用 Store 提供的方法
870
- store.updateStat('hp', -10);
871
-
872
- // ❌ 避免:直接修改状态(不会触发更新)
873
- store.stats.hp -= 10;
874
- ```
875
-
876
- ### 6. 消息使用
877
-
878
- ```typescript
879
- // ✅ 好的做法:根据重要性选择合适的展示方式
880
- store.addLog('你走进了房间', 'info'); // 普通信息
881
- store.showToast('获得 10 金币', 'success'); // 轻量提示
882
- store.showModal('任务完成!', 'success'); // 重要信息
883
-
884
- // ❌ 避免:所有消息都用弹窗(打扰用户)
885
- store.showModal('你走了一步', 'info');
886
- ```
887
-
888
- ### 7. 扩展数据
889
-
890
- ```typescript
891
- // ✅ 好的做法:定义明确的扩展数据类型
892
- interface ExtraData {
893
- reputation: number;
894
- guild: string;
895
- achievements: string[];
896
- }
897
-
898
- // ❌ 避免:使用 any 或过于宽泛的类型
899
- type ExtraData = any;
900
- ```
901
-
902
- ---
903
-
904
- ## 常见问题
905
-
906
- ### Q: 如何添加新的属性类型?
907
-
908
- A: 在类型定义中添加新的字符串字面量:
909
-
910
- ```typescript
911
- // 添加 'stamina' 属性
912
- type Stats = 'hp' | 'mp' | 'gold' | 'stamina';
913
- ```
914
-
915
- ### Q: 如何实现复杂的条件判断?
916
-
917
- A: 使用 `custom` 函数:
918
-
919
- ```typescript
920
- requirements: {
921
- custom: (state) => {
922
- // 复杂逻辑:(有钥匙 AND 力量>=5) OR 有万能钥匙
923
- const hasKeyAndStrength =
924
- state.inventory.includes('key') && state.stats.strength >= 5;
925
- const hasMasterKey = state.inventory.includes('master_key');
926
- return hasKeyAndStrength || hasMasterKey;
927
- }
928
- }
929
- ```
930
-
931
- ### Q: 如何保存多个存档?
932
-
933
- A: 使用不同的 `persistName`:
934
-
935
- ```typescript
936
- const slot1 = createGameEngineStore(initialState, 'save-slot-1');
937
- const slot2 = createGameEngineStore(initialState, 'save-slot-2');
938
- const slot3 = createGameEngineStore(initialState, 'save-slot-3');
939
- ```
940
-
941
- ### Q: 如何实现自动保存?
942
-
943
- A: 监听关键事件并保存快照:
944
-
945
- ```typescript
946
- gameEvents.on(EngineEvents.ACTION_EXECUTED, () => {
947
- useGameStore.getState().saveSnapshot();
948
- });
949
- ```
950
-
951
- ---
952
-
953
- ## 进阶主题
954
-
955
- ### 自定义系统扩展
956
-
957
- 你可以创建自己的系统来扩展引擎功能:
958
-
959
- ```typescript
960
- // src/game/systems/achievement.ts
961
- export class AchievementSystem {
962
- static checkAchievements(store: GameStore) {
963
- // 检查成就解锁条件
964
- if (store.stats.gold >= 1000 && !store.flags.rich_achievement) {
965
- store.setFlag('rich_achievement', true);
966
- store.showModal('成就解锁:富豪', 'success');
447
+
448
+ resultText: (state) => {
449
+ const parts = [];
450
+ if (state.flags.midnight_ritual) {
451
+ parts.push('午夜时分,仪式达到了高潮');
967
452
  }
453
+ if (state.stats.sanity < 20) {
454
+ parts.push('你的理智岌岌可危');
455
+ }
456
+ if (state.flags.charm_used) {
457
+ parts.push('护符保护了你,但它已经碎裂了');
458
+ }
459
+ return parts.join(',') + '。';
968
460
  }
969
- }
970
-
971
- // 在事件监听中使用
972
- gameEvents.on(EngineEvents.STAT_CHANGE, (data) => {
973
- if (data.stat === 'gold') {
974
- AchievementSystem.checkAchievements(useGameStore.getState());
975
- }
976
- });
977
- ```
978
-
979
- ### 动态内容加载
980
-
981
- ```typescript
982
- // 从 JSON 文件加载动作
983
- import actionsData from './data/actions.json';
984
-
985
- const actions: ActionDef[] = actionsData.map(data => ({
986
- ...data,
987
- // 转换 JSON 数据为 ActionDef
988
- }));
461
+ };
989
462
  ```
990
463
 
991
464
  ---
992
465
 
993
- ## 总结
994
-
995
- 这个游戏引擎框架提供了:
996
-
997
- - 🎯 **类型安全** - 完整的 TypeScript 支持
998
- - 🔄 **状态管理** - 基于 Zustand 的响应式状态
999
- - 📡 **事件系统** - 解耦的发布-订阅模式
1000
- - ⏮️ **历史管理** - 撤销/重做功能
1001
- - 💬 **消息系统** - 多级消息通知
1002
- - 🎮 **流程控制** - 完整的动作执行流程
1003
- - 🔍 **查询系统** - 便捷的状态查询
1004
-
1005
- 通过这些系统的组合,你可以快速构建各种类型的文字冒险和 RPG 游戏!
1006
-
1007
- ---
1008
-
1009
- ## 迁移指南
1010
-
1011
- ### 从旧版本迁移
466
+ ## 📝 总结
1012
467
 
1013
- 如果你之前使用了错误的 `addLog` 设计(带 `display` 参数),需要进行以下修改:
468
+ 本引擎提供了以下增强功能:
1014
469
 
1015
- #### 旧的错误做法
1016
-
1017
- ```typescript
1018
- // 错误:将弹窗存入日志(会导致持久化问题)
1019
- store.addLog('打开宝箱', 'info', 'modal');
1020
- store.addLog('获得金币', 'success', 'toast');
1021
- ```
1022
-
1023
- **问题**:
1024
- - 页面刷新后,弹窗会再次出现
1025
- - 撤销操作后,弹窗记录仍然存在
1026
- - 日志面板会显示不应该显示的内容
1027
-
1028
- #### ✅ 新的正确做法
1029
-
1030
- ```typescript
1031
- // 正确:分离记录和通知
1032
-
1033
- // 1. 添加历史记录(持久化)
1034
- store.addLog('你打开了宝箱', 'result');
1035
-
1036
- // 2. 显示瞬时通知(不持久化)
1037
- store.showModal('获得传说武器!', 'success');
1038
- store.showToast('获得 10 金币', 'success');
1039
- ```
470
+ 1. **动态 Flag 支持** - flagsSet 支持函数形式
471
+ 2. ✅ **完整状态访问** - customFull 可修改所有状态
472
+ 3. ✅ **条件性效果** - conditionalEffects 根据状态决定效果
473
+ 4. ✅ **职责分离** - afterEffects 和 resultText 分离副作用和文本
474
+ 5. **动态 StatsChange** - statsChange 支持函数形式
475
+ 6. **批量 Flag 操作** - flagsBatch 支持正则、前缀、列表
476
+ 7. ✅ **时间推进副作用** - onTimeAdvance 自动处理每日重置
477
+ 8. ✅ **灵活的 Requirements** - custom 可返回失败原因
478
+ 9. ✅ **明确的执行顺序** - 5 个阶段,清晰的文档说明
479
+ 10. ✅ **命名快照系统** - 支持保存和恢复命名快照
1040
480
 
1041
- #### 需要修改的地方
1042
-
1043
- 1. **移除 `addLog` 的第三个参数**
1044
- ```typescript
1045
- // 旧代码
1046
- store.addLog('消息', 'info', 'toast');
1047
-
1048
- // 新代码
1049
- store.addLog('消息', 'info'); // 仅用于历史记录
1050
- store.showToast('消息', 'info'); // 用于通知
1051
- ```
1052
-
1053
- 2. **添加 `OverlaySystem` 组件**
1054
- ```typescript
1055
- // src/App.tsx
1056
- import { OverlaySystem } from 'steamsheep-ts-game-engine/ui';
1057
-
1058
- function App() {
1059
- return (
1060
- <>
1061
- <OverlaySystem /> {/* 新增:处理飘字和弹窗 */}
1062
- <GameContent />
1063
- </>
1064
- );
1065
- }
1066
- ```
1067
-
1068
- 3. **更新类型定义**
1069
- - `LogEntry` 不再有 `display` 字段
1070
- - 使用 `NotificationPayload` 处理通知
1071
- - 监听 `NOTIFICATION` 事件而不是 `MESSAGE` 事件
481
+ 所有功能都是类型安全的,并且完全向后兼容!
1072
482
 
1073
483
  ---
1074
484
 
1075
- ## 相关文档
485
+ ## 📚 更多文档
1076
486
 
487
+ - [完整功能示例](./examples/complete-features-example.ts)
488
+ - [动态 Flag 示例](./examples/dynamic-flags-example.ts)
1077
489
  - [核心类型定义](./core/types.ts)
1078
490
  - [状态管理](./state/store.ts)
1079
- - [事件系统](./systems/events.ts)
1080
491
  - [历史管理](./state/history.ts)
1081
- - [流程系统](./systems/flow.ts)
1082
- - [查询系统](./systems/query.ts)
1083
- - [覆盖层系统](./ui/OverlaySystem.tsx)
1084
492
 
1085
- ## 许可证
493
+ ## 📄 许可证
1086
494
 
1087
495
  MIT