Compare commits

...

2 Commits

Author SHA1 Message Date
c97ed868e7 chore: 调整 workspace 2026-07-02 19:43:34 +08:00
74370bba3a feat: 装备道具属性 2026-07-02 19:41:13 +08:00
6 changed files with 94 additions and 35 deletions

View File

@ -136,7 +136,7 @@ export const enum ItemCategory {
Equipment Equipment
} }
export interface IItemEffect { export interface IItemEffect<THero> {
/** 即捡即用事件内容Pick 类型) */ /** 即捡即用事件内容Pick 类型) */
readonly pickEvent: unknown; readonly pickEvent: unknown;
/** 道具使用事件内容Constant 和 Consumable 类型) */ /** 道具使用事件内容Constant 和 Consumable 类型) */
@ -146,22 +146,37 @@ export interface IItemEffect {
* *
* @param item * @param item
*/ */
pickEffect(item: IItemRawData): void; pickEffect(item: IItemRawData<THero>): void;
/** /**
* 使使 * 使使
* @param item * @param item
*/ */
useEffect(item: IItemRawData): void; useEffect(item: IItemRawData<THero>): void;
/** /**
* 使 * 使
* @param item * @param item
*/ */
canUse(item: IItemRawData): boolean; canUse(item: IItemRawData<THero>): boolean;
} }
export interface IItemRawData { export interface IItemEquipData<THero> {
/** 可以装备至的装备槽,`number` 表示指定索引装备槽,`string` 表示指定装备槽名称,每个装备槽之间为或的关系 */
readonly slots: (number | string)[];
/** 动画 id */
readonly animate: AnimationIds;
/** 数值加成 */
readonly value: Map<SelectKey<THero, number>, number>;
/** 百分比加成 */
readonly percentage: Map<SelectKey<THero, number>, number>;
/** 穿上装备事件 */
readonly loadEvent: unknown;
/** 脱下装备事件 */
readonly unloadEvent: unknown;
}
export interface IItemRawData<THero> {
/** 道具在地图上的图块数字 */ /** 道具在地图上的图块数字 */
readonly num: number; readonly num: number;
/** 道具的字符串标识符 */ /** 道具的字符串标识符 */
@ -177,26 +192,26 @@ export interface IItemRawData {
readonly hideInToolbox: boolean; readonly hideInToolbox: boolean;
/** 道具效果对象(非 Equipment 类型) */ /** 道具效果对象(非 Equipment 类型) */
readonly effect: IItemEffect; readonly effect: IItemEffect<THero>;
/** 装备道具属性Equipment 类型) */ /** 装备道具属性Equipment 类型) */
readonly equip: unknown; readonly equip: IItemEquipData<THero>;
} }
export interface IItemLegacyConverter<TLegacy> { export interface IItemLegacyConverter<THero, TLegacy> {
/** /**
* *
* @param num * @param num
* @param legacy * @param legacy
*/ */
fromLegacy(num: number, legacy: TLegacy): IItemRawData; fromLegacy(num: number, legacy: TLegacy): IItemRawData<THero>;
} }
export interface IItemStore<TLegacy> { export interface IItemStore<THero, TLegacy> {
/** /**
* *
* @param num * @param num
*/ */
getData(num: number): IItemRawData | null; getData(num: number): IItemRawData<THero> | null;
/** /**
* *
@ -208,20 +223,22 @@ export interface IItemStore<TLegacy> {
* *
* @param data * @param data
*/ */
addItem(data: IItemRawData): void; addItem(data: IItemRawData<THero>): void;
/** /**
* *
* @param converter * @param converter
*/ */
attachLegacyConverter(converter: IItemLegacyConverter<TLegacy>): void; attachLegacyConverter(
converter: IItemLegacyConverter<THero, TLegacy>
): void;
/** /**
* 使 * 使
* @param num * @param num
* @param legacy * @param legacy
*/ */
fromLegacy(num: number, legacy: TLegacy): IItemRawData; fromLegacy(num: number, legacy: TLegacy): IItemRawData<THero>;
} }
//#endregion //#endregion

View File

@ -47,7 +47,7 @@ export interface IDataCommon {
/** 图块定义存储 */ /** 图块定义存储 */
readonly tileStore: ITileStore<MapDataOf<keyof NumberToId>>; readonly tileStore: ITileStore<MapDataOf<keyof NumberToId>>;
/** 道具定义存储 */ /** 道具定义存储 */
readonly itemStore: IItemStore<Item<AllIdsOf<'items'>>>; readonly itemStore: IItemStore<IHeroAttr, Item<AllIdsOf<'items'>>>;
/** 朝向绑定 */ /** 朝向绑定 */
readonly roleFace: IRoleFaceBinder; readonly roleFace: IRoleFaceBinder;
/** 朝向管理 */ /** 朝向管理 */

View File

@ -82,7 +82,7 @@ export class CoreState implements ICoreState {
readonly roleFace: IRoleFaceBinder; readonly roleFace: IRoleFaceBinder;
readonly faceManager: IFaceManager; readonly faceManager: IFaceManager;
readonly tileStore: ITileStore<LegacyTileData>; readonly tileStore: ITileStore<LegacyTileData>;
readonly itemStore: IItemStore<LegacyItemData>; readonly itemStore: IItemStore<IHeroAttr, LegacyItemData>;
// Layer 1 数据层,所有可存档内容都在这,一般用于数据存储 // Layer 1 数据层,所有可存档内容都在这,一般用于数据存储
readonly maps: IMapStore; readonly maps: IMapStore;

View File

@ -1,5 +1,7 @@
import { import {
IHeroAttr,
IItemEffect, IItemEffect,
IItemEquipData,
IItemLegacyConverter, IItemLegacyConverter,
IItemRawData, IItemRawData,
ItemCategory ItemCategory
@ -8,16 +10,26 @@ import { IStateSystem } from '@user/data-system';
export type LegacyItemData = Item<AllIdsOf<'items'>>; export type LegacyItemData = Item<AllIdsOf<'items'>>;
type LegacyItemEffectFn = (state: IStateSystem, item: IItemRawData) => void; type LegacyItemEffectFn = (
type LegacyItemCanUseFn = (state: IStateSystem, item: IItemRawData) => boolean; state: IStateSystem,
item: IItemRawData<IHeroAttr>
) => void;
type LegacyItemCanUseFn = (
state: IStateSystem,
item: IItemRawData<IHeroAttr>
) => boolean;
class LegacyItemEffect implements IItemEffect { class LegacyItemEffect implements IItemEffect<IHeroAttr> {
pickEvent: unknown; pickEvent: unknown;
useEvent: unknown; useEvent: unknown;
/** 即捡即用效果 */
private readonly pickFn: LegacyItemEffectFn | null = null; private readonly pickFn: LegacyItemEffectFn | null = null;
/** 道具使用效果 */
private readonly useFn: LegacyItemEffectFn | null = null; private readonly useFn: LegacyItemEffectFn | null = null;
/** 是否能够使用道具 */
private readonly canUseFn: LegacyItemCanUseFn | null = null; private readonly canUseFn: LegacyItemCanUseFn | null = null;
/** 全局状态对象 */
private readonly state: IStateSystem; private readonly state: IStateSystem;
constructor(legacy: LegacyItemData, state: IStateSystem) { constructor(legacy: LegacyItemData, state: IStateSystem) {
@ -50,24 +62,27 @@ class LegacyItemEffect implements IItemEffect {
} }
} }
pickEffect(item: IItemRawData): void { pickEffect(item: IItemRawData<IHeroAttr>): void {
this.pickFn?.(this.state, item); this.pickFn?.(this.state, item);
} }
useEffect(item: IItemRawData): void { useEffect(item: IItemRawData<IHeroAttr>): void {
this.useFn?.(this.state, item); this.useFn?.(this.state, item);
} }
canUse(item: IItemRawData): boolean { canUse(item: IItemRawData<IHeroAttr>): boolean {
if (!this.canUseFn) return true; if (!this.canUseFn) return true;
else return this.canUseFn(this.state, item); else return this.canUseFn(this.state, item);
} }
} }
export class ItemLegacyBridge implements IItemLegacyConverter<LegacyItemData> { export class ItemLegacyBridge implements IItemLegacyConverter<
IHeroAttr,
LegacyItemData
> {
constructor(private readonly state: IStateSystem) {} constructor(private readonly state: IStateSystem) {}
fromLegacy(num: number, legacy: LegacyItemData): IItemRawData { fromLegacy(num: number, legacy: LegacyItemData): IItemRawData<IHeroAttr> {
const effect = new LegacyItemEffect(legacy, this.state); const effect = new LegacyItemEffect(legacy, this.state);
return { return {
num, num,
@ -77,10 +92,35 @@ export class ItemLegacyBridge implements IItemLegacyConverter<LegacyItemData> {
text: legacy.text ?? '', text: legacy.text ?? '',
hideInToolbox: legacy.hideInToolBox, hideInToolbox: legacy.hideInToolBox,
effect, effect,
equip: legacy.equip equip: this.convertEquip(legacy.equip)
}; };
} }
private convertEquip(legacy: Equip): IItemEquipData<IHeroAttr> {
const valueMap = new Map<SelectKey<IHeroAttr, number>, number>();
const perMap = new Map<SelectKey<IHeroAttr, number>, number>();
for (const [key, value] of Object.entries(legacy.value)) {
valueMap.set(key as SelectKey<IHeroAttr, number>, value);
}
for (const [key, value] of Object.entries(legacy.percentage)) {
perMap.set(key as SelectKey<IHeroAttr, number>, value);
}
return {
slots: [legacy.type],
animate: legacy.animate,
value: valueMap,
percentage: perMap,
loadEvent: legacy.equipEvent,
unloadEvent: legacy.unequipEvent
};
}
/**
*
* @param cls
*/
private mapCategory(cls: ItemCls): ItemCategory { private mapCategory(cls: ItemCls): ItemCategory {
switch (cls) { switch (cls) {
case 'constants': case 'constants':

View File

@ -1,12 +1,14 @@
packages: packages:
- packages/* - packages/*
- packages-user/* - packages-user/*
- src/ - src/
ignore: ignore:
- packages/**/dist - packages/**/dist
- packages/**/node_modules - packages/**/node_modules
- packages-user/**/dist
- packages-user/**/node_modules
onlyBuiltDependencies: onlyBuiltDependencies:
- core-js - core-js
- esbuild - esbuild
- ttf2woff2 - ttf2woff2
- vue-demi - vue-demi

View File

@ -22,7 +22,7 @@
2. **不恢复我的修改**:我做的任何代码修改都是有原因的。若我在两次对话期间新增、删除或修改了部分代码,不要将其恢复。 2. **不恢复我的修改**:我做的任何代码修改都是有原因的。若我在两次对话期间新增、删除或修改了部分代码,不要将其恢复。
3. **遇到歧义立即提问**:若思考或实现时遇到任何问题——例如描述模糊、接口不清晰、某些地方存在歧义等——应立即向我提问,而不是按自己的想法去写。若完成需求所需的接口尚不存在,也应当立即停止实现,向我提出疑问,而不是擅自新增接口来推进。 3. **遇到歧义立即提问**:若思考或实现时遇到任何问题——例如描述模糊、接口不清晰、某些地方存在歧义等——应立即向我提问,而不是按自己的想法去写。若完成需求所需的接口尚不存在,也应当立即停止实现,向我提出疑问,而不是擅自新增接口来推进。
4. **修改文件前重读文件,修改后说明内容**:修改文件前务必重新阅读以确保内容是最新版。修改后必须在对话中说明所有改动的文件及具体改动内容。在修改文件时,**不得修改**文档的 `涉及文件` 节未提及的文件。 4. **修改文件前重读文件,修改后说明内容**:修改文件前务必重新阅读以确保内容是最新版。修改后必须在对话中说明所有改动的文件及具体改动内容。在修改文件时,**不得修改**文档的 `涉及文件` 节未提及的文件。
5. **关于 `dev.md` 的强度升级**`dev.md` 中"一般不建议/尽量避免"的条目在此处视为**绝对禁止**"建议/最好"的条目在此处视为**必须遵守**。 5. **关于 `dev.md` 的强度升级**`dev.md` 中"一般不建议/尽量避免"的条目在此处视为**绝对禁止**"建议/最好"的条目在此处视为**必须遵守**。所有私有方法与成员必须添加 jsDoc 注释,在构造函数的参数中定义的成员除外。
6. **渲染端永远处于被动**:任何情况下都不可能会出现数据端主动通知渲染端进行更新的场景,渲染端仅通过钩子与数据端通信,通过钩子被动获取信息,并在某些情况下影响数据端行为。 6. **渲染端永远处于被动**:任何情况下都不可能会出现数据端主动通知渲染端进行更新的场景,渲染端仅通过钩子与数据端通信,通过钩子被动获取信息,并在某些情况下影响数据端行为。
7. **关于非空判断**:对于对象,直接使用 `if (!object)` 的方式进行判断,对于字面量,使用 `lodash` 接口 `isNil` 判断 `if (isNil(value))`,不要使用 `if (value === undefined)` 这种方式。 7. **关于非空判断**:对于对象,直接使用 `if (!object)` 的方式进行判断,对于字面量,使用 `lodash` 接口 `isNil` 判断 `if (isNil(value))`,不要使用 `if (value === undefined)` 这种方式。
8. **方法顺序**:对于类的方法,根据功能进行排序,而不是按照修饰词进行排序。对于较长或功能较多的类,使用 `#region` 进行分区,分区按照方法功能进行划分。所有类的成员必须写到类的开头,并按照功能排序。 8. **方法顺序**:对于类的方法,根据功能进行排序,而不是按照修饰词进行排序。对于较长或功能较多的类,使用 `#region` 进行分区,分区按照方法功能进行划分。所有类的成员必须写到类的开头,并按照功能排序。