steamsheep-ts-game-engine 2.0.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.
@@ -129,37 +129,85 @@ var HistoryManager = class {
129
129
  * @param maxSnapshots - 最大保存的快照数量,默认使用配置中的值
130
130
  */
131
131
  constructor(maxSnapshots = DEFAULT_CONFIG.MAX_HISTORY_SNAPSHOTS) {
132
- /** 存储的状态快照数组 */
132
+ /** 存储的状态快照数组(按时间顺序) */
133
133
  __publicField(this, "snapshots", []);
134
- /** 最大快照数量限制 */
134
+ /** 命名快照映射表(name -> snapshot) */
135
+ __publicField(this, "namedSnapshots", /* @__PURE__ */ new Map());
136
+ /** 最大快照数量限制(不包括命名快照) */
135
137
  __publicField(this, "maxSnapshots");
136
138
  this.maxSnapshots = maxSnapshots;
137
139
  }
138
140
  /**
139
- * 保存当前状态快照
141
+ * 保存当前状态快照(匿名快照)
140
142
  *
141
- * 通过深拷贝保存状态,防止后续修改影响历史记录
142
- * 当快照数量超过最大限制时,自动移除最旧的快照
143
+ * 通过深拷贝保存状态,防止后续修改影响历史记录。
144
+ * 当快照数量超过最大限制时,自动移除最旧的快照。
145
+ *
146
+ * 匿名快照用于常规的撤销操作,会受到数量限制。
143
147
  *
144
148
  * @param state - 要保存的游戏状态
149
+ * @param description - 快照描述(可选)
145
150
  *
146
151
  * @example
147
152
  * ```typescript
148
153
  * history.push(gameState);
154
+ * history.push(gameState, '攻击哥布林前');
149
155
  * ```
150
156
  */
151
- push(state) {
152
- const snapshot = JSON.parse(JSON.stringify(state));
157
+ push(state, description) {
158
+ const stateCopy = JSON.parse(JSON.stringify(state));
159
+ const snapshot = {
160
+ id: `snapshot_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
161
+ timestamp: Date.now(),
162
+ description,
163
+ state: stateCopy
164
+ };
153
165
  this.snapshots.push(snapshot);
154
166
  if (this.snapshots.length > this.maxSnapshots) {
155
167
  this.snapshots.shift();
156
168
  }
157
169
  }
170
+ /**
171
+ * 保存命名快照
172
+ *
173
+ * ⚠️ 新功能:保存可以通过名称访问的快照
174
+ *
175
+ * 命名快照不受数量限制,不会被自动清理。
176
+ * 适用于重要的游戏节点,如Boss战前、章节开始等。
177
+ *
178
+ * 如果已存在同名快照,会被覆盖。
179
+ *
180
+ * @param state - 要保存的游戏状态
181
+ * @param name - 快照名称(唯一标识符)
182
+ * @param description - 快照描述(可选)
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * // 保存Boss战前的状态
187
+ * history.pushNamed(gameState, 'before_boss_fight', '挑战最终Boss前');
188
+ *
189
+ * // 保存章节开始的状态
190
+ * history.pushNamed(gameState, 'chapter_2_start', '第二章开始');
191
+ * ```
192
+ */
193
+ pushNamed(state, name, description) {
194
+ const stateCopy = JSON.parse(JSON.stringify(state));
195
+ const snapshot = {
196
+ id: `named_${name}_${Date.now()}`,
197
+ name,
198
+ timestamp: Date.now(),
199
+ description,
200
+ state: stateCopy
201
+ };
202
+ this.namedSnapshots.set(name, snapshot);
203
+ }
158
204
  /**
159
205
  * 回退到上一个状态(撤销操作)
160
206
  *
161
- * 移除并返回最近的一个快照
162
- * 如果没有可用的快照,返回 undefined
207
+ * 移除并返回最近的一个匿名快照。
208
+ * 如果没有可用的快照,返回 undefined
209
+ *
210
+ * 注意:此操作不会影响命名快照。
163
211
  *
164
212
  * @returns 上一个状态快照,如果历史为空则返回 undefined
165
213
  *
@@ -173,13 +221,136 @@ var HistoryManager = class {
173
221
  * ```
174
222
  */
175
223
  pop() {
176
- return this.snapshots.pop();
224
+ const snapshot = this.snapshots.pop();
225
+ return snapshot?.state;
226
+ }
227
+ /**
228
+ * 恢复到命名快照
229
+ *
230
+ * ⚠️ 新功能:通过名称恢复到指定的快照
231
+ *
232
+ * 返回指定名称的快照状态,但不从历史记录中删除它。
233
+ * 这允许多次恢复到同一个命名快照。
234
+ *
235
+ * @param name - 快照名称
236
+ * @returns 指定名称的状态快照,如果不存在则返回 undefined
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * // 恢复到Boss战前
241
+ * const savedState = history.restoreNamed('before_boss_fight');
242
+ * if (savedState) {
243
+ * restoreState(savedState);
244
+ * } else {
245
+ * console.log('快照不存在');
246
+ * }
247
+ * ```
248
+ */
249
+ restoreNamed(name) {
250
+ const snapshot = this.namedSnapshots.get(name);
251
+ return snapshot?.state;
252
+ }
253
+ /**
254
+ * 删除命名快照
255
+ *
256
+ * 从历史记录中删除指定名称的快照。
257
+ *
258
+ * @param name - 要删除的快照名称
259
+ * @returns 是否成功删除
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * history.deleteNamed('before_boss_fight');
264
+ * ```
265
+ */
266
+ deleteNamed(name) {
267
+ return this.namedSnapshots.delete(name);
268
+ }
269
+ /**
270
+ * 检查命名快照是否存在
271
+ *
272
+ * @param name - 快照名称
273
+ * @returns 是否存在指定名称的快照
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * if (history.hasNamed('before_boss_fight')) {
278
+ * console.log('可以恢复到Boss战前');
279
+ * }
280
+ * ```
281
+ */
282
+ hasNamed(name) {
283
+ return this.namedSnapshots.has(name);
284
+ }
285
+ /**
286
+ * 列出所有快照
287
+ *
288
+ * ⚠️ 新功能:获取所有快照的信息列表
289
+ *
290
+ * 返回所有快照(包括匿名和命名快照)的元数据。
291
+ * 不包含实际的状态数据,只包含快照信息。
292
+ *
293
+ * @returns 快照信息数组,按时间倒序排列(最新的在前)
294
+ *
295
+ * @example
296
+ * ```typescript
297
+ * const snapshots = history.listSnapshots();
298
+ * snapshots.forEach(snap => {
299
+ * console.log(`${snap.name || '匿名'}: ${snap.description || '无描述'}`);
300
+ * console.log(` 时间: ${new Date(snap.timestamp).toLocaleString()}`);
301
+ * });
302
+ * ```
303
+ */
304
+ listSnapshots() {
305
+ const allSnapshots = [];
306
+ this.snapshots.forEach((snap) => {
307
+ allSnapshots.push({
308
+ id: snap.id,
309
+ name: snap.name,
310
+ timestamp: snap.timestamp,
311
+ description: snap.description
312
+ });
313
+ });
314
+ this.namedSnapshots.forEach((snap) => {
315
+ allSnapshots.push({
316
+ id: snap.id,
317
+ name: snap.name,
318
+ timestamp: snap.timestamp,
319
+ description: snap.description
320
+ });
321
+ });
322
+ return allSnapshots.sort((a, b) => b.timestamp - a.timestamp);
323
+ }
324
+ /**
325
+ * 列出所有命名快照
326
+ *
327
+ * 只返回命名快照的信息列表。
328
+ *
329
+ * @returns 命名快照信息数组
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * const namedSnaps = history.listNamedSnapshots();
334
+ * console.log(`共有 ${namedSnaps.length} 个命名快照`);
335
+ * ```
336
+ */
337
+ listNamedSnapshots() {
338
+ const result = [];
339
+ this.namedSnapshots.forEach((snap) => {
340
+ result.push({
341
+ id: snap.id,
342
+ name: snap.name,
343
+ timestamp: snap.timestamp,
344
+ description: snap.description
345
+ });
346
+ });
347
+ return result.sort((a, b) => b.timestamp - a.timestamp);
177
348
  }
178
349
  /**
179
350
  * 查看最近的快照但不移除
180
351
  *
181
- * 返回最新的快照但不从历史记录中删除它
182
- * 用于预览上一个状态而不实际执行撤销操作
352
+ * 返回最新的匿名快照但不从历史记录中删除它。
353
+ * 用于预览上一个状态而不实际执行撤销操作。
183
354
  *
184
355
  * @returns 最近的状态快照,如果历史为空则返回 undefined
185
356
  *
@@ -192,26 +363,36 @@ var HistoryManager = class {
192
363
  * ```
193
364
  */
194
365
  peek() {
195
- return this.snapshots[this.snapshots.length - 1];
366
+ const snapshot = this.snapshots[this.snapshots.length - 1];
367
+ return snapshot?.state;
196
368
  }
197
369
  /**
198
370
  * 清空所有历史记录
199
371
  *
200
- * 移除所有保存的快照,释放内存
201
- * 通常在开始新游戏或重置时使用
372
+ * 移除所有保存的快照(包括匿名和命名快照),释放内存。
373
+ * 通常在开始新游戏或重置时使用。
374
+ *
375
+ * @param includeNamed - 是否同时清空命名快照(默认为 false)
202
376
  *
203
377
  * @example
204
378
  * ```typescript
205
- * history.clear(); // 清空所有历史
379
+ * // 只清空匿名快照
380
+ * history.clear();
381
+ *
382
+ * // 清空所有快照(包括命名快照)
383
+ * history.clear(true);
206
384
  * ```
207
385
  */
208
- clear() {
386
+ clear(includeNamed = false) {
209
387
  this.snapshots = [];
388
+ if (includeNamed) {
389
+ this.namedSnapshots.clear();
390
+ }
210
391
  }
211
392
  /**
212
- * 获取当前历史记录数量
393
+ * 获取当前匿名快照数量
213
394
  *
214
- * @returns 当前保存的快照数量
395
+ * @returns 当前保存的匿名快照数量
215
396
  *
216
397
  * @example
217
398
  * ```typescript
@@ -224,6 +405,32 @@ var HistoryManager = class {
224
405
  get size() {
225
406
  return this.snapshots.length;
226
407
  }
408
+ /**
409
+ * 获取命名快照数量
410
+ *
411
+ * @returns 当前保存的命名快照数量
412
+ *
413
+ * @example
414
+ * ```typescript
415
+ * console.log(`命名快照数量: ${history.namedSize}`);
416
+ * ```
417
+ */
418
+ get namedSize() {
419
+ return this.namedSnapshots.size;
420
+ }
421
+ /**
422
+ * 获取总快照数量
423
+ *
424
+ * @returns 所有快照的总数量(匿名 + 命名)
425
+ *
426
+ * @example
427
+ * ```typescript
428
+ * console.log(`总快照数量: ${history.totalSize}`);
429
+ * ```
430
+ */
431
+ get totalSize() {
432
+ return this.snapshots.length + this.namedSnapshots.size;
433
+ }
227
434
  };
228
435
 
229
436
  // core/messages.ts
@@ -412,19 +619,29 @@ var createGameEngineStore = (initialState, persistName = "generic-rpg-save") =>
412
619
  reset: () => set({ ...initialState, logs: [] }),
413
620
  // ========== 历史记录操作实现 ==========
414
621
  /**
415
- * 实现:保存当前状态快照
622
+ * 实现:保存当前状态快照(匿名快照)
416
623
  *
417
624
  * 保存除日志外的所有状态数据到历史管理器。
418
625
  * 日志通常不需要回退,因此被排除以节省内存。
419
626
  */
420
- saveSnapshot: () => {
627
+ saveSnapshot: (description) => {
421
628
  const currentState = get();
422
- history.push(currentState);
629
+ history.push(currentState, description);
630
+ },
631
+ /**
632
+ * 实现:保存命名快照
633
+ *
634
+ * 保存可以通过名称访问的快照。
635
+ * 命名快照不受数量限制,适用于重要的游戏节点。
636
+ */
637
+ saveNamedSnapshot: (name, description) => {
638
+ const currentState = get();
639
+ history.pushNamed(currentState, name, description);
423
640
  },
424
641
  /**
425
642
  * 实现:撤销到上一个状态
426
643
  *
427
- * 从历史管理器中恢复上一个快照。
644
+ * 从历史管理器中恢复上一个匿名快照。
428
645
  * 恢复时保留当前的日志,并添加系统提示。
429
646
  *
430
647
  * @returns 是否成功撤销
@@ -444,6 +661,56 @@ var createGameEngineStore = (initialState, persistName = "generic-rpg-save") =>
444
661
  return true;
445
662
  }
446
663
  return false;
664
+ },
665
+ /**
666
+ * 实现:恢复到命名快照
667
+ *
668
+ * 通过名称恢复到指定的快照。
669
+ * 快照不会被删除,可以多次恢复。
670
+ *
671
+ * @returns 是否成功恢复
672
+ */
673
+ restoreSnapshot: (name) => {
674
+ const saved = history.restoreNamed(name);
675
+ if (saved) {
676
+ set({
677
+ stats: saved.stats,
678
+ inventory: saved.inventory,
679
+ flags: saved.flags,
680
+ world: saved.world,
681
+ extra: saved.extra
682
+ // logs 保留当前的,不回退日志
683
+ });
684
+ get().addLog(`\u5DF2\u6062\u590D\u5230\u5FEB\u7167\uFF1A${name}`, "info");
685
+ return true;
686
+ }
687
+ return false;
688
+ },
689
+ /**
690
+ * 实现:删除命名快照
691
+ *
692
+ * 从历史记录中删除指定名称的快照。
693
+ *
694
+ * @returns 是否成功删除
695
+ */
696
+ deleteSnapshot: (name) => {
697
+ return history.deleteNamed(name);
698
+ },
699
+ /**
700
+ * 实现:列出所有快照
701
+ *
702
+ * 返回所有快照的元数据列表。
703
+ */
704
+ listSnapshots: () => {
705
+ return history.listSnapshots();
706
+ },
707
+ /**
708
+ * 实现:检查命名快照是否存在
709
+ *
710
+ * @returns 是否存在指定名称的快照
711
+ */
712
+ hasSnapshot: (name) => {
713
+ return history.hasNamed(name);
447
714
  }
448
715
  }),
449
716
  {