diff --git a/docs/dev/item/item-store.md b/docs/dev/item/item-store.md index 52e0c6e..ff3e66e 100644 --- a/docs/dev/item/item-store.md +++ b/docs/dev/item/item-store.md @@ -22,7 +22,7 @@ ### 设计意图 -参考 `ITileRawData` 的设计,`IItemRawData` 是道具的原始数据定义,存储于 `IItemStore` 中。与 `ITileRawData` 具有 `num`(图块数字)、`id`(字符串标识符)、`type`(逻辑类型)等字段一样,`IItemRawData` 存储道具的项目级静态定义。该接口已存在于 `store/types.ts` 中(当前为空占位),需要补齐字段。 +参考 `ITileRawData` 的设计,`IItemRawData` 是道具的原始数据定义,存储于 `IItemStore` 中。除基础数据字段外,通过 `readonly effect: IItemEffect` 成员承载道具效果。旧样板字符串效果由 `IItemLegacyConverter` 在转换时通过 `new Function` 编译为 `IItemEffect` 实例。 `IItemStore` 作为 Layer 0 的基础设施被 `IDataCommon` 持有,与 `ITileStore` 平级。后续 `IItemManager`(Layer 1)通过 `IDataCommonExtended.state.itemStore` 访问原始数据,据此创建 `IItem` 运行时实例。 @@ -33,20 +33,39 @@ - `IItemRawData.category`:预期频率**中频**。道具分类,决定使用时的基础行为分支。典型使用场景:拾取道具时判断是否为 `Pick` 类以立即触发效果。 - `IItemRawData.name`:预期频率**高频**。道具显示名称,UI 展示的核心字段。 - `IItemRawData.text`:预期频率**低频**。道具描述文本,仅详情展示时出现。 +- `IItemRawData.effect`:预期频率**中频**。道具效果对象,包含 `pickEffect`、`useEffect`、`canUse` 三个方法。典型使用场景:拾取道具时调用 `effect.pickEffect(state)` 触发即捡即用效果。 ### 预期体量 -`IItemRawData` 为纯数据接口,约 10–12 行。 +`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` 的转换器接口。与后续 `IItemLegacyBridge` 不同——`IItemLegacyConverter` 位于 store 层负责原始数据的导入,`IItemLegacyBridge`(Layer 1)负责运行时属性的转换。二者分层独立,分别处理静态数据与运行时对象。 +参考 `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`:预期频率**低频**。仅在样板加载阶段调用,执行旧格式→新格式的批量转换。 +- `IItemLegacyConverter.fromLegacy`:预期频率**低频**。仅在样板加载阶段调用,执行旧格式→新格式的批量转换,含字符串效果的编译。 ### 预期体量 @@ -82,14 +101,15 @@ ### `@user/data-common/src/store/types.ts` - [x] 新增 `ItemCategory` 枚举:包含 `Unknown`、`Constant`、`Consumable`、`Pick`、`Equipment` 五个成员。放在 `store/types.ts` 以避免与 `types.ts` 的循环引用。 -- [x] 补齐 `IItemRawData` 接口(原为空占位):新增 `num`、`id`、`category`、`name`、`text` 字段。 -- [x] 新增 `IItemLegacyConverter` 接口:定义旧样板道具→`IItemRawData` 的转换方法。 +- [x] 补齐 `IItemRawData` 接口:新增 `readonly effect: IItemEffect` 成员。 +- [x] 新增 `IItemEffect` 接口:包含 `pickEffect(item: IItemRawData): void`、`useEffect(item: IItemRawData): void`、`canUse(item: IItemRawData): boolean` 三个方法。 +- [x] 新增 `IItemLegacyConverter` 接口:定义旧样板道具→`IItemRawData` 的转换方法,负责将旧样板字符串效果编译为 `IItemEffect` 实例。 - [x] 新增 `IItemStore` 接口:提供 `getData`、`getCategory`、`addItem`、`attachLegacyConverter`、`fromLegacy` 方法,不需要 id↔num 双向索引。 ### `@user/data-common/src/store/itemStore.ts`(新建) - [x] 新增 `ItemStore` 类:实现 `IItemStore` 接口,参考 `TileStore` 的实现模式。 -- [x] 内部维护 `dataMap: Map` 一张表,无需 id↔num 双向映射。 +- [x] 内部维护 `dataMap: Map` 一张表,无需 id num 双向映射。 - [x] 实现 `fromLegacy`:无 converter 时抛出错误。 ### `@user/data-common/src/store/index.ts` diff --git a/docs/dev/item/item-system.md b/docs/dev/item/item-system.md deleted file mode 100644 index 8f50ab4..0000000 --- a/docs/dev/item/item-system.md +++ /dev/null @@ -1,157 +0,0 @@ -# 需求综述 - -开发道具系统,核心包括以下内容: - -1. **道具分类**:道具分为四种类型——`constant` 永久道具、`consumable` 一次性道具、`pick` 即捡即用道具、`equipment` 装备类道具。 -2. **旧样板兼容**:参考 `packages-user/data-state/src/enemy/legacy.ts` 的模式,定义旧样板兼容接口,不关心旧样板内部存储格式。 -3. **道具事件与道具脚本**:事件是面向新手的低代码自定义功能(该子系统尚未构建,仅提供接口占位);脚本则传入函数,用于高级用户自定义道具行为。 - -整体架构遵循现有模式:Layer 0(`data-common`)定义 `ItemCategory` 枚举与 `IItemStore` 存储层(见 [item-store.md](./item-store.md)),Layer 1(`data-base`)定义核心运行时接口与实现类,Layer 2(`data-system`)为执行引擎层,Layer 3(`data-state`)提供旧样板兼容桥接实现等顶层集成。 - -# 接口设计分析 - -## IItemAttr(Layer 1 - 运行时属性) - -### 设计意图 - -道具的基础属性接口,定义所有道具共有的字段。与 `IEnemyAttr` 类似,作为道具数据的核心载体。道具分类不同会导致其附加能力差异较大(如装备类有攻防加成、消耗类有恢复量等),因此 `IItemAttr` 仅包含所有道具共有的最小属性集合,分类特有的属性通过事件 / 脚本机制覆盖。 - -### 接口分析 - -- `IItemAttr.id`:预期频率**高频**。道具的唯一标识,在查询道具模板、存档读档等场景中频繁使用。典型使用场景:通过 id 获取指定道具模板。 -- `IItemAttr.name`:预期频率**高频**。道具显示名称,几乎所有 UI 展示场景都需要。典型使用场景:物品栏、商店、拾取提示中显示道具名称。 -- `IItemAttr.category`:预期频率**中频**。分类决定道具的基础行为模式(拾取后是否消耗、是否能装备等),在使用道具时作为关键判断依据。典型使用场景:判断道具是否可以装备到装备栏。 -- `IItemAttr.text`:预期频率**低频**。道具描述文本,仅在详情展示中出现。典型使用场景:鼠标悬浮或点击时的 Tooltip。 - -### 预期体量 - -`IItemAttr` 仅为一个简单的属性接口,预计约 10–15 行。 - -## IReadonlyItem / IItem - -### 设计意图 - -遵循怪物的只读 / 可变双接口模式:`IReadonlyItem` 提供读访问,`IItem` 继承前者并扩展写操作与 `ISaveableContent`。道具的核心能力(事件与脚本)挂载于 `IItem` 上。 - -### 接口分析 - -- `IReadonlyItem.id`:预期频率**高频**。同 `IItemAttr.id`,在只读场景下主要使用的访问方式。 -- `IReadonlyItem.code`:预期频率**中频**。道具在地图上的图块数字,用于从地图图块关联到道具模板。典型使用场景:处理事件触发器从地图上读取道具信息。 -- `IReadonlyItem.category`:预期频率**中频**。判断道具类别以决定后续行为。 -- `IReadonlyItem.getAttribute`:预期频率**中频**。获取道具属性的指定字段,与怪物接口保持一致。典型使用场景:战斗计算中读取装备道具提供的属性加成。 -- `IItem.addEvent`:预期频率**低频**。添加事件配置(低代码),通常由样板编辑器生成。由于事件系统尚未构建,此处仅提供接口占位。 -- `IItem.addScript`:预期频率**低频**。添加脚本函数,通常由高级用户在代码中直接编写。 -- `IItem.clone`:预期频率**中频**。深拷贝道具对象,用于创建怪物掉落物的独立实例。典型使用场景:战斗结算后为怪物掉落道具生成独立副本。 - -### 预期体量 - -两个接口合计约 30–40 行,其中 `IItem` 主要是在 `IReadonlyItem` 基础上增加写操作与 `ISaveableContent` 实现。 - -## IItemEvent 与 IItemScript - -### 设计意图 - -道具自定义行为的两种途径: - -- **IItemEvent**:面向新手的低代码配置。用户通过编辑器配置预设事件类型及参数,运行时由事件引擎解析执行。当前事件引擎尚未构建,本接口仅作为占位,后续事件系统实现后再对接。 -- **IItemScript**:面向高级用户的函数式定制。用户直接传入函数,由道具系统在适当时机调用。脚本持有道具自身的引用,可在函数内通过 `item` 访问道具信息。 - -### 接口分析 - -- `IItemEvent.type`:预期频率**高频(编辑器层面)**。事件类型标识,决定事件引擎如何解析该事件。典型使用场景:样板编辑器中下拉选择"回复 HP"事件类型。 -- `IItemEvent.params`:预期频率**高频**。事件参数,与 type 配合决定具体效果。 -- `IItemScript.execute`:预期频率**中频**。执行脚本函数时的入口。典型使用场景:道具使用触发自定义逻辑(如播放动画后增减属性)。 - -### 预期体量 - -两个接口合计约 15–20 行,当前阶段仅需定义最小化的接口形状。 - -## IItemSaveState / IItemManagerSaveState - -### 设计意图 - -存档状态接口,与 `IEnemySaveState` / `IEnemyManagerSaveState` 对应的设计。`IItemSaveState` 保存单个道具的完整状态快照,`IItemManagerSaveState` 保存管理器中所有变更的模板。事件与脚本是否需要存档取决于其可变性 — 如果事件配置与脚本函数在运行时不可变,则无需存档。 - -### 预期体量 - -两个接口合计约 15–20 行。 - -## IItemLegacyBridge - -### 设计意图 - -参考 `IEnemyLegacyBridge` 的设计,定义从旧样板道具对象到新道具属性的转换接口。不关心旧样板数据结构,仅定义方法签名。 - -### 接口分析 - -- `IItemLegacyBridge.fromLegacyItem`:预期频率**低频**。仅在样板加载时调用一次,用于将所有旧样板道具转换为新格式。仅此一方法,不存在中频场景。 - -### 预期体量 - -仅一个方法,约 8–12 行。 - -## IItemManager - -### 设计意图 - -遵循 `IEnemyManager` 的 Manager 模式,管理道具模板(prefab),负责创建、注册、旧样板导入及存档读档。事件注册与脚本注册暂不纳入 Manager(当前事件系统未构建,脚本为函数引用无需注册),但预留接口位置。 - -### 接口分析 - -- `IItemManager.createItem`:预期频率**中频**。根据图块数字创建道具实例,是战斗掉落、商店购买等场景的入口。典型使用场景:战斗结算时根据怪物掉落表 code 创建道具。 -- `IItemManager.addPrefab`:预期频率**低频**。添加道具模板,样板加载阶段调用。 -- `IItemManager.addPrefabFromLegacy`:预期频率**低频**。从旧样板导入道具模板,样板加载阶段调用。 -- `IItemManager.fromLegacyItem`:预期频率**低频**。从旧样板道具对象转换。 -- `IItemManager.getPrefab`:预期频率**中频**。获取道具模板的只读视图。典型使用场景:UI 查询道具信息用于展示。 - -### 预期体量 - -接口约 60–80 行,实现类约 200–300 行(包含 prefab 管理、dirty 追踪、存档读档逻辑,与 `EnemyManager` 体量相当)。 - -# 问题 - -1. `IItemAttr` 当前仅设计了 `id`、`name`、`category`、`text` 四个字段,是否需要额外添加分类相关的通用字段(如道具对应图块数字 `code`)?`code` 目前放在了 `IReadonlyItem` 层而非 `IItemAttr` 层,这样是否合理? -2. 对于装备类道具,是否需要一个独立的 `IEquipmentAttr` 接口来承载攻防加成等属性?还是这些应该完全通过事件 / 脚本机制实现? -3. `IItemEvent` 的事件类型标识应使用字符串还是枚举?按照开发规范应使用枚举,但事件系统尚未构建,此时定义枚举存在不确定性。建议先定义为 `string` 留白,待事件系统构建后再替换为枚举? -4. 道具的数据是否应该进入存档?如果道具在游戏中不可变(即模板数据不变化),则无需存档。但考虑到可能存在"道具数量"这类可变状态,是否需要单独的状态管理?还是数量管理不属于道具系统本身的职责? -5. 道具与背包系统之间的关系如何处理?背包系统是否独立于道具系统?还是道具系统需要预留背包相关接口? - -# 涉及文件 - -## 需要引用的文件 - -- `@user/data-common/src/save/types.ts`:引用 `ISaveableContent`、`SaveCompression`。 -- `@user/data-base/src/enemy/types.ts`:参考 `IEnemyManager` 和 `IEnemyLegacyBridge` 的接口模式。 - -## 需要修改的文件 - -### `@user/data-common/src/types.ts` - -- [ ] 新增 `IItemAttr` 接口:定义道具运行时属性的基础字段。 - -### `@user/data-base/src/item/types.ts`(新建) - -- [ ] 新增 `IItemSaveState` 接口:单个道具的存档格式。 -- [ ] 新增 `IItemManagerSaveState` 接口:道具管理器的存档格式。 -- [ ] 新增 `IReadonlyItem` 接口:只读道具接口。 -- [ ] 新增 `IItem` 接口:可变道具接口,继承 `IReadonlyItem` 和 `ISaveableContent>`。 -- [ ] 新增 `IItemEvent` 接口:事件配置占位接口。 -- [ ] 新增 `IItemScriptFn` 类型别名:道具脚本函数签名。 -- [ ] 新增 `IItemLegacyBridge` 接口:旧样板兼容桥接接口。 -- [ ] 新增 `IItemManager` 接口:道具管理器接口,继承 `ISaveableContent>`。 - -### `@user/data-base/src/item/enum.ts`(新建,如需要) - -- [ ] 若 `IItemEvent.type` 需要枚举,在此定义事件类型枚举。 - -### `@user/data-base/src/item/index.ts`(新建) - -- [ ] 新增 barrel 导出,汇总导出 `types.ts` 中的所有内容。 - -### `@user/data-base/src/index.ts` - -- [ ] 新增 `export * from './item'` 导出。 - -### `@user/data-base/src/types.ts` - -- [ ] 修改 `IStateBase` 接口:新增 `readonly itemManager: IItemManager` 成员。 diff --git a/packages-user/data-common/src/store/types.ts b/packages-user/data-common/src/store/types.ts index c3cd52b..3f078ab 100644 --- a/packages-user/data-common/src/store/types.ts +++ b/packages-user/data-common/src/store/types.ts @@ -136,6 +136,31 @@ export const enum ItemCategory { 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; @@ -148,16 +173,11 @@ export interface IItemRawData { /** 道具描述文本 */ readonly text: string; - /** 即捡即用效果(Pick 类型) */ - readonly pickEffect: string; - /** 即捡即用事件(Pick 类型) */ - readonly pickEvent: unknown; - /** 道具使用效果(Constant 和 Consumable 类型) */ - readonly useEffect: string; - /** 道具使用事件(Constant 和 Consumable 类型) */ - readonly useEvent: unknown; - /** 是否能够使用道具(Constant 和 Consumable 类型) */ - readonly canUse: string; + /** 是否在道具栏中隐藏 */ + readonly hideInToolbox: boolean; + + /** 道具效果对象(非 Equipment 类型) */ + readonly effect: IItemEffect; /** 装备道具属性(Equipment 类型) */ readonly equip: unknown; } diff --git a/packages-user/data-common/src/types.ts b/packages-user/data-common/src/types.ts index 179f9df..d701821 100644 --- a/packages-user/data-common/src/types.ts +++ b/packages-user/data-common/src/types.ts @@ -47,7 +47,7 @@ export interface IDataCommon { /** 图块定义存储 */ readonly tileStore: ITileStore>; /** 道具定义存储 */ - readonly itemStore: IItemStore; + readonly itemStore: IItemStore>>; /** 朝向绑定 */ readonly roleFace: IRoleFaceBinder; /** 朝向管理 */ diff --git a/packages-user/data-state/src/core.ts b/packages-user/data-state/src/core.ts index 3c7d9b1..c75e3b9 100644 --- a/packages-user/data-state/src/core.ts +++ b/packages-user/data-state/src/core.ts @@ -15,7 +15,9 @@ import { IHeroAttr, IEnemyAttr, ISaveSystem, - SaveSystem + SaveSystem, + IItemStore, + ItemStore } from '@user/data-common'; import { EnemyManager, @@ -63,7 +65,12 @@ import { TILE_HEIGHT, TILE_WIDTH } from './shared'; -import { LegacyTileData, TileLegacyBridge } from './legacy'; +import { + ItemLegacyBridge, + LegacyItemData, + LegacyTileData, + TileLegacyBridge +} from './legacy'; import { ILoadProgressTotal, LoadProgressTotal } from '@motajs/loader'; import { isNil } from 'lodash-es'; import { logger } from '@motajs/common'; @@ -73,8 +80,9 @@ export class CoreState implements ICoreState { // Layer 0 公共层,最底层的接口,不会依赖任何其他内容,一般是工具性接口及不需要存档的数据 readonly roleFace: IRoleFaceBinder; readonly faceManager: IFaceManager; - readonly tileStore: ITileStore; readonly saveSystem: ISaveSystem; + readonly tileStore: ITileStore; + readonly itemStore: IItemStore; // Layer 1 数据层,所有可存档内容都在这,一般用于数据存储 readonly maps: IMapStore; @@ -118,6 +126,10 @@ export class CoreState implements ICoreState { const tileStore = new TileStore(); tileStore.attachLegacyConverter(new TileLegacyBridge()); this.tileStore = tileStore; + // 道具 + const itemStore = new ItemStore(); + itemStore.attachLegacyConverter(new ItemLegacyBridge(this)); + this.itemStore = itemStore; //#endregion @@ -203,6 +215,7 @@ export class CoreState implements ICoreState { // 加载初始化,先使用兼容层实现 loading.once('loaded', () => { this.initTileStore(core.maps.blocksInfo); + this.initItemStore(core.items.items); this.initEnemyManager(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80); this.initMapStore( core.floorIds, @@ -250,6 +263,22 @@ 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 旧样板怪物存储对象 diff --git a/packages-user/data-state/src/legacy/item.ts b/packages-user/data-state/src/legacy/item.ts index e09a14e..43f2ccc 100644 --- a/packages-user/data-state/src/legacy/item.ts +++ b/packages-user/data-state/src/legacy/item.ts @@ -1,101 +1,98 @@ -import EventEmitter from 'eventemitter3'; -import { loading } from '@user/data-base'; +import { + IItemEffect, + IItemLegacyConverter, + IItemRawData, + ItemCategory +} from '@user/data-common'; +import { IStateSystem } from '@user/data-system'; -type EffectFn = () => void; -type CanUseEffectFn = () => boolean; +export type LegacyItemData = Item>; -interface ItemStateEvent { - use: []; -} +type LegacyItemEffectFn = (state: IStateSystem, item: IItemRawData) => void; +type LegacyItemCanUseFn = (state: IStateSystem, item: IItemRawData) => boolean; -export class ItemState< - I extends AllIdsOf<'items'> = AllIdsOf<'items'> -> extends EventEmitter { - static items: Map, ItemState> = new Map(); +class LegacyItemEffect implements IItemEffect { + pickEvent: unknown; + useEvent: unknown; - id: I; - cls: ItemClsOf; - name: string; - text?: string; - hideInToolBox: boolean; - hideInReplay: boolean; + private readonly pickFn: LegacyItemEffectFn | null = null; + private readonly useFn: LegacyItemEffectFn | null = null; + private readonly canUseFn: LegacyItemCanUseFn | null = null; + private readonly state: IStateSystem; - /** 即捡即用效果 */ - itemEffect?: string; - /** 即捡即用道具捡过之后的提示 */ - itemEffectTip?: string; - /** 使用道具时执行的事件 */ - useItemEvent?: MotaEvent; - /** 使用道具时执行的代码 */ - useItemEffect?: string; - /** 能否使用道具 */ - canUseItemEffect?: string | boolean; + constructor(legacy: LegacyItemData, state: IStateSystem) { + this.state = state; + this.pickEvent = undefined; + this.useEvent = legacy.useItemEvent; - private noRoute: boolean = false; - - itemEffectFn?: EffectFn; - useItemEffectFn?: EffectFn; - canUseItemEffectFn?: CanUseEffectFn; - - 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.itemEffect) { + this.pickFn = new Function( + 'state', + 'item', + legacy.itemEffect + ) as LegacyItemEffectFn; } - if (this.useItemEffect) { - this.useItemEffectFn = new Function( - `state`, - this.useItemEffect - ) as EffectFn; + if (legacy.useItemEffect) { + this.useFn = new Function( + 'state', + 'item', + legacy.useItemEffect + ) as LegacyItemEffectFn; } - if (this.canUseItemEffect) { - if (typeof this.canUseItemEffect === 'boolean') { - this.canUseItemEffectFn = () => - this.canUseItemEffect as boolean; - } else { - this.useItemEffectFn = new Function( - `state`, - this.canUseItemEffect - ) as CanUseEffectFn; - } + if (typeof legacy.canUseItemEffect === 'string') { + this.canUseFn = new Function( + 'state', + 'item', + legacy.canUseItemEffect + ) as LegacyItemCanUseFn; + } else { + this.canUseFn = () => !!legacy.canUseItemEffect; } } - /** - * 获取一个道具的信息 - * @param id 要获取的道具id - */ - static item>(id: I): ItemState | undefined { - return this.items.get(id) as ItemState; + pickEffect(item: IItemRawData): void { + this.pickFn?.(this.state, item); + } + + useEffect(item: IItemRawData): void { + this.useFn?.(this.state, item); + } + + canUse(item: IItemRawData): boolean { + if (!this.canUseFn) return true; + else return this.canUseFn(this.state, item); } } -loading.once('coreInit', () => { - for (const key of Object.keys(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a)) { - ItemState.items.set( - key as AllIdsOf<'items'>, - new ItemState(key as AllIdsOf<'items'>) - ); +export class ItemLegacyBridge implements IItemLegacyConverter { + constructor(private readonly state: IStateSystem) {} + + fromLegacy(num: number, legacy: LegacyItemData): IItemRawData { + const effect = new LegacyItemEffect(legacy, this.state); + 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; + } + } +} diff --git a/packages/common/src/logger.json b/packages/common/src/logger.json index ccb30aa..32f255d 100644 --- a/packages/common/src/logger.json +++ b/packages/common/src/logger.json @@ -202,6 +202,7 @@ "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.", "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'." } } diff --git a/src/types/declaration/item.d.ts b/src/types/declaration/item.d.ts index 8e83b3c..16417e3 100644 --- a/src/types/declaration/item.d.ts +++ b/src/types/declaration/item.d.ts @@ -98,6 +98,10 @@ interface Equip extends EquipBase { * 道具相关的内容 */ interface Items { + readonly items: { + [P in AllIdsOf<'items'>]: Item

; + }; + /** * @deprecated 可使用,2.C 将会有替代接口\ * 获得所有道具