8.8 KiB
需求综述
为勇士添加背包系统,用于存储永久道具(Constant)、消耗道具(Consumable)和装备道具(Equipment)。即捡即用道具(Pick)在 addItem/getItem 时直接执行效果,不进入背包。
背包系统由 IHeroItems 接口定义,作为 IHeroState 的新成员挂载。三种类型的道具分别存储在三张 Map<number, IHeroItemState> 中,键为道具图块数字 num。背包需要支持存档读档,继承 ISaveableContent<IHeroItemsSave>。
接口设计分析
IHeroItemState
设计意图
单个道具在背包中的运行时状态,存储道具的基础标识信息与当前数量。raw 为对 IItemRawData 的引用,提供 category、name 等定义数据的便捷访问,读档时通过 state.itemStore.getData(num) 恢复。
接口分析
IHeroItemState.id:预期频率中频。道具的字符串标识符,用于通过 id 寻址时做匹配。典型使用场景:通过 id 判断是否持有某特定道具。IHeroItemState.num:预期频率高频。道具的图块数字,是 Map 键及与地图关联的核心标识。典型使用场景:地图事件中检查玩家是否拥有num对应的钥匙。IHeroItemState.raw:预期频率高频。道具原始定义数据引用,提供 category、name、effect 等属性。典型使用场景:显示道具名称、判断道具类型、执行道具效果。IHeroItemState.count:预期频率中频。道具当前持有数量。三种可存储类型的道具均正常存储数量,不存在时视为无此道具。
预期体量
约 12-16 行。
IHeroItemSave
设计意图
单个道具的存档格式,仅需存储可序列化的字段(num、count),id 和 raw 在读档时通过 itemStore 和 tileStore 恢复。
预期体量
约 5-8 行。
IHeroItemsSave
设计意图
整个背包的存档格式,包含三种类型道具的存档数组。由于三种类型的道具分表存储,存档也对应分表。
预期体量
约 5-8 行。
IHeroItems
设计意图
勇士背包管理接口,作为 IHeroState 的新成员。提供道具的增减查用等基础操作,所有操作均支持通过 num(数字)或 id(字符串)寻址。
addItem 是核心接口,负责向勇士给予道具(count 可为负数以实现扣除)。getItem 是 addItem(item, 1) 的便捷写法,语义为勇士在游戏中"拾取"一个道具。getItemState 是查询接口,获取道具的只读状态。
对于 Pick 类型道具,addItem / getItem 不将其存入背包,而是直接执行 raw.effect.pickEffect。
接口分析
IHeroItems.addItem(item, count?):预期频率中频。向勇士添加或减少道具。若道具为 Pick 类型则立刻执行其效果;否则根据category选择对应分表,增加或减少count,数量归零或负数时自动删除条目。典型使用场景:勇士在地图上走到道具格后拾取道具;事件脚本中给予或扣除玩家道具。IHeroItems.getItem(item):预期频率高频。使勇士获取一个道具,即addItem(item, 1)的另一种写法。Pick 类型立即执行效果,其他类型背包数量加一。典型使用场景:勇士走到地图道具格上触发拾取。IHeroItems.getItemState(item):预期频率高频。获取指定道具的只读状态,是所有查询操作的入口。返回null表示未持有。典型使用场景:NPC 检查玩家是否持有某任务道具;UI 显示道具详情。IHeroItems.useItem(item):预期频率中频。使用道具,仅对 Constant 和 Consumable 类型生效。调用raw.effect.useEffect,Consumable 类型使用后数量减一。返回boolean表示是否使用成功。IHeroItems.itemCount(item):预期频率高频。获取道具当前持有数量,不存在时返回 0。典型使用场景:UI 中显示道具剩余数量。
预期体量
接口约 20-25 行。
实现类 HeroItems 约 80-100 行。内部维护三张 Map<number, IHeroItemState>,提供增删查用及存档读档逻辑。
实现思路
id 与 num 的统一寻址
所有接口参数均接受 number | string 类型的 item。当传入为数字时,直接用作 Map 键;当传入为字符串时,需要先通过 state.tileStore.idToNumber(item) 转换为 num,再用于查找。若转换结果为 null 则视为不存在。
注意:道具 id 与 num 的转换依赖 ITileStore.idToNumber,而非 IItemStore(道具本身是 tile 的一类具体属性,通过 tile 即可定位,IItemStore 只负责道具定义存储,不需要 id↔num 双向索引)。
道具存取流程
addItem(item, count):将item解析为num,调用state.itemStore.getData(num)获取raw。若raw为null则忽略。若raw.category === ItemCategory.Pick,执行raw.effect.pickEffect(raw)后直接返回。否则,根据raw.category选择对应分表Map<number, IHeroItemState>,若已存在条目则加大count,结果 ≤ 0 则删除条目,否则创建新条目插入。getItem(item):调用addItem(item, 1)。getItemState(item):将item解析为num,通过state.itemStore.getCategory(num)获取道具类型并定位对应分表,从分表中获取条目并返回只读快照,不存在返回null。useItem(item):通过getItemState获取条目,若不存在或非 Constant/Consumable 类型则返回false;若raw.effect.canUse为false则返回false。调用raw.effect.useEffect。若为 Consumable 类型,count--,归零时删除条目。返回true。itemCount(item):通过getItemState获取条目,存在则返回count,不存在返回undefined。
存档读档
saveState:遍历三张分表,将每个条目提取{ num, count }形成IHeroItemSave,构造IHeroItemsSave并返回。loadState:遍历IHeroItemsSave中三种类型数组,对每条IHeroItemSave调用state.itemStore.getData(num)恢复raw,重建分表。
涉及文件
需要引用的文件
@user/data-common:引用ISaveableContent、IDataCommonExtended、SaveCompression、IItemRawData、ItemCategory、IDataCommon。@user/data-base/hero/types.ts:引用IHeroState,向其新增items成员。
需要修改的文件
@user/data-base/hero/types.ts
- 新增
IHeroItemSave接口:包含readonly num: number、readonly count: number。 - 新增
IHeroItemsSave接口:包含readonly constants: readonly IHeroItemSave[]、readonly consumables: readonly IHeroItemSave[]、readonly equipments: readonly IHeroItemSave[]。 - 新增
IHeroItemState接口:包含readonly id: string、readonly num: number、readonly raw: IItemRawData<THero>、count: number。 - 新增
IHeroItems<THero>接口:继承ISaveableContent<IHeroItemsSave>、IDataCommonExtended,提供addItem、getItem、getItemState、useItem、itemCount方法。 - 修改
IHeroStateSave<THero>:新增readonly items: IHeroItemsSave成员。 - 修改
IHeroState<THero>:新增readonly items: IHeroItems<THero>成员。
@user/data-base/hero/items.ts(新建)
- 实现
HeroItemState类:实现IHeroItemState接口,存储id、num、raw、count。 - 实现
HeroItems类:实现IHeroItems<THero>接口,按功能分区(查询 / 增删 / 使用 / 存档 / 私有辅助)。 HeroItems内部维护constants、consumables、equipments三张Map<number, HeroItemState>。- 实现
internalGetItemState私有方法:通过itemStore.getCategory定位分表后从中获取,返回可修改引用。 - 实现
getItemState:包装internalGetItemState,返回Readonly快照。 - 实现
addItem:通过state.itemStore.getData获取raw,Pick 类型执行效果后直接返回;其他类型按category插入对应分表,支持负数删除。 - 实现
getItem:转发至addItem(item, 1)。 - 实现
useItem:仅对 Constant/Consumable 调用raw.effect.useEffect,Consumable 减量,返回成功与否。 - 实现
itemCount:通过getItemState返回 count 或 0。 - 实现
mapToSave/loadMap:存档仅存{ num, count },读档通过itemStore.getData恢复raw。 - 实现
saveState/loadState:序列化与反序列化三张分表。
@user/data-base/hero/state.ts
HeroState构造函数中新增this.items = new HeroItems(state)。saveState中新增items: this.items.saveState(compression)。loadState中新增this.items.loadState(state.items, compression)。
@user/data-base/hero/index.ts
- 新增
export * from './items'导出。