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