diff --git a/dev.md b/dev.md index 05714d5..e4474e5 100644 --- a/dev.md +++ b/dev.md @@ -98,7 +98,7 @@ - 只进行必要的非空判断,非必要时直接使用非空断言 `!`。 - 除非参数要求传入函数等情况,不建议在函数内定义局部函数。 - 语句尽量不换行,除非必要,尤其注意三元运算符与 `private readonly` 类成员。 -- 任何时候都不应该写 `getter` 和 `setter`。 +- 一般不建议写 `getter` 和 `setter`,但某些场景下还是可以使用的。 ## 双端分离 @@ -133,4 +133,4 @@ | `@user/data-common | Layer 0 | 公共层,定义 `IDataCommon` 及公共无依赖接口 | | `@user/data-base` | Layer 1 | 数据层,定义 `IStateBase` 及可存档游戏数据(地图、怪物、玩家属性等) | | `@user/data-system` | Layer 2 | 执行层,定义 `ICoreState`,依赖数据层实现玩家控制、战斗计算等影响游戏进程的动作 | -| `@user/data-state` | — | 数据端的顶层模块,指导 Layer 2 的执行行为,不直接参与执行 | +| `@user/data-state` | Layer 3 | 数据端的顶层模块,一般仅用于初始化以及仅供渲染端调用的顶层模块 | diff --git a/packages-user/client-modules/src/render/map/extension/hero.ts b/packages-user/client-modules/src/render/map/extension/hero.ts index e28d05a..3ecaeac 100644 --- a/packages-user/client-modules/src/render/map/extension/hero.ts +++ b/packages-user/client-modules/src/render/map/extension/hero.ts @@ -3,8 +3,8 @@ import { FaceDirection, getFaceMovement, HeroAnimateDirection, - IHeroMover, - IHeroMovingHooks, + IHeroMoveController, + IHeroMoveControllerHooks, nextFaceDirection } from '@user/data-base'; import { IMapLayer } from '@user/data-base'; @@ -55,7 +55,7 @@ export class MapHeroRenderer implements IMapHeroRenderer { new TextureRowSplitter(); /** 勇士钩子 */ - readonly controller: IHookController; + readonly controller: IHookController; /** 勇士每个朝向的贴图对象 */ readonly textureMap: Map = new Map(); /** 勇士渲染实体,与 `entities[0]` 同引用 */ @@ -73,7 +73,7 @@ export class MapHeroRenderer implements IMapHeroRenderer { constructor( readonly renderer: IMapRenderer, readonly layer: IMapLayer, - readonly hero: IHeroMover + readonly hero: IHeroMoveController ) { this.controller = hero.addHook(new MapHeroHook(this)); this.controller.load(); @@ -107,7 +107,7 @@ export class MapHeroRenderer implements IMapHeroRenderer { private addHeroMoving( renderer: IMapRenderer, layer: IMapLayer, - hero: IHeroMover + hero: IHeroMoveController ) { if (isNil(hero.image)) { logger.warn(88); @@ -451,7 +451,7 @@ export class MapHeroRenderer implements IMapHeroRenderer { } } -class MapHeroHook implements Partial { +class MapHeroHook implements Partial { constructor(readonly hero: MapHeroRenderer) {} onSetImage(image: ImageIds): void { diff --git a/packages-user/client-modules/src/render/map/extension/manager.ts b/packages-user/client-modules/src/render/map/extension/manager.ts index 4a01b30..2850779 100644 --- a/packages-user/client-modules/src/render/map/extension/manager.ts +++ b/packages-user/client-modules/src/render/map/extension/manager.ts @@ -1,4 +1,4 @@ -import { IHeroMover } from '@user/data-base'; +import { IHeroMoveController } from '@user/data-base'; import { IMapLayer } from '@user/data-base'; import { IMapDoorRenderer, @@ -14,7 +14,7 @@ import { IOnMapTextRenderer } from './types'; export class MapExtensionManager implements IMapExtensionManager { /** 勇士状态至勇士渲染器的映射 */ - readonly heroMap: Map = new Map(); + readonly heroMap: Map = new Map(); /** 地图图层到门渲染器的映射 */ readonly doorMap: Map = new Map(); /** 单例的文字渲染拓展(独立图层) */ @@ -22,7 +22,10 @@ export class MapExtensionManager implements IMapExtensionManager { constructor(readonly renderer: IMapRenderer) {} - addHero(state: IHeroMover, layer: IMapLayer): IMapHeroRenderer | null { + addHero( + state: IHeroMoveController, + layer: IMapLayer + ): IMapHeroRenderer | null { if (this.heroMap.has(state)) { logger.error(45, 'hero renderer'); return null; @@ -32,7 +35,7 @@ export class MapExtensionManager implements IMapExtensionManager { return heroRenderer; } - removeHero(state: IHeroMover): void { + removeHero(state: IHeroMoveController): void { const renderer = this.heroMap.get(state); if (!renderer) return; renderer.destroy(); diff --git a/packages-user/client-modules/src/render/map/extension/types.ts b/packages-user/client-modules/src/render/map/extension/types.ts index bd685cc..e06064c 100644 --- a/packages-user/client-modules/src/render/map/extension/types.ts +++ b/packages-user/client-modules/src/render/map/extension/types.ts @@ -2,7 +2,7 @@ import { ITexture, Font } from '@motajs/render'; import { FaceDirection, HeroAnimateDirection, - IHeroMover + IHeroMoveController } from '@user/data-base'; import { IMapLayer } from '@user/data-base'; @@ -10,7 +10,7 @@ import { IMapRenderResult } from '../types'; export interface IMapExtensionManager { /** 勇士状态至勇士渲染器的映射 */ - readonly heroMap: Map; + readonly heroMap: Map; /** 地图图层到门渲染器的映射 */ readonly doorMap: Map; /** 单例的文字渲染拓展(独立图层) */ @@ -21,13 +21,16 @@ export interface IMapExtensionManager { * @param state 勇士状态 * @param layer 勇士所在图层 */ - addHero(state: IHeroMover, layer: IMapLayer): IMapHeroRenderer | null; + addHero( + state: IHeroMoveController, + layer: IMapLayer + ): IMapHeroRenderer | null; /** * 移除勇士渲染拓展 * @param state 勇士状态 */ - removeHero(state: IHeroMover): void; + removeHero(state: IHeroMoveController): void; /** * 添加开门动画拓展 diff --git a/packages-user/data-base/src/hero/attribute.ts b/packages-user/data-base/src/hero/attribute.ts index 4e0a8a8..ef55598 100644 --- a/packages-user/data-base/src/hero/attribute.ts +++ b/packages-user/data-base/src/hero/attribute.ts @@ -87,6 +87,24 @@ export class HeroAttribute implements IHeroAttribute { this.finalAttribute[name] = value; } + *catchCalculateProgress(name: K) { + const modifierList = this.modifier.get(name); + if (!modifierList) return; + + const baseValue = this.attribute[name]; + let value = baseValue; + for (const modifier of modifierList as IHeroModifier[]) { + const nextValue = modifier.modify(value, baseValue, name); + // 部署之后就没必要弹这个警告了,额外判断反而可能会有一定的性能损失,直接 tree-shaking 优化掉 + if (import.meta.env.DEV && this.isSameReference(value, nextValue)) { + const modiferName = modifier.constructor.name; + logger.warn(109, modiferName, String(name)); + } + value = nextValue; + yield [modifier, value] as [IHeroModifier, THero[K]]; + } + } + getBaseAttribute(name: K): THero[K] { return this.attribute[name]; } diff --git a/packages-user/data-base/src/hero/mover.ts b/packages-user/data-base/src/hero/mover.ts index b384978..bab6792 100644 --- a/packages-user/data-base/src/hero/mover.ts +++ b/packages-user/data-base/src/hero/mover.ts @@ -1,14 +1,18 @@ import { Hookable, HookController, IHookController } from '@motajs/common'; import { isNil } from 'lodash-es'; import { getFaceMovement, nextFaceDirection } from '@user/data-common'; -import { IHeroFollower, IHeroMover, IHeroMovingHooks } from './types'; +import { + IHeroFollower, + IHeroMoveController, + IHeroMoveControllerHooks +} from './types'; import { FaceDirection } from '@user/data-common'; const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png'; -export class HeroMover - extends Hookable - implements IHeroMover +export class HeroMoveController + extends Hookable + implements IHeroMoveController { x: number = 0; y: number = 0; @@ -22,8 +26,8 @@ export class HeroMover readonly followers: IHeroFollower[] = []; protected createController( - hook: Partial - ): IHookController { + hook: Partial + ): IHookController { return new HookController(this, hook); } diff --git a/packages-user/data-base/src/hero/state.ts b/packages-user/data-base/src/hero/state.ts index 045e470..ece98db 100644 --- a/packages-user/data-base/src/hero/state.ts +++ b/packages-user/data-base/src/hero/state.ts @@ -2,7 +2,7 @@ import { HeroAttribute } from './attribute'; import { IHeroAttribute, IHeroModifier, - IHeroMover, + IHeroMoveController, IHeroState, IHeroStateSave, IModifierStateSave, @@ -16,11 +16,11 @@ export class HeroState implements IHeroState { private readonly registry: Map IHeroModifier> = new Map(); constructor( - public mover: IHeroMover, + public mover: IHeroMoveController, public attribute: IHeroAttribute ) {} - attachMover(mover: IHeroMover): void { + attachMover(mover: IHeroMoveController): void { this.mover = mover; } @@ -28,7 +28,7 @@ export class HeroState implements IHeroState { this.attribute = attribute; } - getHeroMover(): IHeroMover { + getHeroMover(): IHeroMoveController { return this.mover; } diff --git a/packages-user/data-base/src/hero/types.ts b/packages-user/data-base/src/hero/types.ts index 3d0f048..e0d9895 100644 --- a/packages-user/data-base/src/hero/types.ts +++ b/packages-user/data-base/src/hero/types.ts @@ -4,10 +4,10 @@ import { FaceDirection, ISaveableContent } from '@user/data-common'; //#region 勇士属性 export interface IHeroModifier< - T = unknown, + K = unknown, V = unknown, - S = unknown -> extends ISaveableContent { + Save = unknown +> extends ISaveableContent { /** 修饰器类型 */ readonly type: string; /** 修饰器优先级 */ @@ -40,12 +40,12 @@ export interface IHeroModifier< * @param baseValue 该属性值的基础属性值 * @param name 属性名称 */ - modify(value: T, baseValue: T, name: PropertyKey): T; + modify(value: K, baseValue: K, name: PropertyKey): K; /** * 深拷贝此修饰器 */ - clone(): IHeroModifier; + clone(): IHeroModifier; } export interface IModifierStateSave { @@ -129,7 +129,7 @@ export interface IHeroAttribute extends IReadonlyHeroAttribute { */ addModifier( name: K, - modifier: IHeroModifier + modifier: IHeroModifier ): void; /** @@ -139,7 +139,7 @@ export interface IHeroAttribute extends IReadonlyHeroAttribute { */ deleteModifier( name: K, - modifier: IHeroModifier + modifier: IHeroModifier ): void; /** @@ -147,6 +147,15 @@ export interface IHeroAttribute extends IReadonlyHeroAttribute { * @param cloneModifier 是否同时复制修饰器,默认复制 */ clone(cloneModifier?: boolean): IHeroAttribute; + + /** + * 获取勇士指定属性计算过程的可迭代对象,一般用于调试。 + * 此方法仅输出计算过程及结果,不会修改内部存储的最终属性。 + * @param name 属性名称 + */ + catchCalculateProgress( + name: K + ): Iterable<[IHeroModifier, THero[K]]>; } //#endregion @@ -169,7 +178,7 @@ export interface IHeroFollower { alpha: number; } -export interface IHeroMovingHooks extends IHookBase { +export interface IHeroMoveControllerHooks extends IHookBase { /** * 当设置勇士的坐标时触发 * @param x 勇士横坐标 @@ -253,7 +262,7 @@ export interface IHeroMovingHooks extends IHookBase { onSetFollowerAlpha(identifier: string, alpha: number): void; } -export interface IHeroMover extends IHookable { +export interface IHeroMoveController extends IHookable { /** 勇士横坐标 */ readonly x: number; /** 勇士纵坐标 */ @@ -372,7 +381,7 @@ export interface IHeroState extends ISaveableContent< IHeroStateSave > { /** 勇士移动对象 */ - readonly mover: IHeroMover; + readonly mover: IHeroMoveController; /** 勇士属性对象 */ readonly attribute: IReadonlyHeroAttribute; @@ -380,12 +389,12 @@ export interface IHeroState extends ISaveableContent< * 绑定勇士移动对象 * @param mover 勇士移动对象 */ - attachMover(mover: IHeroMover): void; + attachMover(mover: IHeroMoveController): void; /** * 获取勇士移动对象 */ - getHeroMover(): IHeroMover; + getHeroMover(): IHeroMoveController; /** * 绑定勇士属性对象 diff --git a/packages-user/data-state/src/core.ts b/packages-user/data-state/src/core.ts index 37aefd2..538eb70 100644 --- a/packages-user/data-state/src/core.ts +++ b/packages-user/data-state/src/core.ts @@ -17,7 +17,7 @@ import { } from '@user/data-common'; import { EnemyManager, - HeroMover, + HeroMoveController, IEnemyManager, HeroAttribute, HeroState, @@ -69,23 +69,23 @@ import { logger } from '@motajs/common'; import { ISaveSystem, SaveSystem } from './save'; export class CoreState implements ICoreState { - // Layer 0 公共层 + // Layer 0 公共层,最底层的接口,不会依赖任何其他内容,一般是工具性接口及不需要存档的数据 readonly roleFace: IRoleFaceBinder; readonly faceManager: IFaceManager; readonly tileStore: ITileStore; - // Layer 1 数据层,所有可存档内容都在这 + // Layer 1 数据层,所有可存档内容都在这,一般用于数据存储 readonly maps: IMapStore; readonly hero: IHeroState; readonly enemyManager: IEnemyManager; readonly flags: IFlagSystem; - // Layer 2 执行层,游戏逻辑对象都在这 + // Layer 2 执行层,游戏逻辑对象都在这,包括一些需要操作数据层的逻辑系统等 readonly enemyContext: IEnemyContext; readonly triggerRegistry: ITriggerRegistry; readonly triggerCollector: ITriggerCollector; - // 用户层内容,也就是最顶层的内容,一般仅用于初始化 + // Layer 3 用户层,也就是最顶层的内容,一般仅用于初始化以及仅供渲染端调用的顶层模块 readonly loadProgress: ILoadProgressTotal; readonly dataLoader: IMotaDataLoader; readonly saveSystem: ISaveSystem; @@ -129,7 +129,7 @@ export class CoreState implements ICoreState { this.maps = new MapStore(tileStore, this); // 勇士 - const heroMover = new HeroMover(); + const heroMover = new HeroMoveController(); const heroAttribute = new HeroAttribute(HERO_DEFAULT_ATTRIBUTE); const heroState = new HeroState(heroMover, heroAttribute); this.hero = heroState; @@ -179,7 +179,7 @@ export class CoreState implements ICoreState { //#endregion - //#region 顶层初始化 + //#region L3 初始化 // 存档系统 this.saveSystem = new SaveSystem(); @@ -209,8 +209,12 @@ export class CoreState implements ICoreState { core.floors as Record ); }); + + //#endregion } + //#region 私有方法 + /** * 初始化图块存储对象 * @param data 旧样板图块定义对象 @@ -355,6 +359,10 @@ export class CoreState implements ICoreState { this.maps.compareWith(reference); } + //#endregion + + //#region 存档方法 + addSaveableContent(id: string, content: ISaveableContent): void { if (this.saveables.has(id)) { logger.warn(112, id); @@ -385,4 +393,6 @@ export class CoreState implements ICoreState { this.executors.set(content, executor); } } + + //#endregion }