From eb34d63e96955ad35661ddbfec4b1ff06d76f2c6 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Sun, 28 Jun 2026 19:32:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BE=E5=9D=97=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data-base/src/map/dynamicTile.ts | 13 ++- packages-user/data-base/src/map/mapLayer.ts | 32 +++++-- packages-user/data-base/src/map/types.ts | 38 +++++++-- packages-user/data-common/src/store/types.ts | 26 ++++++ packages-user/data-state/src/core.ts | 4 +- packages-user/data-state/src/legacy/tile.ts | 83 +++++++++++++++++-- packages/common/src/logger.json | 3 +- src/types/declaration/core.d.ts | 1 + 8 files changed, 176 insertions(+), 24 deletions(-) diff --git a/packages-user/data-base/src/map/dynamicTile.ts b/packages-user/data-base/src/map/dynamicTile.ts index 3169edf..b2f68c2 100644 --- a/packages-user/data-base/src/map/dynamicTile.ts +++ b/packages-user/data-base/src/map/dynamicTile.ts @@ -4,14 +4,18 @@ import { IDataCommon, IMoverController, IObjectMover, - IRoleFaceBinder + IRoleFaceBinder, + ITileRawData } from '@user/data-common'; import { IDynamicLayer, IDynamicTile } from './types'; import { DynamicTileMover } from './mover'; +import { logger } from '@motajs/common'; export class DynamicTile implements IDynamicTile { readonly state: IDataCommon; readonly mover: IObjectMover; + + raw: ITileRawData | null; triggerType: number; /** 当前的朝向绑定对象 */ @@ -26,6 +30,13 @@ export class DynamicTile implements IDynamicTile { this.state = layer.state; this.mover = new DynamicTileMover(this); this.triggerType = -1; + const data = this.state.tileStore.getData(num); + if (!data) { + logger.warn(143, num.toString()); + this.raw = null; + } else { + this.raw = data; + } } setFaceBinder(binder: IRoleFaceBinder | null): void { diff --git a/packages-user/data-base/src/map/mapLayer.ts b/packages-user/data-base/src/map/mapLayer.ts index 373dac2..eee5ceb 100644 --- a/packages-user/data-base/src/map/mapLayer.ts +++ b/packages-user/data-base/src/map/mapLayer.ts @@ -1,6 +1,7 @@ import { isNil } from 'lodash-es'; import { IDynamicLayer, + ILayerLocation, ILayerState, IMapLayer, IMapLayerData, @@ -55,6 +56,10 @@ export class MapLayer this.dynamicLayer = new DynamicLayer(this); } + inMap(x: number, y: number): boolean { + return x >= 0 && y >= 0 && x < this.width && y < this.height; + } + /** * 在地图尺寸变化后重新映射手动触发器覆盖表 */ @@ -150,15 +155,32 @@ export class MapLayer } getBlock(x: number, y: number): number { - if (x < 0 || y < 0 || x >= this.width || y >= this.height) { - // 不在地图内,返回 -1 + if (!this.inMap(x, y)) { return -1; } return this.mapArray[y * this.width + x]; } + getLocationData(x: number, y: number): ILayerLocation | null { + if (!this.inMap(x, y)) return null; + const index = y * this.width + x; + const num = this.mapArray[index]; + const raw = this.state.tileStore.getData(num); + const dynamics = this.dynamicLayer.getDynamicTilesAt(x, y); + const trigger = this.triggerMap.get(index) ?? -1; + + const data: ILayerLocation = { + tile: num, + raw, + trigger, + dynamics + }; + + return data; + } + getTriggerType(x: number, y: number): number { - if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + if (!this.inMap(x, y)) { return -1; } const index = y * this.width + x; @@ -169,7 +191,7 @@ export class MapLayer } setTriggerType(type: number, x: number, y: number): void { - if (x < 0 || y < 0 || x >= this.width || y >= this.height) { + if (!this.inMap(x, y)) { return; } const index = y * this.width + x; @@ -181,7 +203,7 @@ export class MapLayer } revertTrigger(x: number, y: number): void { - if (x >= 0 && y >= 0 && x < this.width && y < this.height) { + if (this.inMap(x, y)) { this.triggerMap.delete(y * this.width + x); } } diff --git a/packages-user/data-base/src/map/types.ts b/packages-user/data-base/src/map/types.ts index f6ca49f..f48345a 100644 --- a/packages-user/data-base/src/map/types.ts +++ b/packages-user/data-base/src/map/types.ts @@ -6,7 +6,8 @@ import { IObjectMovable, IObjectMover, IRoleFaceBinder, - ISaveableContent + ISaveableContent, + ITileRawData } from '@user/data-common'; import { ITileStore } from '@user/data-common'; @@ -15,10 +16,21 @@ import { ITileStore } from '@user/data-common'; export interface IMapLayerData { /** 当前引用是否过期,当地图图层内部的地图数组引用更新时,此项会变为 `true` */ expired: boolean; - /** 地图图块数组,是对内部存储的直接引用 */ + /** 地图图块数组,是对内部存储的直接引用,仅建议读取,不建议修改 */ array: Uint32Array; } +export interface ILayerLocation { + /** 该点的静态图块数字 */ + readonly tile: number; + /** 该点的静态图块信息 */ + readonly raw: ITileRawData | null; + /** 该点的静态触发器,-1 表示未设置(即使用图块本身的触发器),否则表示该点的静态触发器,覆盖图块本身的触发器 */ + readonly trigger: number; + /** 该点包含的所有动态图块 */ + readonly dynamics: Iterable; +} + export interface IMapLayerHooks extends IHookBase { /** * 当地图大小发生变化时执行,如果调用了地图的 `resize` 方法,但是地图大小没变,则不会触发 @@ -91,6 +103,13 @@ export interface IMapLayer /** 此图层对应的动态图块图层,z 层级与静态图块一致 */ readonly dynamicLayer: IDynamicLayer; + /** + * 判断指定坐标是否在地图内 + * @param x 横坐标 + * @param y 纵坐标 + */ + inMap(x: number, y: number): boolean; + /** * 设置某一点的图块 * @param block 图块数字 @@ -107,6 +126,13 @@ export interface IMapLayer */ getBlock(x: number, y: number): number; + /** + * 获取指定点的所有图块信息 + * @param x 横坐标 + * @param y 纵坐标 + */ + getLocationData(x: number, y: number): ILayerLocation | null; + /** * 获取指定点的静态图块对应的有效触发器类型,若手动覆盖不存在则回退到图块默认触发器 * @param x 图块横坐标 @@ -512,21 +538,21 @@ export interface IDynamicLayerHooks extends IHookBase { * @param tile 被创建的动态图块 * @param layer 所属的动态图层 */ - onCreateTile(tile: IDynamicTile, layer: IDynamicLayer): void; + onCreateTile?(tile: IDynamicTile, layer: IDynamicLayer): void; /** * 当图块被删除时触发 * @param tile 被删除的动态图块 * @param layer 所属的动态图层 */ - onDeleteTile(tile: IDynamicTile, layer: IDynamicLayer): Promise; + onDeleteTile?(tile: IDynamicTile, layer: IDynamicLayer): Promise; /** * 当更新动态图块的位置时触发(包括使用 `mover` 触发的移动) * @param tile 更新位置的图块 * @param layer 所属的动态图层 */ - onUpdateTilePosition(tile: IDynamicTile, layer: IDynamicLayer): void; + onUpdateTilePosition?(tile: IDynamicTile, layer: IDynamicLayer): void; } export interface IDynamicLayer @@ -613,6 +639,8 @@ export interface IDynamicTile extends IObjectMovable, IDataCommonExtended { readonly layer: IDynamicLayer; /** 当前动态图块的移动器 */ readonly mover: IObjectMover; + /** 当前动态图块的图块数据 */ + readonly raw: ITileRawData | null; /** * 设置图块朝向,会一并修改 {@link num},返回设置后的当前图块数字 diff --git a/packages-user/data-common/src/store/types.ts b/packages-user/data-common/src/store/types.ts index 4528233..3bb1e79 100644 --- a/packages-user/data-common/src/store/types.ts +++ b/packages-user/data-common/src/store/types.ts @@ -1,3 +1,5 @@ +//#region tile + export const enum TileType { /** 未知或尚未归类的图块 */ Unknown, @@ -19,6 +21,26 @@ export const enum TileType { Tileset } +export const enum PassBit { + /** 上方向掩码 */ + Up = 0b0001, + /** 右方向掩码 */ + Right = 0b0010, + /** 下方向掩码 */ + Down = 0b0100, + /** 左方向掩码 */ + Left = 0b1000 +} + +export interface ITilePassData { + /** 是否仅当图块处在事件层时生效 */ + readonly onlyEvents: boolean; + /** 可以离开的方向 */ + readonly outPass: number; + /** 可以进入的方向 */ + readonly inPass: number; +} + export interface ITileRawData { /** 图块数字 */ readonly num: number; @@ -28,6 +50,8 @@ export interface ITileRawData { readonly trigger: number; /** 图块逻辑类型 */ readonly type: TileType; + /** 图块的通行性对象 */ + readonly pass: ITilePassData; } export interface ITileLegacyConverter { @@ -89,3 +113,5 @@ export interface ITileStore { */ fromLegacy(num: number, legacy: TLegacy): ITileRawData; } + +//#endregion diff --git a/packages-user/data-state/src/core.ts b/packages-user/data-state/src/core.ts index 538eb70..1706387 100644 --- a/packages-user/data-state/src/core.ts +++ b/packages-user/data-state/src/core.ts @@ -17,7 +17,6 @@ import { } from '@user/data-common'; import { EnemyManager, - HeroMoveController, IEnemyManager, HeroAttribute, HeroState, @@ -129,9 +128,8 @@ export class CoreState implements ICoreState { this.maps = new MapStore(tileStore, this); // 勇士 - const heroMover = new HeroMoveController(); const heroAttribute = new HeroAttribute(HERO_DEFAULT_ATTRIBUTE); - const heroState = new HeroState(heroMover, heroAttribute); + const heroState = new HeroState(this, dir8, heroAttribute); this.hero = heroState; this.loadProgress = new LoadProgressTotal(); diff --git a/packages-user/data-state/src/legacy/tile.ts b/packages-user/data-state/src/legacy/tile.ts index ca50d42..083294b 100644 --- a/packages-user/data-state/src/legacy/tile.ts +++ b/packages-user/data-state/src/legacy/tile.ts @@ -1,21 +1,14 @@ import { ITileLegacyConverter, + ITilePassData, ITileRawData, + PassBit, TileType } from '@user/data-common'; export type LegacyTileData = MapDataOf; export class TileLegacyBridge implements ITileLegacyConverter { - fromLegacy(num: number, legacy: LegacyTileData): ITileRawData { - return { - num, - id: legacy.id, - trigger: -1, - type: this.getTileType(num, legacy) - }; - } - private getTileType(num: number, legacy: LegacyTileData): TileType { if (num === 0) return TileType.None; switch (legacy.cls) { @@ -40,4 +33,76 @@ export class TileLegacyBridge implements ITileLegacyConverter { return TileType.Unknown; } } + + private getPass(num: number, legacy: LegacyTileData): ITilePassData { + if (num === 0) { + return { + onlyEvents: true, + inPass: 0b1111, + outPass: 0b1111 + }; + } else if (num === 17) { + return { + onlyEvents: false, + inPass: 0b0000, + outPass: 0b0000 + }; + } else { + if (legacy.noPass) { + return { + onlyEvents: true, + inPass: 0b0000, + outPass: 0b1111 + }; + } else if (legacy.cannotIn && legacy.cannotOut) { + let inPass = 0b1111; + let outPass = 0b1111; + if (legacy.cannotIn.includes('up')) { + inPass &= ~PassBit.Up; + } + if (legacy.cannotIn.includes('right')) { + inPass &= ~PassBit.Right; + } + if (legacy.cannotIn.includes('down')) { + inPass &= ~PassBit.Down; + } + if (legacy.cannotIn.includes('left')) { + inPass &= ~PassBit.Left; + } + if (legacy.cannotOut.includes('up')) { + outPass &= ~PassBit.Up; + } + if (legacy.cannotOut.includes('right')) { + outPass &= ~PassBit.Right; + } + if (legacy.cannotOut.includes('down')) { + outPass &= ~PassBit.Down; + } + if (legacy.cannotOut.includes('left')) { + outPass &= ~PassBit.Left; + } + return { + onlyEvents: false, + inPass, + outPass + }; + } else { + return { + onlyEvents: true, + inPass: 0b1111, + outPass: 0b1111 + }; + } + } + } + + fromLegacy(num: number, legacy: LegacyTileData): ITileRawData { + return { + num, + id: legacy.id, + trigger: -1, + type: this.getTileType(num, legacy), + pass: this.getPass(num, legacy) + }; + } } diff --git a/packages/common/src/logger.json b/packages/common/src/logger.json index 2e013e4..a73ea71 100644 --- a/packages/common/src/logger.json +++ b/packages/common/src/logger.json @@ -199,6 +199,7 @@ "139": "Expected $1 binding before start battle flow, but got a null object.", "140": "Different priority of combat script is expected, but got a same one.", "141": "Some issue may occur in binded damage system on combat flow, please check the console.", - "142": "Expected a specific tile number after convert id '$1' to number, but got null." + "142": "Expected a specific tile number after convert id '$1' to number, but got null.", + "143": "Cannot create dynamic tile for $1 since its raw data is not found." } } diff --git a/src/types/declaration/core.d.ts b/src/types/declaration/core.d.ts index cf223b0..ca59d15 100644 --- a/src/types/declaration/core.d.ts +++ b/src/types/declaration/core.d.ts @@ -1282,6 +1282,7 @@ interface MapDataOf { */ cls: ClsOf; + noPass?: boolean; bigImage?: ImageIds; faceIds?: Record; animate?: number;