refactor: manager 的复用方案 & fix: 类型错误

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
unanmed 2026-04-24 23:12:18 +08:00
parent 2ce50bf23e
commit 476f735adc
17 changed files with 76 additions and 32 deletions

View File

@ -1,6 +1,6 @@
import { BaseProps, TagDefine } from '@motajs/render-vue'; import { BaseProps, TagDefine } from '@motajs/render-vue';
import { ERenderItemEvent, SizedCanvasImageSource } from '@motajs/render'; import { ERenderItemEvent, SizedCanvasImageSource } from '@motajs/render';
import { ILayerState } from '@user/data-state'; import { ILayerState } from '@user/data-base';
import { IMapExtensionManager, IMapRenderer } from '../map'; import { IMapExtensionManager, IMapRenderer } from '../map';
export interface IconProps extends BaseProps { export interface IconProps extends BaseProps {

View File

@ -1,5 +1,5 @@
import { MotaOffscreenCanvas2D, RenderItem } from '@motajs/render'; import { MotaOffscreenCanvas2D, RenderItem } from '@motajs/render';
import { ILayerState } from '@user/data-state'; import { ILayerState } from '@user/data-base';
import { IMapRenderer } from './types'; import { IMapRenderer } from './types';
import { ElementNamespace, ComponentInternalInstance } from 'vue'; import { ElementNamespace, ComponentInternalInstance } from 'vue';
import { CELL_HEIGHT, CELL_WIDTH, MAP_HEIGHT, MAP_WIDTH } from '../../shared'; import { CELL_HEIGHT, CELL_WIDTH, MAP_HEIGHT, MAP_WIDTH } from '../../shared';

View File

@ -2,7 +2,7 @@ import {
IMapLayer, IMapLayer,
IMapLayerHookController, IMapLayerHookController,
IMapLayerHooks IMapLayerHooks
} from '@user/data-state'; } from '@user/data-base';
import { IMapDoorRenderer } from './types'; import { IMapDoorRenderer } from './types';
import { IMapRenderer } from '../types'; import { IMapRenderer } from '../types';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';

View File

@ -7,7 +7,7 @@ import {
IHeroMovingHooks, IHeroMovingHooks,
nextFaceDirection nextFaceDirection
} from '@user/data-base'; } from '@user/data-base';
import { IMapLayer, state } from '@user/data-state'; import { IMapLayer } from '@user/data-base';
import { IMapRenderer, IMapRendererTicker, IMovingBlock } from '../types'; import { IMapRenderer, IMapRendererTicker, IMovingBlock } from '../types';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { IHookController, logger } from '@motajs/common'; import { IHookController, logger } from '@motajs/common';
@ -15,6 +15,7 @@ import { BlockCls, IMaterialFramedData } from '@user/client-base';
import { ITexture, ITextureSplitter, TextureRowSplitter } from '@motajs/render'; import { ITexture, ITextureSplitter, TextureRowSplitter } from '@motajs/render';
import { IMapHeroRenderer } from './types'; import { IMapHeroRenderer } from './types';
import { TimingFn } from 'mutate-animate'; import { TimingFn } from 'mutate-animate';
import { state } from '@user/data-state';
/** 默认的移动时长 */ /** 默认的移动时长 */
const DEFAULT_TIME = 100; const DEFAULT_TIME = 100;

View File

