mirror of
https://github.com/motajs/template.git
synced 2026-07-17 01:11:09 +08:00
Compare commits
No commits in common. "2d6f7092016523d97ac8f902d925985488d7184e" and "94a16b31ba237303384000d13600671dd4c05ae7" have entirely different histories.
2d6f709201
...
94a16b31ba
@ -1,121 +0,0 @@
|
|||||||
# 需求综述
|
|
||||||
|
|
||||||
参考 `ITileStore` 的设计,为道具系统定义 Layer 0 的原始数据存储层,包含 `IItemRawData`、`IItemLegacyConverter`、`IItemStore` 接口及 `ItemStore` 实现类,并挂载至 `IDataCommon`。道具本身是一类 tile 的具体属性,通过 tile 即可定位,不需要独立的 id↔num 双向索引。
|
|
||||||
|
|
||||||
# 接口设计分析
|
|
||||||
|
|
||||||
## ItemCategory 枚举
|
|
||||||
|
|
||||||
在 `store/types.ts` 中与 `TileType` 并列定义,用于标识道具分类:
|
|
||||||
|
|
||||||
- `Unknown`:未知或尚未归类的道具。
|
|
||||||
- `Constant`:永久道具,获得后常驻生效。
|
|
||||||
- `Consumable`:一次性道具,使用后消耗。
|
|
||||||
- `Pick`:即捡即用道具,捡到立刻生效。
|
|
||||||
- `Equipment`:装备类道具,可装备 / 卸下。
|
|
||||||
|
|
||||||
### 设计意图
|
|
||||||
|
|
||||||
放在 `store/types.ts` 而非 `data-common/src/types.ts`,以避免 `types.ts` ↔ `store/types.ts` 之间的循环引用。`types.ts` 通过 `./store` 导入 `IItemStore` 挂载到 `IDataCommon`;若将 `ItemCategory` 放在 `types.ts`,`store/types.ts` 引用它时会产生循环。
|
|
||||||
|
|
||||||
## IItemRawData
|
|
||||||
|
|
||||||
### 设计意图
|
|
||||||
|
|
||||||
参考 `ITileRawData` 的设计,`IItemRawData` 是道具的原始数据定义,存储于 `IItemStore` 中。除基础数据字段外,通过 `readonly effect: IItemEffect` 成员承载道具效果。旧样板字符串效果由 `IItemLegacyConverter` 在转换时通过 `new Function` 编译为 `IItemEffect` 实例。
|
|
||||||
|
|
||||||
`IItemStore` 作为 Layer 0 的基础设施被 `IDataCommon` 持有,与 `ITileStore` 平级。后续 `IItemManager`(Layer 1)通过 `IDataCommonExtended.state.itemStore` 访问原始数据,据此创建 `IItem<TAttr>` 运行时实例。
|
|
||||||
|
|
||||||
### 接口分析
|
|
||||||
|
|
||||||
- `IItemRawData.num`:预期频率**高频**。道具在地图上的图块数字,是地图→道具的核心关联键。典型使用场景:事件触发器读取地图上某图块对应的道具信息。
|
|
||||||
- `IItemRawData.id`:预期频率**高频**。道具的字符串唯一标识符。典型使用场景:通过 id 跨地图引用道具。
|
|
||||||
- `IItemRawData.category`:预期频率**中频**。道具分类,决定使用时的基础行为分支。典型使用场景:拾取道具时判断是否为 `Pick` 类以立即触发效果。
|
|
||||||
- `IItemRawData.name`:预期频率**高频**。道具显示名称,UI 展示的核心字段。
|
|
||||||
- `IItemRawData.text`:预期频率**低频**。道具描述文本,仅详情展示时出现。
|
|
||||||
- `IItemRawData.effect`:预期频率**中频**。道具效果对象,包含 `pickEffect`、`useEffect`、`canUse` 三个方法。典型使用场景:拾取道具时调用 `effect.pickEffect(state)` 触发即捡即用效果。
|
|
||||||
|
|
||||||
### 预期体量
|
|
||||||
|
|
||||||
`IItemRawData` 约 15–20 行(含 `effect` 成员)。
|
|
||||||
|
|
||||||
## IItemEffect
|
|
||||||
|
|
||||||
### 设计意图
|
|
||||||
|
|
||||||
道具效果接口,与 `IItemRawData` 并列定义在 `store/types.ts` 中。包含 `pickEffect`、`useEffect`、`canUse` 三个方法,参数为当前道具的 `IItemRawData`。由 `IItemLegacyConverter` 在转换时通过 `new Function` 编译实例化,编译函数内部自行持有 `IStateSystem` 引用(由实现类构造函数传入),方法内据此调用编译函数。
|
|
||||||
|
|
||||||
### 接口分析
|
|
||||||
|
|
||||||
- `IItemEffect.pickEffect`:预期频率**低频**。拾取道具时调用(仅 `Pick` 类型),参数为当前道具数据。
|
|
||||||
- `IItemEffect.useEffect`:预期频率**中频**。使用道具时调用(`Constant` 和 `Consumable` 类型),参数为当前道具数据。典型使用场景:使用消耗道具触发属性变化。
|
|
||||||
- `IItemEffect.canUse`:预期频率**中频**。判定道具是否可用(`Constant` 和 `Consumable` 类型),参数为当前道具数据。典型使用场景:开门钥匙判断是否满足使用条件。
|
|
||||||
|
|
||||||
### 预期体量
|
|
||||||
|
|
||||||
`IItemEffect` 约 12–15 行。
|
|
||||||
|
|
||||||
## IItemLegacyConverter
|
|
||||||
|
|
||||||
### 设计意图
|
|
||||||
|
|
||||||
参考 `ITileLegacyConverter`,定义从旧样板道具对象到 `IItemRawData` 的转换器接口。转换时需将旧样板中的字符串效果(`itemEffect`、`useItemEffect`、`canUseItemEffect`)通过 `new Function` 编译为 `IItemEffect` 实例,赋给 `IItemRawData.effect`。编编译函数签名为 `(state: IStateSystem) => void`(`canUse` 返回 `boolean`),实现类内部自行持有 `IStateSystem`,接口方法仅传递 `IItemRawData`。
|
|
||||||
|
|
||||||
与后续 `IItemLegacyBridge` 不同——`IItemLegacyConverter` 位于 store 层负责原始数据的导入(含效果编译),`IItemLegacyBridge`(Layer 1)负责运行时属性的转换。二者分层独立,分别处理静态数据与运行时对象。
|
|
||||||
|
|
||||||
### 接口分析
|
|
||||||
|
|
||||||
- `IItemLegacyConverter.fromLegacy`:预期频率**低频**。仅在样板加载阶段调用,执行旧格式→新格式的批量转换,含字符串效果的编译。
|
|
||||||
|
|
||||||
### 预期体量
|
|
||||||
|
|
||||||
一个方法,约 8–10 行。
|
|
||||||
|
|
||||||
## IItemStore
|
|
||||||
|
|
||||||
### 设计意图
|
|
||||||
|
|
||||||
参考 `ITileStore` 的接口模式,作为道具原始数据的集中存储。道具本身是一类 tile 的具体属性,通过 tile 即可查找到对应的道具信息,因此不需要 `ITileStore` 那样的双向 id↔num 索引。但数据内仍存储 `id` 与 `num`,方便在需要时进行查询。
|
|
||||||
|
|
||||||
### 接口分析
|
|
||||||
|
|
||||||
- `IItemStore.getData`:预期频率**高频**。按图块数字获取完整原始数据,是查询道具定义的最常用入口。典型使用场景:地图图层解析到某图块数字后读取道具定义。
|
|
||||||
- `IItemStore.getCategory`:预期频率**中频**。快速判断道具分类,避免完整获取数据。典型使用场景:拾取判定时只关心是否为 `Pick` 类型。
|
|
||||||
- `IItemStore.addItem`:预期频率**低频**。添加一个道具原始定义,仅在样板加载时调用。
|
|
||||||
- `IItemStore.attachLegacyConverter`:预期频率**低频**。挂载旧样板转换器,初始化阶段调用。
|
|
||||||
- `IItemStore.fromLegacy`:预期频率**低频**。使用当前转换器转换并写入一个旧样板道具定义。
|
|
||||||
|
|
||||||
### 预期体量
|
|
||||||
|
|
||||||
接口约 20–25 行,实现类 `ItemStore` 约 50–70 行(内部仅维护 `dataMap: Map<number, IItemRawData>` 一张表,与 `TileStore` 相比少了双向索引表)。
|
|
||||||
|
|
||||||
# 涉及文件
|
|
||||||
|
|
||||||
## 需要引用的文件
|
|
||||||
|
|
||||||
- `@user/data-common/src/store/types.ts`:参考 `ITileRawData`、`ITileLegacyConverter`、`ITileStore` 的接口模式,用于设计 `IItemStore`。
|
|
||||||
- `@user/data-common/src/store/tileStore.ts`:参考 `TileStore` 的实现模式,用于实现 `ItemStore`。
|
|
||||||
|
|
||||||
## 需要修改的文件
|
|
||||||
|
|
||||||
### `@user/data-common/src/store/types.ts`
|
|
||||||
|
|
||||||
- [x] 新增 `ItemCategory` 枚举:包含 `Unknown`、`Constant`、`Consumable`、`Pick`、`Equipment` 五个成员。放在 `store/types.ts` 以避免与 `types.ts` 的循环引用。
|
|
||||||
- [x] 补齐 `IItemRawData` 接口:新增 `readonly effect: IItemEffect` 成员。
|
|
||||||
- [x] 新增 `IItemEffect` 接口:包含 `pickEffect(item: IItemRawData): void`、`useEffect(item: IItemRawData): void`、`canUse(item: IItemRawData): boolean` 三个方法。
|
|
||||||
- [x] 新增 `IItemLegacyConverter<TLegacy>` 接口:定义旧样板道具→`IItemRawData` 的转换方法,负责将旧样板字符串效果编译为 `IItemEffect` 实例。
|
|
||||||
- [x] 新增 `IItemStore<TLegacy>` 接口:提供 `getData`、`getCategory`、`addItem`、`attachLegacyConverter`、`fromLegacy` 方法,不需要 id↔num 双向索引。
|
|
||||||
|
|
||||||
### `@user/data-common/src/store/itemStore.ts`(新建)
|
|
||||||
|
|
||||||
- [x] 新增 `ItemStore<TLegacy>` 类:实现 `IItemStore<TLegacy>` 接口,参考 `TileStore` 的实现模式。
|
|
||||||
- [x] 内部维护 `dataMap: Map<number, IItemRawData>` 一张表,无需 id num 双向映射。
|
|
||||||
- [x] 实现 `fromLegacy`:无 converter 时抛出错误。
|
|
||||||
|
|
||||||
### `@user/data-common/src/store/index.ts`
|
|
||||||
|
|
||||||
- [x] 新增 `export * from './itemStore'` 导出。
|
|
||||||
|
|
||||||
### `@user/data-common/src/types.ts`
|
|
||||||
|
|
||||||
- [x] 修改 `IDataCommon` 接口:新增 `readonly itemStore: IItemStore` 成员。
|
|
||||||
@ -52,3 +52,31 @@ export interface IRoleFaceBinder {
|
|||||||
*/
|
*/
|
||||||
getMainFace(identifier: number): IFaceData | null;
|
getMainFace(identifier: number): IFaceData | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#region 功能接口
|
||||||
|
|
||||||
|
export const enum SaveCompression {
|
||||||
|
/** 不进行压缩,仅提取必要数据 */
|
||||||
|
NoCompression,
|
||||||
|
/** 进行小幅度压缩,以性能为主要考虑目标 */
|
||||||
|
LowCompression,
|
||||||
|
/** 进行大幅度压缩,以体积为主要考虑目标 */
|
||||||
|
HighCompression
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISaveableContent<T> {
|
||||||
|
/**
|
||||||
|
* 保存对象状态,返回的对象应该经过深拷贝(即 `structuedClone`)
|
||||||
|
* @param compression 压缩级别
|
||||||
|
*/
|
||||||
|
saveState(compression: SaveCompression): T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取对象状态
|
||||||
|
* @param state 状态对象
|
||||||
|
* @param compression 压缩级别
|
||||||
|
*/
|
||||||
|
loadState(state: T, compression: SaveCompression): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
export * from './common';
|
export * from './common';
|
||||||
export * from './save';
|
|
||||||
export * from './store';
|
export * from './store';
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
export * from './itemStore';
|
|
||||||
export * from './tileStore';
|
export * from './tileStore';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
import { logger } from '@motajs/common';
|
|
||||||
import {
|
|
||||||
IItemLegacyConverter,
|
|
||||||
IItemRawData,
|
|
||||||
IItemStore,
|
|
||||||
ItemCategory
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
export class ItemStore<TLegacy> implements IItemStore<TLegacy> {
|
|
||||||
/** 以道具图块数字为键的原始道具定义表 */
|
|
||||||
private readonly dataMap: Map<number, IItemRawData> = new Map();
|
|
||||||
|
|
||||||
/** 当前挂载的旧样板道具转换器 */
|
|
||||||
private converter: IItemLegacyConverter<TLegacy> | null = null;
|
|
||||||
|
|
||||||
getData(num: number): IItemRawData | null {
|
|
||||||
return this.dataMap.get(num) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCategory(num: number): ItemCategory {
|
|
||||||
return this.dataMap.get(num)?.category ?? ItemCategory.Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
addItem(data: IItemRawData): void {
|
|
||||||
this.dataMap.set(data.num, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
attachLegacyConverter(converter: IItemLegacyConverter<TLegacy>): void {
|
|
||||||
this.converter = converter;
|
|
||||||
}
|
|
||||||
|
|
||||||
fromLegacy(num: number, legacy: TLegacy): IItemRawData {
|
|
||||||
const converter = this.converter;
|
|
||||||
if (!converter) {
|
|
||||||
logger.error(57);
|
|
||||||
throw new Error('Expected a item legacy converter.');
|
|
||||||
}
|
|
||||||
const data = converter.fromLegacy(num, legacy);
|
|
||||||
this.addItem(data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -70,7 +70,7 @@ export class TileStore<TLegacy = unknown> implements ITileStore<TLegacy> {
|
|||||||
const converter = this.legacyConverter;
|
const converter = this.legacyConverter;
|
||||||
if (!converter) {
|
if (!converter) {
|
||||||
logger.error(56);
|
logger.error(56);
|
||||||
throw new Error('Expected a tile legacy converter.');
|
throw new Error('Expected a tile legacy converter');
|
||||||
}
|
}
|
||||||
const data = converter.fromLegacy(num, legacy);
|
const data = converter.fromLegacy(num, legacy);
|
||||||
this.addTile(data);
|
this.addTile(data);
|
||||||
|
|||||||
@ -68,7 +68,7 @@ export interface ITileLegacyConverter<TLegacy> {
|
|||||||
fromLegacy(num: number, legacy: TLegacy): ITileRawData;
|
fromLegacy(num: number, legacy: TLegacy): ITileRawData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITileStore<TLegacy> {
|
export interface ITileStore<TLegacy = unknown> {
|
||||||
/**
|
/**
|
||||||
* 获取指定图块数字对应的完整原始定义
|
* 获取指定图块数字对应的完整原始定义
|
||||||
* @param num 图块数字
|
* @param num 图块数字
|
||||||
@ -120,108 +120,3 @@ export interface ITileStore<TLegacy> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region item
|
|
||||||
|
|
||||||
export const enum ItemCategory {
|
|
||||||
/** 未知或尚未归类的道具 */
|
|
||||||
Unknown,
|
|
||||||
/** 永久道具,使用后不会消耗 */
|
|
||||||
Constant,
|
|
||||||
/** 一次性道具,使用后消耗 */
|
|
||||||
Consumable,
|
|
||||||
/** 即捡即用道具,捡到立刻生效 */
|
|
||||||
Pick,
|
|
||||||
/** 装备类道具,可装备 / 卸下 */
|
|
||||||
Equipment
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IItemEffect {
|
|
||||||
/** 即捡即用事件内容(Pick 类型) */
|
|
||||||
readonly pickEvent: unknown;
|
|
||||||
/** 道具使用事件内容(Constant 和 Consumable 类型) */
|
|
||||||
readonly useEvent: unknown;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 即捡即用效果,拾取道具时调用
|
|
||||||
* @param item 当前道具数据
|
|
||||||
*/
|
|
||||||
pickEffect(item: IItemRawData): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 道具使用效果,使用道具时调用
|
|
||||||
* @param item 当前道具数据
|
|
||||||
*/
|
|
||||||
useEffect(item: IItemRawData): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 能否使用道具
|
|
||||||
* @param item 当前道具数据
|
|
||||||
*/
|
|
||||||
canUse(item: IItemRawData): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IItemRawData {
|
|
||||||
/** 道具在地图上的图块数字 */
|
|
||||||
readonly num: number;
|
|
||||||
/** 道具的字符串标识符 */
|
|
||||||
readonly id: string;
|
|
||||||
/** 道具分类 */
|
|
||||||
readonly category: ItemCategory;
|
|
||||||
/** 道具显示名称 */
|
|
||||||
readonly name: string;
|
|
||||||
/** 道具描述文本 */
|
|
||||||
readonly text: string;
|
|
||||||
|
|
||||||
/** 是否在道具栏中隐藏 */
|
|
||||||
readonly hideInToolbox: boolean;
|
|
||||||
|
|
||||||
/** 道具效果对象(非 Equipment 类型) */
|
|
||||||
readonly effect: IItemEffect;
|
|
||||||
/** 装备道具属性(Equipment 类型) */
|
|
||||||
readonly equip: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IItemLegacyConverter<TLegacy> {
|
|
||||||
/**
|
|
||||||
* 将旧样板道具定义转换为新的道具原始数据
|
|
||||||
* @param num 道具图块数字
|
|
||||||
* @param legacy 旧样板道具定义
|
|
||||||
*/
|
|
||||||
fromLegacy(num: number, legacy: TLegacy): IItemRawData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IItemStore<TLegacy> {
|
|
||||||
/**
|
|
||||||
* 获取指定图块数字对应的道具原始数据
|
|
||||||
* @param num 道具图块数字
|
|
||||||
*/
|
|
||||||
getData(num: number): IItemRawData | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定图块数字对应的道具分类
|
|
||||||
* @param num 道具图块数字
|
|
||||||
*/
|
|
||||||
getCategory(num: number): ItemCategory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加一个道具原始数据定义
|
|
||||||
* @param data 道具原始数据
|
|
||||||
*/
|
|
||||||
addItem(data: IItemRawData): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 挂载一个旧样板转换器
|
|
||||||
* @param converter 旧样板转换器
|
|
||||||
*/
|
|
||||||
attachLegacyConverter(converter: IItemLegacyConverter<TLegacy>): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用当前转换器转换并写入一个旧样板道具定义
|
|
||||||
* @param num 道具图块数字
|
|
||||||
* @param legacy 旧样板道具定义
|
|
||||||
*/
|
|
||||||
fromLegacy(num: number, legacy: TLegacy): IItemRawData;
|
|
||||||
}
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { ITileLocator } from '@motajs/common';
|
import { ITileLocator } from '@motajs/common';
|
||||||
import { IFaceManager, IRoleFaceBinder } from './common';
|
import { IFaceManager, IRoleFaceBinder } from './common';
|
||||||
import { IItemStore, ITileStore } from './store';
|
import { ITileStore } from './store';
|
||||||
import { ISaveSystem } from './save';
|
|
||||||
|
|
||||||
export interface IEnemyAttr {
|
export interface IEnemyAttr {
|
||||||
/** 怪物生命值 */
|
/** 怪物生命值 */
|
||||||
@ -46,14 +45,10 @@ export interface IHeroAttr {
|
|||||||
export interface IDataCommon {
|
export interface IDataCommon {
|
||||||
/** 图块定义存储 */
|
/** 图块定义存储 */
|
||||||
readonly tileStore: ITileStore<MapDataOf<keyof NumberToId>>;
|
readonly tileStore: ITileStore<MapDataOf<keyof NumberToId>>;
|
||||||
/** 道具定义存储 */
|
|
||||||
readonly itemStore: IItemStore<Item<AllIdsOf<'items'>>>;
|
|
||||||
/** 朝向绑定 */
|
/** 朝向绑定 */
|
||||||
readonly roleFace: IRoleFaceBinder;
|
readonly roleFace: IRoleFaceBinder;
|
||||||
/** 朝向管理 */
|
/** 朝向管理 */
|
||||||
readonly faceManager: IFaceManager;
|
readonly faceManager: IFaceManager;
|
||||||
/** 存档系统 */
|
|
||||||
readonly saveSystem: ISaveSystem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDataCommonExtended {
|
export interface IDataCommonExtended {
|
||||||
|
|||||||
@ -13,11 +13,7 @@ import {
|
|||||||
FaceGroup,
|
FaceGroup,
|
||||||
FaceDirection,
|
FaceDirection,
|
||||||
IHeroAttr,
|
IHeroAttr,
|
||||||
IEnemyAttr,
|
IEnemyAttr
|
||||||
ISaveSystem,
|
|
||||||
SaveSystem,
|
|
||||||
IItemStore,
|
|
||||||
ItemStore
|
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import {
|
import {
|
||||||
EnemyManager,
|
EnemyManager,
|
||||||
@ -65,24 +61,18 @@ import {
|
|||||||
TILE_HEIGHT,
|
TILE_HEIGHT,
|
||||||
TILE_WIDTH
|
TILE_WIDTH
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import {
|
import { LegacyTileData, TileLegacyBridge } from './legacy';
|
||||||
ItemLegacyBridge,
|
|
||||||
LegacyItemData,
|
|
||||||
LegacyTileData,
|
|
||||||
TileLegacyBridge
|
|
||||||
} from './legacy';
|
|
||||||
import { ILoadProgressTotal, LoadProgressTotal } from '@motajs/loader';
|
import { ILoadProgressTotal, LoadProgressTotal } from '@motajs/loader';
|
||||||
import { isNil } from 'lodash-es';
|
import { isNil } from 'lodash-es';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
|
import { ISaveSystem, SaveSystem } from './save';
|
||||||
import { DefaultHeroMoveTopImpl } from './hero';
|
import { DefaultHeroMoveTopImpl } from './hero';
|
||||||
|
|
||||||
export class CoreState implements ICoreState {
|
export class CoreState implements ICoreState {
|
||||||
// Layer 0 公共层,最底层的接口,不会依赖任何其他内容,一般是工具性接口及不需要存档的数据
|
// Layer 0 公共层,最底层的接口,不会依赖任何其他内容,一般是工具性接口及不需要存档的数据
|
||||||
readonly saveSystem: ISaveSystem;
|
|
||||||
readonly roleFace: IRoleFaceBinder;
|
readonly roleFace: IRoleFaceBinder;
|
||||||
readonly faceManager: IFaceManager;
|
readonly faceManager: IFaceManager;
|
||||||
readonly tileStore: ITileStore<LegacyTileData>;
|
readonly tileStore: ITileStore<LegacyTileData>;
|
||||||
readonly itemStore: IItemStore<LegacyItemData>;
|
|
||||||
|
|
||||||
// Layer 1 数据层,所有可存档内容都在这,一般用于数据存储
|
// Layer 1 数据层,所有可存档内容都在这,一般用于数据存储
|
||||||
readonly maps: IMapStore;
|
readonly maps: IMapStore;
|
||||||
@ -98,6 +88,7 @@ export class CoreState implements ICoreState {
|
|||||||
// Layer 3 用户层,也就是最顶层的内容,一般仅用于初始化以及仅供渲染端调用的顶层模块
|
// Layer 3 用户层,也就是最顶层的内容,一般仅用于初始化以及仅供渲染端调用的顶层模块
|
||||||
readonly loadProgress: ILoadProgressTotal;
|
readonly loadProgress: ILoadProgressTotal;
|
||||||
readonly dataLoader: IMotaDataLoader;
|
readonly dataLoader: IMotaDataLoader;
|
||||||
|
readonly saveSystem: ISaveSystem;
|
||||||
|
|
||||||
/** 可存档对象映射 */
|
/** 可存档对象映射 */
|
||||||
private readonly saveables: Map<string, ISaveableContent<any>> = new Map();
|
private readonly saveables: Map<string, ISaveableContent<any>> = new Map();
|
||||||
@ -112,17 +103,6 @@ export class CoreState implements ICoreState {
|
|||||||
constructor() {
|
constructor() {
|
||||||
//#region L0 初始化
|
//#region L0 初始化
|
||||||
|
|
||||||
// 存档系统
|
|
||||||
this.saveSystem = new SaveSystem();
|
|
||||||
// 配置存档系统,一般情况下不建议动,除非你知道你在干什么
|
|
||||||
this.saveSystem.config({
|
|
||||||
autosaveLevel: SaveCompression.LowCompression,
|
|
||||||
commonSaveLevel: SaveCompression.HighCompression,
|
|
||||||
autosaveTimeTolerance: 50,
|
|
||||||
saveTimeTolerance: 100,
|
|
||||||
autosaveStackSize: 20
|
|
||||||
});
|
|
||||||
|
|
||||||
// 朝向
|
// 朝向
|
||||||
this.roleFace = new RoleFaceBinder();
|
this.roleFace = new RoleFaceBinder();
|
||||||
this.faceManager = new FaceManager();
|
this.faceManager = new FaceManager();
|
||||||
@ -137,10 +117,6 @@ export class CoreState implements ICoreState {
|
|||||||
const tileStore = new TileStore<LegacyTileData>();
|
const tileStore = new TileStore<LegacyTileData>();
|
||||||
tileStore.attachLegacyConverter(new TileLegacyBridge());
|
tileStore.attachLegacyConverter(new TileLegacyBridge());
|
||||||
this.tileStore = tileStore;
|
this.tileStore = tileStore;
|
||||||
// 道具
|
|
||||||
const itemStore = new ItemStore<LegacyItemData>();
|
|
||||||
itemStore.attachLegacyConverter(new ItemLegacyBridge(this));
|
|
||||||
this.itemStore = itemStore;
|
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
@ -204,7 +180,16 @@ export class CoreState implements ICoreState {
|
|||||||
|
|
||||||
//#region L3 初始化
|
//#region L3 初始化
|
||||||
|
|
||||||
// 存档内容
|
// 存档系统
|
||||||
|
this.saveSystem = new SaveSystem();
|
||||||
|
// 配置存档系统,一般情况下不建议动,除非你知道你在干什么
|
||||||
|
this.saveSystem.config({
|
||||||
|
autosaveLevel: SaveCompression.LowCompression,
|
||||||
|
commonSaveLevel: SaveCompression.HighCompression,
|
||||||
|
autosaveTimeTolerance: 50,
|
||||||
|
saveTimeTolerance: 100,
|
||||||
|
autosaveStackSize: 20
|
||||||
|
});
|
||||||
this.addSaveableContent('@system/hero', this.hero);
|
this.addSaveableContent('@system/hero', this.hero);
|
||||||
this.addSaveableContent('@system/flags', this.flags);
|
this.addSaveableContent('@system/flags', this.flags);
|
||||||
this.addSaveableContent('@system/maps', this.maps);
|
this.addSaveableContent('@system/maps', this.maps);
|
||||||
@ -217,7 +202,6 @@ export class CoreState implements ICoreState {
|
|||||||
// 加载初始化,先使用兼容层实现
|
// 加载初始化,先使用兼容层实现
|
||||||
loading.once('loaded', () => {
|
loading.once('loaded', () => {
|
||||||
this.initTileStore(core.maps.blocksInfo);
|
this.initTileStore(core.maps.blocksInfo);
|
||||||
this.initItemStore(core.items.items);
|
|
||||||
this.initEnemyManager(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80);
|
this.initEnemyManager(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80);
|
||||||
this.initMapStore(
|
this.initMapStore(
|
||||||
core.floorIds,
|
core.floorIds,
|
||||||
@ -265,22 +249,6 @@ export class CoreState implements ICoreState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化道具存储对象
|
|
||||||
* @param data 旧样板道具定义对象
|
|
||||||
*/
|
|
||||||
private initItemStore(data: typeof core.items.items) {
|
|
||||||
const entries = Object.entries(data);
|
|
||||||
for (const [id, legacy] of entries) {
|
|
||||||
const num = this.tileStore.idToNumber(id);
|
|
||||||
if (isNil(num)) {
|
|
||||||
logger.warn(145, id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.itemStore.fromLegacy(num, legacy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化怪物管理器对象
|
* 初始化怪物管理器对象
|
||||||
* @param data 旧样板怪物存储对象
|
* @param data 旧样板怪物存储对象
|
||||||
|
|||||||
@ -1,98 +1,101 @@
|
|||||||
import {
|
import EventEmitter from 'eventemitter3';
|
||||||
IItemEffect,
|
import { loading } from '@user/data-base';
|
||||||
IItemLegacyConverter,
|
|
||||||
IItemRawData,
|
|
||||||
ItemCategory
|
|
||||||
} from '@user/data-common';
|
|
||||||
import { IStateSystem } from '@user/data-system';
|
|
||||||
|
|
||||||
export type LegacyItemData = Item<AllIdsOf<'items'>>;
|
type EffectFn = () => void;
|
||||||
|
type CanUseEffectFn = () => boolean;
|
||||||
|
|
||||||
type LegacyItemEffectFn = (state: IStateSystem, item: IItemRawData) => void;
|
interface ItemStateEvent {
|
||||||
type LegacyItemCanUseFn = (state: IStateSystem, item: IItemRawData) => boolean;
|
use: [];
|
||||||
|
}
|
||||||
|
|
||||||
class LegacyItemEffect implements IItemEffect {
|
export class ItemState<
|
||||||
pickEvent: unknown;
|
I extends AllIdsOf<'items'> = AllIdsOf<'items'>
|
||||||
useEvent: unknown;
|
> extends EventEmitter<ItemStateEvent> {
|
||||||
|
static items: Map<AllIdsOf<'items'>, ItemState> = new Map();
|
||||||
|
|
||||||
private readonly pickFn: LegacyItemEffectFn | null = null;
|
id: I;
|
||||||
private readonly useFn: LegacyItemEffectFn | null = null;
|
cls: ItemClsOf<I>;
|
||||||
private readonly canUseFn: LegacyItemCanUseFn | null = null;
|
name: string;
|
||||||
private readonly state: IStateSystem;
|
text?: string;
|
||||||
|
hideInToolBox: boolean;
|
||||||
|
hideInReplay: boolean;
|
||||||
|
|
||||||
constructor(legacy: LegacyItemData, state: IStateSystem) {
|
/** 即捡即用效果 */
|
||||||
this.state = state;
|
itemEffect?: string;
|
||||||
this.pickEvent = undefined;
|
/** 即捡即用道具捡过之后的提示 */
|
||||||
this.useEvent = legacy.useItemEvent;
|
itemEffectTip?: string;
|
||||||
|
/** 使用道具时执行的事件 */
|
||||||
|
useItemEvent?: MotaEvent;
|
||||||
|
/** 使用道具时执行的代码 */
|
||||||
|
useItemEffect?: string;
|
||||||
|
/** 能否使用道具 */
|
||||||
|
canUseItemEffect?: string | boolean;
|
||||||
|
|
||||||
if (legacy.itemEffect) {
|
private noRoute: boolean = false;
|
||||||
this.pickFn = new Function(
|
|
||||||
'state',
|
itemEffectFn?: EffectFn;
|
||||||
'item',
|
useItemEffectFn?: EffectFn;
|
||||||
legacy.itemEffect
|
canUseItemEffectFn?: CanUseEffectFn;
|
||||||
) as LegacyItemEffectFn;
|
|
||||||
|
constructor(id: I) {
|
||||||
|
super();
|
||||||
|
const items = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a;
|
||||||
|
this.id = id;
|
||||||
|
const item = items[id];
|
||||||
|
this.cls = item.cls;
|
||||||
|
this.name = item.name;
|
||||||
|
this.text = item.text;
|
||||||
|
this.hideInToolBox = item.hideInToolBox;
|
||||||
|
this.hideInReplay = item.hideInReplay;
|
||||||
|
this.itemEffect = item.itemEffect;
|
||||||
|
this.itemEffectTip = item.itemEffectTip;
|
||||||
|
this.useItemEvent = item.useItemEvent;
|
||||||
|
this.useItemEffect = item.useItemEffect;
|
||||||
|
this.canUseItemEffect = item.canUseItemEffect;
|
||||||
|
|
||||||
|
this.compileFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
private compileFunction() {
|
||||||
|
if (this.itemEffect) {
|
||||||
|
this.itemEffectFn = new Function(
|
||||||
|
`state`,
|
||||||
|
this.itemEffect
|
||||||
|
) as EffectFn;
|
||||||
}
|
}
|
||||||
if (legacy.useItemEffect) {
|
if (this.useItemEffect) {
|
||||||
this.useFn = new Function(
|
this.useItemEffectFn = new Function(
|
||||||
'state',
|
`state`,
|
||||||
'item',
|
this.useItemEffect
|
||||||
legacy.useItemEffect
|
) as EffectFn;
|
||||||
) as LegacyItemEffectFn;
|
|
||||||
}
|
}
|
||||||
if (typeof legacy.canUseItemEffect === 'string') {
|
if (this.canUseItemEffect) {
|
||||||
this.canUseFn = new Function(
|
if (typeof this.canUseItemEffect === 'boolean') {
|
||||||
'state',
|
this.canUseItemEffectFn = () =>
|
||||||
'item',
|
this.canUseItemEffect as boolean;
|
||||||
legacy.canUseItemEffect
|
} else {
|
||||||
) as LegacyItemCanUseFn;
|
this.useItemEffectFn = new Function(
|
||||||
} else {
|
`state`,
|
||||||
this.canUseFn = () => !!legacy.canUseItemEffect;
|
this.canUseItemEffect
|
||||||
|
) as CanUseEffectFn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pickEffect(item: IItemRawData): void {
|
/**
|
||||||
this.pickFn?.(this.state, item);
|
* 获取一个道具的信息
|
||||||
}
|
* @param id 要获取的道具id
|
||||||
|
*/
|
||||||
useEffect(item: IItemRawData): void {
|
static item<I extends AllIdsOf<'items'>>(id: I): ItemState<I> | undefined {
|
||||||
this.useFn?.(this.state, item);
|
return this.items.get(id) as ItemState<I>;
|
||||||
}
|
|
||||||
|
|
||||||
canUse(item: IItemRawData): boolean {
|
|
||||||
if (!this.canUseFn) return true;
|
|
||||||
else return this.canUseFn(this.state, item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ItemLegacyBridge implements IItemLegacyConverter<LegacyItemData> {
|
loading.once('coreInit', () => {
|
||||||
constructor(private readonly state: IStateSystem) {}
|
for (const key of Object.keys(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a)) {
|
||||||
|
ItemState.items.set(
|
||||||
fromLegacy(num: number, legacy: LegacyItemData): IItemRawData {
|
key as AllIdsOf<'items'>,
|
||||||
const effect = new LegacyItemEffect(legacy, this.state);
|
new ItemState(key as AllIdsOf<'items'>)
|
||||||
return {
|
);
|
||||||
num,
|
|
||||||
id: legacy.id,
|
|
||||||
category: this.mapCategory(legacy.cls),
|
|
||||||
name: legacy.name,
|
|
||||||
text: legacy.text ?? '',
|
|
||||||
hideInToolbox: legacy.hideInToolBox,
|
|
||||||
effect,
|
|
||||||
equip: legacy.equip
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
private mapCategory(cls: ItemCls): ItemCategory {
|
|
||||||
switch (cls) {
|
|
||||||
case 'constants':
|
|
||||||
return ItemCategory.Constant;
|
|
||||||
case 'tools':
|
|
||||||
return ItemCategory.Consumable;
|
|
||||||
case 'equips':
|
|
||||||
return ItemCategory.Equipment;
|
|
||||||
case 'items':
|
|
||||||
return ItemCategory.Pick;
|
|
||||||
default:
|
|
||||||
return ItemCategory.Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -4,10 +4,9 @@ import {
|
|||||||
IGlobalTrasaction,
|
IGlobalTrasaction,
|
||||||
ISaveRead,
|
ISaveRead,
|
||||||
ISaveSystem,
|
ISaveSystem,
|
||||||
ISaveSystemConfig,
|
ISaveSystemConfig
|
||||||
ISaveableContent,
|
|
||||||
SaveCompression
|
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { ISaveableContent, SaveCompression } from '@user/data-common';
|
||||||
import { isNil } from 'lodash-es';
|
import { isNil } from 'lodash-es';
|
||||||
|
|
||||||
interface ISaveRecord {
|
interface ISaveRecord {
|
||||||
@ -1,29 +1,6 @@
|
|||||||
|
import { ISaveableContent, SaveCompression } from '@user/data-common';
|
||||||
import { Dexie, Table } from 'dexie';
|
import { Dexie, Table } from 'dexie';
|
||||||
|
|
||||||
export const enum SaveCompression {
|
|
||||||
/** 不进行压缩,仅提取必要数据 */
|
|
||||||
NoCompression,
|
|
||||||
/** 进行小幅度压缩,以性能为主要考虑目标 */
|
|
||||||
LowCompression,
|
|
||||||
/** 进行大幅度压缩,以体积为主要考虑目标 */
|
|
||||||
HighCompression
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ISaveableContent<T> {
|
|
||||||
/**
|
|
||||||
* 保存对象状态,返回的对象应该经过深拷贝(即 `structuedClone`)
|
|
||||||
* @param compression 压缩级别
|
|
||||||
*/
|
|
||||||
saveState(compression: SaveCompression): T;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取对象状态
|
|
||||||
* @param state 状态对象
|
|
||||||
* @param compression 压缩级别
|
|
||||||
*/
|
|
||||||
loadState(state: T, compression: SaveCompression): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGlobalTrasaction {
|
export interface IGlobalTrasaction {
|
||||||
/** 全局存储对应的表 */
|
/** 全局存储对应的表 */
|
||||||
readonly table: Table<unknown, string>;
|
readonly table: Table<unknown, string>;
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { IMotaDataLoader, IStateBase } from '@user/data-base';
|
import { IMotaDataLoader, IStateBase } from '@user/data-base';
|
||||||
import { ILoadProgressTotal } from '@motajs/loader';
|
import { ILoadProgressTotal } from '@motajs/loader';
|
||||||
|
import { ISaveSystem } from './save';
|
||||||
import { IStateSystem } from '@user/data-system';
|
import { IStateSystem } from '@user/data-system';
|
||||||
import { ISaveableContent } from '@user/data-common';
|
import { ISaveableContent } from '@user/data-common';
|
||||||
|
|
||||||
@ -17,6 +18,8 @@ export interface ICoreState extends IStateSystem {
|
|||||||
readonly loadProgress: ILoadProgressTotal;
|
readonly loadProgress: ILoadProgressTotal;
|
||||||
/** 数据端加载对象 */
|
/** 数据端加载对象 */
|
||||||
readonly dataLoader: IMotaDataLoader;
|
readonly dataLoader: IMotaDataLoader;
|
||||||
|
/** 存档系统 */
|
||||||
|
readonly saveSystem: ISaveSystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将某个存档执行器绑定至指定的可存档对象,一个可存档对象只能绑定一个执行器,
|
* 将某个存档执行器绑定至指定的可存档对象,一个可存档对象只能绑定一个执行器,
|
||||||
|
|||||||
@ -55,8 +55,7 @@
|
|||||||
"53": "Expected serializable value set as enemy's default attribute.",
|
"53": "Expected serializable value set as enemy's default attribute.",
|
||||||
"54": "Legacy '$1' API has been removed, consider using new APIs: '$2'.",
|
"54": "Legacy '$1' API has been removed, consider using new APIs: '$2'.",
|
||||||
"55": "Cannot load MapStore state: reference data (compareWith) has not been set.",
|
"55": "Cannot load MapStore state: reference data (compareWith) has not been set.",
|
||||||
"56": "Cannot convert legacy tile data since no tile legacy converter is attached to TileStore.",
|
"56": "Cannot convert legacy tile data since no tile legacy converter is attached to TileStore."
|
||||||
"57": "Cannot convert legacy item data since no item legacy converter is attached to ItemStore."
|
|
||||||
},
|
},
|
||||||
"warn": {
|
"warn": {
|
||||||
"1": "Resource with type of 'none' is loaded.",
|
"1": "Resource with type of 'none' is loaded.",
|
||||||
@ -202,7 +201,6 @@
|
|||||||
"141": "Some issue may occur in binded damage system on combat flow, please check the console.",
|
"141": "Some issue may occur in binded damage system on combat flow, please check the console.",
|
||||||
"142": "Expected a specific tile number after convert id '$1' to number, but got null.",
|
"142": "Expected a specific tile number after convert id '$1' to number, but got null.",
|
||||||
"143": "Cannot create dynamic tile for $1 since its raw data is not found.",
|
"143": "Cannot create dynamic tile for $1 since its raw data is not found.",
|
||||||
"144": "A hero move top implementation object binding is required for hero moving.",
|
"144": "A hero move top implementation object binding is required for hero moving."
|
||||||
"145": "No tile registered for item '$1'."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
3. **发现接口问题时提问**:若认为类型标注中的接口设计有问题,或在实现中发现缺少某些接口,应向我提问是否添加,经我同意后方可添加。
|
3. **发现接口问题时提问**:若认为类型标注中的接口设计有问题,或在实现中发现缺少某些接口,应向我提问是否添加,经我同意后方可添加。
|
||||||
4. **接口设计兼顾合理性与便捷性**:设计接口时不仅要考虑合理性,还要考虑使用便捷性。罕见场景应当被支持,但不应与常见场景共用同一接口——这只会增加常见场景的使用难度。
|
4. **接口设计兼顾合理性与便捷性**:设计接口时不仅要考虑合理性,还要考虑使用便捷性。罕见场景应当被支持,但不应与常见场景共用同一接口——这只会增加常见场景的使用难度。
|
||||||
5. **避免多余的非空判断与类型守卫**:类型已满足约束时不应再额外判断或过滤。例如 `Promise.all` 接受 `(Promise<unknown> | void)[]`,无需写成 `Promise.all(arr.filter(v => !!v))`。
|
5. **避免多余的非空判断与类型守卫**:类型已满足约束时不应再额外判断或过滤。例如 `Promise.all` 接受 `(Promise<unknown> | void)[]`,无需写成 `Promise.all(arr.filter(v => !!v))`。
|
||||||
6. **关于整个仓库的类型检查**:单个需求**不需要**跑全仓库的类型检查,仅在重构时需要。如果跑类型检查,由于系统构建的不完全,可能包含一部分其他系统的错误信息,自行辨别。
|
6. **关于整个仓库的类型检查**:单个需求**不需要**跑全仓库的类型检查,仅在重构时需要。
|
||||||
|
|
||||||
# 开发流程
|
# 开发流程
|
||||||
|
|
||||||
|
|||||||
4
src/types/declaration/item.d.ts
vendored
4
src/types/declaration/item.d.ts
vendored
@ -98,10 +98,6 @@ interface Equip extends EquipBase {
|
|||||||
* 道具相关的内容
|
* 道具相关的内容
|
||||||
*/
|
*/
|
||||||
interface Items {
|
interface Items {
|
||||||
readonly items: {
|
|
||||||
[P in AllIdsOf<'items'>]: Item<P>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated 可使用,2.C 将会有替代接口\
|
* @deprecated 可使用,2.C 将会有替代接口\
|
||||||
* 获得所有道具
|
* 获得所有道具
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user