refactor: 修改 mover 名称

This commit is contained in:
unanmed 2026-05-29 14:07:21 +08:00
parent 2c89a4ac37
commit c48f91c25c
9 changed files with 92 additions and 45 deletions

4
dev.md
View File

@ -98,7 +98,7 @@
- 只进行必要的非空判断,非必要时直接使用非空断言 `!` - 只进行必要的非空判断,非必要时直接使用非空断言 `!`
- 除非参数要求传入函数等情况,不建议在函数内定义局部函数。 - 除非参数要求传入函数等情况,不建议在函数内定义局部函数。
- 语句尽量不换行,除非必要,尤其注意三元运算符与 `private readonly` 类成员。 - 语句尽量不换行,除非必要,尤其注意三元运算符与 `private readonly` 类成员。
- 任何时候都不应该写 `getter``setter` - 一般不建议写 `getter``setter`,但某些场景下还是可以使用的
## 双端分离 ## 双端分离
@ -133,4 +133,4 @@
| `@user/data-common | Layer 0 | 公共层,定义 `IDataCommon` 及公共无依赖接口 | | `@user/data-common | Layer 0 | 公共层,定义 `IDataCommon` 及公共无依赖接口 |
| `@user/data-base` | Layer 1 | 数据层,定义 `IStateBase` 及可存档游戏数据(地图、怪物、玩家属性等) | | `@user/data-base` | Layer 1 | 数据层,定义 `IStateBase` 及可存档游戏数据(地图、怪物、玩家属性等) |
| `@user/data-system` | Layer 2 | 执行层,定义 `ICoreState`,依赖数据层实现玩家控制、战斗计算等影响游戏进程的动作 | | `@user/data-system` | Layer 2 | 执行层,定义 `ICoreState`,依赖数据层实现玩家控制、战斗计算等影响游戏进程的动作 |
| `@user/data-state` | — | 数据端的顶层模块,指导 Layer 2 的执行行为,不直接参与执行 | | `@user/data-state` | Layer 3 | 数据端的顶层模块,一般仅用于初始化以及仅供渲染端调用的顶层模块 |

View File

