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
}
export interface IItemEffect {
export interface IItemEffect<THero> {
/** 即捡即用事件内容Pick 类型) */
readonly pickEvent: unknown;
/** 道具使用事件内容Constant 和 Consumable 类型) */
@ -146,22 +146,37 @@ export interface IItemEffect {
*
* @param item
*/
pickEffect(item: IItemRawData): void;
pickEffect(item: IItemRawData<THero>): void;
/**
* 使使
* @param item
*/
useEffect(item: IItemRawData): void;
useEffect(item: IItemRawData<THero>): void;
/**
* 使
* @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;
/** 道具的字符串标识符 */
@ -177,26 +192,26 @@ export interface IItemRawData {
readonly hideInToolbox: boolean;
/** 道具效果对象(非 Equipment 类型) */
readonly effect: IItemEffect;
readonly effect: IItemEffect<THero>;
/** 装备道具属性Equipment 类型) */
readonly equip: unknown;
readonly equip: IItemEquipData<THero>;
}
export interface IItemLegacyConverter<TLegacy> {
export interface IItemLegacyConverter<THero, TLegacy> {
/**
*
* @param num
* @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
*/
getData(num: number): IItemRawData | null;
getData(num: number): IItemRawData<THero> | null;
/**
*
@ -208,20 +223,22 @@ export interface IItemStore<TLegacy> {
*
* @param data
*/
addItem(data: IItemRawData): void;
addItem(data: IItemRawData<THero>): void;
/**
*
* @param converter
*/
attachLegacyConverter(converter: IItemLegacyConverter<TLegacy>): void;
attachLegacyConverter(
converter: IItemLegacyConverter<THero, TLegacy>
): void;
/**
* 使
* @param num
* @param legacy
*/
fromLegacy(num: number, legacy: TLegacy): IItemRawData;
fromLegacy(num: number, legacy: TLegacy): IItemRawData<THero>;
}
//#endregion

View File

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

View File

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

View File

@ -1,5 +1,7 @@
import {
IHeroAttr,
IItemEffect,
IItemEquipData,
IItemLegacyConverter,
IItemRawData,
ItemCategory
@ -8,16 +10,26 @@ import { IStateSystem } from '@user/data-system';
export type LegacyItemData = Item<AllIdsOf<'items'>>;
type LegacyItemEffectFn = (state: IStateSystem, item: IItemRawData) => void;
type LegacyItemCanUseFn = (state: IStateSystem, item: IItemRawData) => boolean;
type LegacyItemEffectFn = (
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;
useEvent: unknown;
/** 即捡即用效果 */
private readonly pickFn: LegacyItemEffectFn | null = null;
/** 道具使用效果 */
private readonly useFn: LegacyItemEffectFn | null = null;
/** 是否能够使用道具 */
private readonly canUseFn: LegacyItemCanUseFn | null = null;
/** 全局状态对象 */
private readonly 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);
}
useEffect(item: IItemRawData): void {
useEffect(item: IItemRawData<IHeroAttr>): void {
this.useFn?.(this.state, item);
}
canUse(item: IItemRawData): boolean {
canUse(item: IItemRawData<IHeroAttr>): boolean {
if (!this.canUseFn) return true;
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) {}
fromLegacy(num: number, legacy: LegacyItemData): IItemRawData {
fromLegacy(num: number, legacy: LegacyItemData): IItemRawData<IHeroAttr> {
const effect = new LegacyItemEffect(legacy, this.state);
return {
num,
@ -77,10 +92,35 @@ export class ItemLegacyBridge implements IItemLegacyConverter<LegacyItemData> {
text: legacy.text ?? '',
hideInToolbox: legacy.hideInToolBox,
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 {
switch (cls) {
case 'constants':

View File

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

View File

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