@ -1,5 +1,5 @@
import { IHeroMover } from '@user/data-base'; import { IHeroMover } from '@user/data-base';
import { IMapLayer } from '@user/data-state'; import { IMapLayer } from '@user/data-base';
import { import {
IMapDoorRenderer, IMapDoorRenderer,
IMapExtensionManager, IMapExtensionManager,

View File

@ -4,7 +4,7 @@ import {
HeroAnimateDirection, HeroAnimateDirection,
IHeroMover IHeroMover
} from '@user/data-base'; } from '@user/data-base';
import { IMapLayer } from '@user/data-state'; import { IMapLayer } from '@user/data-base';
import { IMapRenderResult } from '../types'; import { IMapRenderResult } from '../types';

View File

@ -2,7 +2,7 @@ import { linear, TimingFn } from 'mutate-animate';
import { IMapRenderer, IMapVertexGenerator, IMovingBlock } from './types'; import { IMapRenderer, IMapVertexGenerator, IMovingBlock } from './types';
import { IMaterialFramedData, IMaterialManager } from '@user/client-base'; import { IMaterialFramedData, IMaterialManager } from '@user/client-base';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { IMapLayer } from '@user/data-state'; import { IMapLayer } from '@user/data-base';
import { DynamicBlockStatus } from './status'; import { DynamicBlockStatus } from './status';
export interface IMovingRenderer { export interface IMovingRenderer {

View File

@ -30,7 +30,7 @@ import {
MapTileBehavior, MapTileBehavior,
MapTileSizeTestMode MapTileSizeTestMode
} from './types'; } from './types';
import { ILayerState, ILayerStateHooks, IMapLayer } from '@user/data-state'; import { ILayerState, ILayerStateHooks, IMapLayer } from '@user/data-base';
import { IHookController, logger } from '@motajs/common'; import { IHookController, logger } from '@motajs/common';
import { compileProgramWith } from '@motajs/client-base'; import { compileProgramWith } from '@motajs/client-base';
import { isNil, maxBy } from 'lodash-es'; import { isNil, maxBy } from 'lodash-es';

View File

@ -1,4 +1,4 @@
import { IMapLayer } from '@user/data-state'; import { IMapLayer } from '@user/data-base';
import { IBlockStatus, IMapVertexStatus } from './types'; import { IBlockStatus, IMapVertexStatus } from './types';
export class StaticBlockStatus implements IBlockStatus { export class StaticBlockStatus implements IBlockStatus {

View File

@ -10,7 +10,7 @@ import {
IMaterialManager, IMaterialManager,
ITrackedAssetData ITrackedAssetData
} from '@user/client-base'; } from '@user/client-base';
import { ILayerState, IMapLayer } from '@user/data-state'; import { ILayerState, IMapLayer } from '@user/data-base';
import { TimingFn } from 'mutate-animate'; import { TimingFn } from 'mutate-animate';
export const enum MapBackgroundRepeat { export const enum MapBackgroundRepeat {

View File

@ -1,4 +1,4 @@
import { IMapLayer } from '@user/data-state'; import { IMapLayer } from '@user/data-base';
import { import {
IBlockData, IBlockData,
IBlockSplitter, IBlockSplitter,

View File

@ -24,6 +24,10 @@ export class EnemyManager<TAttr> implements IEnemyManager<TAttr> {
private readonly prefabById: Map<string, IEnemy<TAttr>> = new Map(); private readonly prefabById: Map<string, IEnemy<TAttr>> = new Map();
/** 旧样板怪物 id 到 code 的映射,用于 fromLegacyEnemy 快速查找已有模板 */ /** 旧样板怪物 id 到 code 的映射,用于 fromLegacyEnemy 快速查找已有模板 */
private readonly legacyIdToCode: Map<string, number> = new Map(); private readonly legacyIdToCode: Map<string, number> = new Map();
/** 复用映射reusedCode -> sourceCode */
private readonly reuseByCode: Map<number, number> = new Map();
/** 复用映射reusedId -> sourceId */
private readonly reuseById: Map<string, string> = new Map();
/** 脏模板集合,存储发生了变化的模板 code */ /** 脏模板集合,存储发生了变化的模板 code */
private readonly dirtySet: Set<number> = new Set(); private readonly dirtySet: Set<number> = new Set();
/** 参考快照code -> IReadonlyEnemy由 compareWith 提供 */ /** 参考快照code -> IReadonlyEnemy由 compareWith 提供 */
@ -124,9 +128,11 @@ export class EnemyManager<TAttr> implements IEnemyManager<TAttr> {
private internalGetPrefab(code: number | string) { private internalGetPrefab(code: number | string) {
if (typeof code === 'number') { if (typeof code === 'number') {
return this.prefabByCode.get(code) ?? null; const sourceCode = this.reuseByCode.get(code) ?? code;
return this.prefabByCode.get(sourceCode) ?? null;
} else { } else {
return this.prefabById.get(code) ?? null; const sourceId = this.reuseById.get(code) ?? code;
return this.prefabById.get(sourceId) ?? null;
} }
} }
@ -155,11 +161,13 @@ export class EnemyManager<TAttr> implements IEnemyManager<TAttr> {
} }
getPrefab(code: number): IReadonlyEnemy<TAttr> | null { getPrefab(code: number): IReadonlyEnemy<TAttr> | null {
return this.prefabByCode.get(code) ?? null; const sourceCode = this.reuseByCode.get(code) ?? code;
return this.prefabByCode.get(sourceCode) ?? null;
} }
getPrefabById(id: string): IReadonlyEnemy<TAttr> | null { getPrefabById(id: string): IReadonlyEnemy<TAttr> | null {
return this.prefabById.get(id) ?? null; const sourceId = this.reuseById.get(id) ?? id;
return this.prefabById.get(sourceId) ?? null;
} }
deletePrefab(code: number | string): void { deletePrefab(code: number | string): void {
@ -181,8 +189,8 @@ export class EnemyManager<TAttr> implements IEnemyManager<TAttr> {
reusePrefab(source: number | string, code: number, id: string): void { reusePrefab(source: number | string, code: number, id: string): void {
const prefab = this.internalGetPrefab(source); const prefab = this.internalGetPrefab(source);
if (!prefab) return; if (!prefab) return;
this.prefabByCode.set(code, prefab); this.reuseByCode.set(code, prefab.code);
this.prefabById.set(id, prefab); this.reuseById.set(id, prefab.id);
} }
compareWith(reference: ReadonlyMap<number, IReadonlyEnemy<TAttr>>): void { compareWith(reference: ReadonlyMap<number, IReadonlyEnemy<TAttr>>): void {

View File

@ -22,7 +22,8 @@ import {
FaceDirection, FaceDirection,
ISaveableContent, ISaveableContent,
IStateSaveData, IStateSaveData,
SaveCompression SaveCompression,
IReadonlyEnemy
} from '@user/data-base'; } from '@user/data-base';
import { IEnemyAttr } from './enemy'; import { IEnemyAttr } from './enemy';
import { import {
@ -42,6 +43,7 @@ import { isNil } from 'lodash-es';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { ISaveSystem } from './save'; import { ISaveSystem } from './save';
import { SaveSystem } from './save/system'; import { SaveSystem } from './save/system';
import { MainEnemyComparer } from './enemy/comparer';
export class CoreState implements ICoreState { export class CoreState implements ICoreState {
// 全局内容 // 全局内容
@ -92,7 +94,9 @@ export class CoreState implements ICoreState {
//#region 怪物初始化 //#region 怪物初始化
// 怪物管理器初始化 // 怪物管理器初始化
const comparer = new MainEnemyComparer();
const enemyManager = new EnemyManager(new EnemyLegacyBridge()); const enemyManager = new EnemyManager(new EnemyLegacyBridge());
enemyManager.attachEnemyComparer(comparer);
enemyManager.setAttributeDefaults('hp', 0); enemyManager.setAttributeDefaults('hp', 0);
enemyManager.setAttributeDefaults('atk', 0); enemyManager.setAttributeDefaults('atk', 0);
enemyManager.setAttributeDefaults('def', 0); enemyManager.setAttributeDefaults('def', 0);
@ -159,6 +163,7 @@ export class CoreState implements ICoreState {
private initEnemyManager(data: Record<EnemyIds, Enemy>) { private initEnemyManager(data: Record<EnemyIds, Enemy>) {
// TODO: 修改怪物模板并存入存档,即 core.setEnemy // TODO: 修改怪物模板并存入存档,即 core.setEnemy
const manager = this.enemyManager; const manager = this.enemyManager;
const reference = new Map<number, IReadonlyEnemy<IEnemyAttr>>();
for (const [id, enemy] of Object.entries(structuredClone(data))) { for (const [id, enemy] of Object.entries(structuredClone(data))) {
const num = this.idNumberMap.get(id); const num = this.idNumberMap.get(id);
if (isNil(num)) continue; if (isNil(num)) continue;
@ -169,7 +174,9 @@ export class CoreState implements ICoreState {
const upCode = this.idNumberMap.get(up)!; const upCode = this.idNumberMap.get(up)!;
const rightCode = this.idNumberMap.get(right)!; const rightCode = this.idNumberMap.get(right)!;
const downCode = this.idNumberMap.get(down)!; const downCode = this.idNumberMap.get(down)!;
manager.addPrefabFromLegacy(downCode, enemy); const prefab = manager.fromLegacyEnemy(downCode, enemy);
reference.set(downCode, prefab);
manager.addPrefab(prefab);
this.roleFace.malloc(downCode, FaceDirection.Down); this.roleFace.malloc(downCode, FaceDirection.Down);
this.roleFace.bind(leftCode, downCode, FaceDirection.Left); this.roleFace.bind(leftCode, downCode, FaceDirection.Left);
this.roleFace.bind(upCode, downCode, FaceDirection.Up); this.roleFace.bind(upCode, downCode, FaceDirection.Up);
@ -178,9 +185,12 @@ export class CoreState implements ICoreState {
manager.reusePrefab(num, upCode, up); manager.reusePrefab(num, upCode, up);
manager.reusePrefab(num, rightCode, right); manager.reusePrefab(num, rightCode, right);
} else { } else {
manager.addPrefabFromLegacy(num, enemy); const prefab = manager.fromLegacyEnemy(num, enemy);
reference.set(num, prefab);
manager.addPrefab(prefab);
} }
} }
manager.compareWith(reference);
} }
addSaveableContent(id: string, content: ISaveableContent<unknown>): void { addSaveableContent(id: string, content: ISaveableContent<unknown>): void {

View File

@ -0,0 +1,31 @@
import { IEnemyComparer, IReadonlyEnemy } from '@user/data-base';
import { IEnemyAttr } from './types';
export class MainEnemyComparer implements IEnemyComparer<IEnemyAttr> {
compare(
enemyA: IReadonlyEnemy<IEnemyAttr>,
enemyB: IReadonlyEnemy<IEnemyAttr>
): boolean {
// 比较基本属性
if (
enemyA.getAttribute('hp') !== enemyB.getAttribute('hp') ||
enemyA.getAttribute('atk') !== enemyB.getAttribute('atk') ||
enemyA.getAttribute('def') !== enemyB.getAttribute('def') ||
enemyA.getAttribute('money') !== enemyB.getAttribute('money') ||
enemyA.getAttribute('exp') !== enemyB.getAttribute('exp') ||
enemyA.getAttribute('point') !== enemyB.getAttribute('point')
) {
return false;
}
// 比较特殊属性
const specialsA = [...enemyA.iterateSpecials()];
const specialsB = [...enemyB.iterateSpecials()];
if (specialsA.length !== specialsB.length) return false;
for (const special of specialsA) {
const other = enemyB.getSpecial(special.code);
if (!other || !special.deepEqualsTo(other)) return false;
}
return true;
}
}

View File

@ -1,10 +1,9 @@
import EventEmitter from 'eventemitter3'; import EventEmitter from 'eventemitter3';
import { backDir, toDir } from './utils'; import { backDir, toDir } from './utils';
import { loading } from '@user/data-base'; import { fromDirectionString, loading } from '@user/data-base';
import type { RenderAdapter } from '@motajs/render';
import type { HeroKeyMover } from '@user/client-modules'; import type { HeroKeyMover } from '@user/client-modules';
import { sleep } from '@motajs/common'; import { sleep } from '@motajs/common';
import { fromDirectionString, state } from '..'; import { state } from '..';
// todo: 转身功能 // todo: 转身功能

View File

@ -1,11 +1,6 @@
import type { TimingFn } from 'mutate-animate'; import type { TimingFn } from 'mutate-animate';
import { import { heroMoveCollection, MoveStep, state } from '@user/data-state';
fromDirectionString, import { fromDirectionString, hook, loading } from '@user/data-base';
heroMoveCollection,
MoveStep,
state
} from '@user/data-state';
import { hook, loading } from '@user/data-base';
import { Patch, PatchClass } from '@motajs/legacy-common'; import { Patch, PatchClass } from '@motajs/legacy-common';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
@ -59,8 +54,6 @@ export function initFallback() {
Mota.r(() => { Mota.r(() => {
// ----- 引入 // ----- 引入
const { mainRenderer } = Mota.require('@user/client-modules');
const Animation = Mota.require('MutateAnimate');
const patch = new Patch(PatchClass.Control); const patch = new Patch(PatchClass.Control);
const patch2 = new Patch(PatchClass.Events); const patch2 = new Patch(PatchClass.Events);

View File

@ -29,8 +29,10 @@ export function getDetailedEnemy(
return typeof func === 'string' ? func : func(enemy); return typeof func === 'string' ? func : func(enemy);
}; };
const special: [string, string, string][] = [...enemy.info.special] const special: [string, string, string][] = [...enemy.info.special]
// @ts-expect-error 之后修
.filter(v => !enemy.info.specialHalo?.includes(v)) .filter(v => !enemy.info.specialHalo?.includes(v))
.map(vv => { .map(vv => {
// @ts-expect-error 之后修
const s = Mota.require('@user/data-state').specials[vv]; const s = Mota.require('@user/data-state').specials[vv];
return [ return [
fromFunc(s.name, enemy.info), fromFunc(s.name, enemy.info),