@ -3,8 +3,8 @@ import {
FaceDirection, FaceDirection,
getFaceMovement, getFaceMovement,
HeroAnimateDirection, HeroAnimateDirection,
IHeroMover, IHeroMoveController,
IHeroMovingHooks, IHeroMoveControllerHooks,
nextFaceDirection nextFaceDirection
} from '@user/data-base'; } from '@user/data-base';
import { IMapLayer } from '@user/data-base'; import { IMapLayer } from '@user/data-base';
@ -55,7 +55,7 @@ export class MapHeroRenderer implements IMapHeroRenderer {
new TextureRowSplitter(); new TextureRowSplitter();
/** 勇士钩子 */ /** 勇士钩子 */
readonly controller: IHookController<IHeroMovingHooks>; readonly controller: IHookController<IHeroMoveControllerHooks>;
/** 勇士每个朝向的贴图对象 */ /** 勇士每个朝向的贴图对象 */
readonly textureMap: Map<FaceDirection, IMaterialFramedData> = new Map(); readonly textureMap: Map<FaceDirection, IMaterialFramedData> = new Map();
/** 勇士渲染实体,与 `entities[0]` 同引用 */ /** 勇士渲染实体,与 `entities[0]` 同引用 */
@ -73,7 +73,7 @@ export class MapHeroRenderer implements IMapHeroRenderer {
constructor( constructor(
readonly renderer: IMapRenderer, readonly renderer: IMapRenderer,
readonly layer: IMapLayer, readonly layer: IMapLayer,
readonly hero: IHeroMover readonly hero: IHeroMoveController
) { ) {
this.controller = hero.addHook(new MapHeroHook(this)); this.controller = hero.addHook(new MapHeroHook(this));
this.controller.load(); this.controller.load();
@ -107,7 +107,7 @@ export class MapHeroRenderer implements IMapHeroRenderer {
private addHeroMoving( private addHeroMoving(
renderer: IMapRenderer, renderer: IMapRenderer,
layer: IMapLayer, layer: IMapLayer,
hero: IHeroMover hero: IHeroMoveController
) { ) {
if (isNil(hero.image)) { if (isNil(hero.image)) {
logger.warn(88); logger.warn(88);
@ -451,7 +451,7 @@ export class MapHeroRenderer implements IMapHeroRenderer {
} }
} }
class MapHeroHook implements Partial<IHeroMovingHooks> { class MapHeroHook implements Partial<IHeroMoveControllerHooks> {
constructor(readonly hero: MapHeroRenderer) {} constructor(readonly hero: MapHeroRenderer) {}
onSetImage(image: ImageIds): void { onSetImage(image: ImageIds): void {

View File

@ -1,4 +1,4 @@
import { IHeroMover } from '@user/data-base'; import { IHeroMoveController } from '@user/data-base';
import { IMapLayer } from '@user/data-base'; import { IMapLayer } from '@user/data-base';
import { import {
IMapDoorRenderer, IMapDoorRenderer,
@ -14,7 +14,7 @@ import { IOnMapTextRenderer } from './types';
export class MapExtensionManager implements IMapExtensionManager { export class MapExtensionManager implements IMapExtensionManager {
/** 勇士状态至勇士渲染器的映射 */ /** 勇士状态至勇士渲染器的映射 */
readonly heroMap: Map<IHeroMover, IMapHeroRenderer> = new Map(); readonly heroMap: Map<IHeroMoveController, IMapHeroRenderer> = new Map();
/** 地图图层到门渲染器的映射 */ /** 地图图层到门渲染器的映射 */
readonly doorMap: Map<IMapLayer, IMapDoorRenderer> = new Map(); readonly doorMap: Map<IMapLayer, IMapDoorRenderer> = new Map();
/** 单例的文字渲染拓展(独立图层) */ /** 单例的文字渲染拓展(独立图层) */
@ -22,7 +22,10 @@ export class MapExtensionManager implements IMapExtensionManager {
constructor(readonly renderer: IMapRenderer) {} constructor(readonly renderer: IMapRenderer) {}
addHero(state: IHeroMover, layer: IMapLayer): IMapHeroRenderer | null { addHero(
state: IHeroMoveController,
layer: IMapLayer
): IMapHeroRenderer | null {
if (this.heroMap.has(state)) { if (this.heroMap.has(state)) {
logger.error(45, 'hero renderer'); logger.error(45, 'hero renderer');
return null; return null;
@ -32,7 +35,7 @@ export class MapExtensionManager implements IMapExtensionManager {
return heroRenderer; return heroRenderer;
} }
removeHero(state: IHeroMover): void { removeHero(state: IHeroMoveController): void {
const renderer = this.heroMap.get(state); const renderer = this.heroMap.get(state);
if (!renderer) return; if (!renderer) return;
renderer.destroy(); renderer.destroy();

View File

@ -2,7 +2,7 @@ import { ITexture, Font } from '@motajs/render';
import { import {
FaceDirection, FaceDirection,
HeroAnimateDirection, HeroAnimateDirection,
IHeroMover IHeroMoveController
} from '@user/data-base'; } from '@user/data-base';
import { IMapLayer } from '@user/data-base'; import { IMapLayer } from '@user/data-base';
@ -10,7 +10,7 @@ import { IMapRenderResult } from '../types';
export interface IMapExtensionManager { export interface IMapExtensionManager {
/** 勇士状态至勇士渲染器的映射 */ /** 勇士状态至勇士渲染器的映射 */
readonly heroMap: Map<IHeroMover, IMapHeroRenderer>; readonly heroMap: Map<IHeroMoveController, IMapHeroRenderer>;
/** 地图图层到门渲染器的映射 */ /** 地图图层到门渲染器的映射 */
readonly doorMap: Map<IMapLayer, IMapDoorRenderer>; readonly doorMap: Map<IMapLayer, IMapDoorRenderer>;
/** 单例的文字渲染拓展(独立图层) */ /** 单例的文字渲染拓展(独立图层) */
@ -21,13 +21,16 @@ export interface IMapExtensionManager {
* @param state * @param state
* @param layer * @param layer
*/ */
addHero(state: IHeroMover, layer: IMapLayer): IMapHeroRenderer | null; addHero(
state: IHeroMoveController,
layer: IMapLayer
): IMapHeroRenderer | null;
/** /**
* *
* @param state * @param state
*/ */
removeHero(state: IHeroMover): void; removeHero(state: IHeroMoveController): void;
/** /**
* *

View File

@ -87,6 +87,24 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
this.finalAttribute[name] = value; this.finalAttribute[name] = value;
} }
*catchCalculateProgress<K extends keyof THero>(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<THero[K]>[]) {
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]>, THero[K]];
}
}
getBaseAttribute<K extends keyof THero>(name: K): THero[K] { getBaseAttribute<K extends keyof THero>(name: K): THero[K] {
return this.attribute[name]; return this.attribute[name];
} }

View File

@ -1,14 +1,18 @@
import { Hookable, HookController, IHookController } from '@motajs/common'; import { Hookable, HookController, IHookController } from '@motajs/common';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { getFaceMovement, nextFaceDirection } from '@user/data-common'; 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'; import { FaceDirection } from '@user/data-common';
const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png'; const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png';
export class HeroMover export class HeroMoveController
extends Hookable<IHeroMovingHooks> extends Hookable<IHeroMoveControllerHooks>
implements IHeroMover implements IHeroMoveController
{ {
x: number = 0; x: number = 0;
y: number = 0; y: number = 0;
@ -22,8 +26,8 @@ export class HeroMover
readonly followers: IHeroFollower[] = []; readonly followers: IHeroFollower[] = [];
protected createController( protected createController(
hook: Partial<IHeroMovingHooks> hook: Partial<IHeroMoveControllerHooks>
): IHookController<IHeroMovingHooks> { ): IHookController<IHeroMoveControllerHooks> {
return new HookController(this, hook); return new HookController(this, hook);
} }

View File

@ -2,7 +2,7 @@ import { HeroAttribute } from './attribute';
import { import {
IHeroAttribute, IHeroAttribute,
IHeroModifier, IHeroModifier,
IHeroMover, IHeroMoveController,
IHeroState, IHeroState,
IHeroStateSave, IHeroStateSave,
IModifierStateSave, IModifierStateSave,
@ -16,11 +16,11 @@ export class HeroState<THero> implements IHeroState<THero> {
private readonly registry: Map<string, () => IHeroModifier> = new Map(); private readonly registry: Map<string, () => IHeroModifier> = new Map();
constructor( constructor(
public mover: IHeroMover, public mover: IHeroMoveController,
public attribute: IHeroAttribute<THero> public attribute: IHeroAttribute<THero>
) {} ) {}
attachMover(mover: IHeroMover): void { attachMover(mover: IHeroMoveController): void {
this.mover = mover; this.mover = mover;
} }
@ -28,7 +28,7 @@ export class HeroState<THero> implements IHeroState<THero> {
this.attribute = attribute; this.attribute = attribute;
} }
getHeroMover(): IHeroMover { getHeroMover(): IHeroMoveController {
return this.mover; return this.mover;
} }

View File

@ -4,10 +4,10 @@ import { FaceDirection, ISaveableContent } from '@user/data-common';
//#region 勇士属性 //#region 勇士属性
export interface IHeroModifier< export interface IHeroModifier<
T = unknown, K = unknown,
V = unknown, V = unknown,
S = unknown Save = unknown
> extends ISaveableContent<S> { > extends ISaveableContent<Save> {
/** 修饰器类型 */ /** 修饰器类型 */
readonly type: string; readonly type: string;
/** 修饰器优先级 */ /** 修饰器优先级 */
@ -40,12 +40,12 @@ export interface IHeroModifier<
* @param baseValue * @param baseValue
* @param name * @param name
*/ */
modify(value: T, baseValue: T, name: PropertyKey): T; modify(value: K, baseValue: K, name: PropertyKey): K;
/** /**
* *
*/ */
clone(): IHeroModifier<T, V>; clone(): IHeroModifier<K, V>;
} }
export interface IModifierStateSave { export interface IModifierStateSave {
@ -129,7 +129,7 @@ export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
*/ */
addModifier<K extends keyof THero>( addModifier<K extends keyof THero>(
name: K, name: K,
modifier: IHeroModifier<THero[K], unknown> modifier: IHeroModifier<THero[K]>
): void; ): void;
/** /**
@ -139,7 +139,7 @@ export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
*/ */
deleteModifier<K extends keyof THero>( deleteModifier<K extends keyof THero>(
name: K, name: K,
modifier: IHeroModifier<THero[K], unknown> modifier: IHeroModifier<THero[K]>
): void; ): void;
/** /**
@ -147,6 +147,15 @@ export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
* @param cloneModifier * @param cloneModifier
*/ */
clone(cloneModifier?: boolean): IHeroAttribute<THero>; clone(cloneModifier?: boolean): IHeroAttribute<THero>;
/**
*
*
* @param name
*/
catchCalculateProgress<K extends keyof THero>(
name: K
): Iterable<[IHeroModifier<THero[K]>, THero[K]]>;
} }
//#endregion //#endregion
@ -169,7 +178,7 @@ export interface IHeroFollower {
alpha: number; alpha: number;
} }
export interface IHeroMovingHooks extends IHookBase { export interface IHeroMoveControllerHooks extends IHookBase {
/** /**
* *
* @param x * @param x
@ -253,7 +262,7 @@ export interface IHeroMovingHooks extends IHookBase {
onSetFollowerAlpha(identifier: string, alpha: number): void; onSetFollowerAlpha(identifier: string, alpha: number): void;
} }
export interface IHeroMover extends IHookable<IHeroMovingHooks> { export interface IHeroMoveController extends IHookable<IHeroMoveControllerHooks> {
/** 勇士横坐标 */ /** 勇士横坐标 */
readonly x: number; readonly x: number;
/** 勇士纵坐标 */ /** 勇士纵坐标 */
@ -372,7 +381,7 @@ export interface IHeroState<THero> extends ISaveableContent<
IHeroStateSave<THero> IHeroStateSave<THero>
> { > {
/** 勇士移动对象 */ /** 勇士移动对象 */
readonly mover: IHeroMover; readonly mover: IHeroMoveController;
/** 勇士属性对象 */ /** 勇士属性对象 */
readonly attribute: IReadonlyHeroAttribute<THero>; readonly attribute: IReadonlyHeroAttribute<THero>;
@ -380,12 +389,12 @@ export interface IHeroState<THero> extends ISaveableContent<
* *
* @param mover * @param mover
*/ */
attachMover(mover: IHeroMover): void; attachMover(mover: IHeroMoveController): void;
/** /**
* *
*/ */
getHeroMover(): IHeroMover; getHeroMover(): IHeroMoveController;
/** /**
* *

View File

@ -17,7 +17,7 @@ import {
} from '@user/data-common'; } from '@user/data-common';
import { import {
EnemyManager, EnemyManager,
HeroMover, HeroMoveController,
IEnemyManager, IEnemyManager,
HeroAttribute, HeroAttribute,
HeroState, HeroState,
@ -69,23 +69,23 @@ import { logger } from '@motajs/common';
import { ISaveSystem, SaveSystem } from './save'; import { ISaveSystem, SaveSystem } from './save';
export class CoreState implements ICoreState { export class CoreState implements ICoreState {
// Layer 0 公共层 // Layer 0 公共层,最底层的接口,不会依赖任何其他内容,一般是工具性接口及不需要存档的数据
readonly roleFace: IRoleFaceBinder; readonly roleFace: IRoleFaceBinder;
readonly faceManager: IFaceManager; readonly faceManager: IFaceManager;
readonly tileStore: ITileStore<LegacyTileData>; readonly tileStore: ITileStore<LegacyTileData>;
// Layer 1 数据层,所有可存档内容都在这 // Layer 1 数据层,所有可存档内容都在这,一般用于数据存储
readonly maps: IMapStore; readonly maps: IMapStore;
readonly hero: IHeroState<IHeroAttr>; readonly hero: IHeroState<IHeroAttr>;
readonly enemyManager: IEnemyManager<IEnemyAttr>; readonly enemyManager: IEnemyManager<IEnemyAttr>;
readonly flags: IFlagSystem; readonly flags: IFlagSystem;
// Layer 2 执行层,游戏逻辑对象都在这 // Layer 2 执行层,游戏逻辑对象都在这,包括一些需要操作数据层的逻辑系统等
readonly enemyContext: IEnemyContext<IEnemyAttr, IHeroAttr>; readonly enemyContext: IEnemyContext<IEnemyAttr, IHeroAttr>;
readonly triggerRegistry: ITriggerRegistry; readonly triggerRegistry: ITriggerRegistry;
readonly triggerCollector: ITriggerCollector; readonly triggerCollector: ITriggerCollector;
// 用户层内容,也就是最顶层的内容,一般仅用于初始化 // Layer 3 用户层,也就是最顶层的内容,一般仅用于初始化以及仅供渲染端调用的顶层模块
readonly loadProgress: ILoadProgressTotal; readonly loadProgress: ILoadProgressTotal;
readonly dataLoader: IMotaDataLoader; readonly dataLoader: IMotaDataLoader;
readonly saveSystem: ISaveSystem; readonly saveSystem: ISaveSystem;
@ -129,7 +129,7 @@ export class CoreState implements ICoreState {
this.maps = new MapStore(tileStore, this); this.maps = new MapStore(tileStore, this);
// 勇士 // 勇士
const heroMover = new HeroMover(); const heroMover = new HeroMoveController();
const heroAttribute = new HeroAttribute(HERO_DEFAULT_ATTRIBUTE); const heroAttribute = new HeroAttribute(HERO_DEFAULT_ATTRIBUTE);
const heroState = new HeroState(heroMover, heroAttribute); const heroState = new HeroState(heroMover, heroAttribute);
this.hero = heroState; this.hero = heroState;
@ -179,7 +179,7 @@ export class CoreState implements ICoreState {
//#endregion //#endregion
//#region 顶层初始化 //#region L3 初始化
// 存档系统 // 存档系统
this.saveSystem = new SaveSystem(); this.saveSystem = new SaveSystem();
@ -209,8 +209,12 @@ export class CoreState implements ICoreState {
core.floors as Record<FloorIds, ResolvedFloor> core.floors as Record<FloorIds, ResolvedFloor>
); );
}); });
//#endregion
} }
//#region 私有方法
/** /**
* *
* @param data * @param data
@ -355,6 +359,10 @@ export class CoreState implements ICoreState {
this.maps.compareWith(reference); this.maps.compareWith(reference);
} }
//#endregion
//#region 存档方法
addSaveableContent(id: string, content: ISaveableContent<unknown>): void { addSaveableContent(id: string, content: ISaveableContent<unknown>): void {
if (this.saveables.has(id)) { if (this.saveables.has(id)) {
logger.warn(112, id); logger.warn(112, id);
@ -385,4 +393,6 @@ export class CoreState implements ICoreState {
this.executors.set(content, executor); this.executors.set(content, executor);
} }
} }
//#endregion
} }