refactor: 地图 MapLayer

This commit is contained in:
AncTe committed 2026-09-29 20:21:50 +08:00
1 parent 122567b050
commit b262918082
15 files changed
+281 -267

No files matched your search

@@ -10,6 +10,10 @@ export class LayerEventView implements ILayerEventView {
/** 当前存储与参考基准不一致的条目数量 */
private dirtyEntries: number = 0;
[Symbol.iterator]() {
return this.store.entries();
}
get(): ReadonlyMap<number, string> {
return this.store;
}
+1 -2
View File
@@ -59,8 +59,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
@shouldReplay('Adding game map layer should be replayed.')
addLayer(alias: string): IMapLayer {
const array = new Uint32Array(this.width * this.height);
const layer = new MapLayer(array, this.width, this.height, this, alias);
const layer = new MapLayer(this.width, this.height, this, alias);
this.layerList.add(layer);
this.forEachHook(hook => {
hook.onUpdateLayer?.(this.layerList);
+190 -200
View File
@@ -5,11 +5,11 @@ import {
IGameMap,
ILayerEventView,
ILayerLocation,
IMapLayer,
IMapLayerData,
IMapLayerHookController,
IMapLayerHooks,
IMapLayerSave,
IResizableMapLayer,
IStaticBlockSave,
IStaticTile
} from './types';
@@ -17,6 +17,7 @@ import { Hookable, HookController, ITileLocator, logger } from '@motajs/common';
import {
FaceDirection,
IDataCommon,
ILocationHelper,
IRoleFaceBinder,
SaveCompression,
shouldReplay
@@ -27,9 +28,10 @@ import { StaticTile } from './staticTile';
export class MapLayer
extends Hookable<IMapLayerHooks, IMapLayerHookController>
implements IMapLayer
implements IResizableMapLayer
{
readonly state: IDataCommon;
readonly indexer: ILocationHelper;
width: number;
height: number;
@@ -59,7 +61,6 @@ export class MapLayer
private readonly pointEvents: Map<number, ILayerEventView> = new Map();
constructor(
array: Uint32Array,
width: number,
height: number,
readonly map: IGameMap,
@@ -72,128 +73,31 @@ export class MapLayer
this.height = height;
const area = width * height;
this.mapArray = new Uint32Array(area);
this.mapArray.set(array);
this.mapData = {
expired: false,
array: this.mapArray
};
this.indexer = map.indexer;
}
/**
* 将动态图块登记到索引表中
* @param tile 动态图块
*/
private addDynamicToPosMap(tile: IDynamicTile): void {
const { x, y } = tile;
const xMap = this.tilePosMap.getOrInsertComputed(y, () => new Map());
const set = xMap.getOrInsertComputed(x, () => new Set());
set.add(tile);
// 需要使用不同引用的 locator,避免与 tile 本身的 locator 同引用导致缓存位置失效
// 缓存位置的目的是能在动态图块位置更新时还持有旧位置,能够执行内部存储的移动
this.cachedDynamics.set(tile, { x, y });
}
/**
* 将动态图块从指定缓存坐标的索引表中移除
* @param tile 动态图块
* @param x 横坐标
* @param y 纵坐标
*/
private removeDynamic(tile: IDynamicTile, x: number, y: number): void {
this.cachedDynamics.delete(tile);
this.tilePosMap.get(y)?.get(x)?.delete(tile);
protected createController(
hook: Partial<IMapLayerHooks>
): IMapLayerHookController {
return new MapLayerHookController(this, hook);
}
//#region 事件操作
/**
* 将动态图块的事件同步回当前静态格点
* @param tile 动态图块
* @param keepEvent 是否保留事件
*/
private syncStaticEvent(tile: IDynamicTile, keepEvent: boolean): void {
const staticTile = this.getTile(tile.x, tile.y);
if (!staticTile) return;
staticTile.set(staticTile.num());
if (keepEvent) {
const staticEvent = staticTile.tileEvent();
const dynamicEvent = tile.tileEvent();
staticEvent.clear();
for (const [priority, id] of dynamicEvent.get()) {
staticEvent.set(priority, id);
}
}
}
/**
* 将所有已创建的点事件视图恢复到原始纯基准
*/
private resetPointEvents(): void {
for (const eventView of this.pointEvents.values()) {
const reference = eventView.ref();
eventView.clear();
for (const [priority, id] of reference) {
eventView.set(priority, id);
}
}
}
/**
* 裁剪超出新图层范围的点事件,并按新宽度重建索引
* @param width 新图层宽度
* @param height 新图层高度
*/
private cropPointEvents(width: number, height: number): void {
const pointEvents = new Map<number, ILayerEventView>();
for (const [index, eventView] of this.pointEvents) {
const x = index % this.width;
const y = Math.floor(index / this.width);
if (x < width && y < height) {
pointEvents.set(y * width + x, eventView);
}
}
this.pointEvents.clear();
for (const [index, eventView] of pointEvents) {
this.pointEvents.set(index, eventView);
}
}
/**
* 收集需要保存的点事件并复制其内部 Map
*/
private savePointEvents(): Map<number, ReadonlyMap<number, string>> {
const pointEvents = new Map<number, ReadonlyMap<number, string>>();
for (const [index, eventView] of this.pointEvents) {
if (!eventView.dirty()) continue;
pointEvents.set(index, new Map(eventView.get()));
}
return pointEvents;
}
/**
* 在图层基准上叠加点事件存档
* @param save 点事件存档,可省略
*/
private loadPointEvents(
save?: ReadonlyMap<number, ReadonlyMap<number, string>>
): void {
this.resetPointEvents();
if (!save) return;
for (const [index, events] of save) {
const x = index % this.width;
const y = Math.floor(index / this.width);
const eventView = this.event(x, y);
if (!eventView) continue;
eventView.clear();
for (const [priority, id] of events) {
eventView.set(priority, id);
}
}
this.pointEvents.forEach(v => v.reset());
}
event(x: number, y: number): ILayerEventView | null {
if (!this.inMap(x, y)) return null;
const index = y * this.width + x;
const index = this.indexer.index(x, y);
let eventView = this.pointEvents.get(index);
if (!eventView) {
eventView = new LayerEventView();
@@ -217,7 +121,7 @@ export class MapLayer
@shouldReplay('Setting map layer block should be replayed.')
setBlock(block: number, x: number, y: number): void {
if (!this.inMap(x, y)) return;
const index = y * this.width + x;
const index = this.indexer.index(x, y);
if (block === this.mapArray[index]) return;
this.mapArray[index] = block;
this.layerDirty = true;
@@ -248,7 +152,7 @@ export class MapLayer
getTile(x: number, y: number): IStaticTile | null {
if (!this.inMap(x, y)) return null;
const index = this.map.indexer.locToIndex(x, y);
const index = this.map.indexer.index(x, y);
let staticTile = this.staticTileCache.get(index);
if (!staticTile) {
staticTile = new StaticTile(x, y, this);
@@ -260,7 +164,7 @@ export class MapLayer
getLocationData(x: number, y: number): ILayerLocation | null {
if (!this.inMap(x, y)) return null;
const staticTile = this.getTile(x, y);
const num = staticTile?.num() ?? -1;
const num = staticTile?.num() ?? 0;
const dynamics = this.getDynamicTilesAt(x, y);
return {
locator: { x, y },
@@ -340,14 +244,18 @@ export class MapLayer
if (x < 0 || y < 0 || r > w || b > h) {
logger.warn(81);
}
const res = new Uint32Array(width * height);
const nl = Math.max(x, 0);
const nt = Math.max(y, 0);
const nr = Math.min(r, w);
const nb = Math.min(b, this.height);
const nw = nr - nl;
const nh = nb - nt;
const res = new Uint32Array(nw * nh);
const arr = this.mapArray;
const nb = Math.min(b, h);
for (let ny = y; ny < nb; ny++) {
const lineStart = ny * w + x;
const lineEnd = lineStart + width;
for (let ny = nt; ny < nb; ny++) {
const lineStart = ny * w + nl;
const dy = ny - y;
res.set(arr.subarray(lineStart, lineEnd), dy * width);
res.set(arr.subarray(lineStart, nr), dy * width);
}
return res;
}
@@ -378,7 +286,7 @@ export class MapLayer
return this.mapData;
}
@shouldReplay('Setting map layer block direction should be replayed.')
@shouldReplay('Setting map layer block direction should be replayed.')
setStaticDirection(x: number, y: number, direction: FaceDirection): number {
const tile = this.getTile(x, y);
if (!tile) return -1;
@@ -400,19 +308,36 @@ export class MapLayer
//#region 动态图层操作
/**
* 将动态图块登记到索引表中
* @param tile 动态图块
*/
private addDynamic(tile: IDynamicTile): void {
const { x, y } = tile;
const xMap = this.tilePosMap.getOrInsertComputed(y, () => new Map());
const set = xMap.getOrInsertComputed(x, () => new Set());
set.add(tile);
// 需要使用不同引用的 locator,避免与 tile 本身的 locator 同引用导致缓存位置失效
// 缓存位置的目的是能在动态图块位置更新时还持有旧位置,能够执行内部存储的移动
this.cachedDynamics.set(tile, { x, y });
}
/**
* 将动态图块从指定缓存坐标的索引表中移除,注意动态图块本身的位置与其所在的索引表可能不同,
* 在动态图块位置更新时,指定位置可以用来移除旧位置的动态图块
* @param tile 动态图块
* @param x 横坐标
* @param y 纵坐标
*/
private removeDynamic(tile: IDynamicTile, x: number, y: number): void {
this.cachedDynamics.delete(tile);
this.tilePosMap.get(y)?.get(x)?.delete(tile);
}
@shouldReplay('Creating dynamic tile should be replayed.')
createDynamic(num: number, x: number, y: number): IDynamicTile {
const tile = new DynamicTile(num, x, y, this);
const location = this.getLocationData(x, y);
if (location?.static) {
const tileEvent = tile.tileEvent();
const staticEvent = location.static.tileEvent();
tileEvent.clear();
for (const [priority, id] of staticEvent.get()) {
tileEvent.set(priority, id);
}
}
this.addDynamicToPosMap(tile);
this.addDynamic(tile);
this.forEachHook(hook => hook.onCreateDynamic?.(tile));
return tile;
}
@@ -433,12 +358,12 @@ export class MapLayer
}
this.setBlock(0, x, y);
const tile = this.createDynamic(num, x, y);
const staticTile = this.getTile(x, y);
if (staticTile) {
staticTile.tileEvent().clear();
}
if (!keepEvent) {
tile.tileEvent().clear();
if (keepEvent) {
const staticTile = this.getTile(x, y);
if (staticTile) {
tile.syncTileEvent(staticTile);
staticTile.tileEvent().clear();
}
}
return tile;
}
@@ -456,12 +381,7 @@ export class MapLayer
const staticTile = this.getTile(tile.x, tile.y);
if (!staticTile) return null;
if (keepEvent) {
const staticEvent = staticTile.tileEvent();
const dynamicEvent = tile.tileEvent();
staticEvent.clear();
for (const [priority, id] of dynamicEvent.get()) {
staticEvent.set(priority, id);
}
staticTile.syncTileEvent(tile);
}
this.removeDynamic(tile, tile.x, tile.y);
this.forEachHook(hook => hook.onDeleteDynamic?.(tile));
@@ -512,11 +432,6 @@ export class MapLayer
return this.cachedDynamics.keys();
}
@shouldReplay('Setting map layer block direction should be replayed.')
setDynamicDirection(tile: IDynamicTile, direction: FaceDirection): number {
return tile.setFaceDirection(direction);
}
@shouldReplay('Updating dynamic tile position should be replayed.')
updateDynamicTile(tile: IDynamicTile): void {
const oldPos = this.cachedDynamics.get(tile);
@@ -524,9 +439,9 @@ export class MapLayer
this.removeDynamic(tile, oldPos.x, oldPos.y);
oldPos.x = tile.x;
oldPos.y = tile.y;
this.addDynamicToPosMap(tile);
this.addDynamic(tile);
} else {
this.addDynamicToPosMap(tile);
this.addDynamic(tile);
this.cachedDynamics.set(tile, { x: tile.x, y: tile.y });
}
this.forEachHook(hook => hook.onUpdateDynamicPosition?.(tile));
@@ -538,7 +453,7 @@ export class MapLayer
@shouldReplay('Opening door should be replayed.')
async openDoor(x: number, y: number): Promise<void> {
const index = y * this.width + x;
const index = this.indexer.index(x, y);
const num = this.mapArray[index];
if (num === 0) return;
await Promise.all(
@@ -551,7 +466,7 @@ export class MapLayer
@shouldReplay('Closing door should be replayed.')
async closeDoor(num: number, x: number, y: number): Promise<void> {
const index = y * this.width + x;
const index = this.indexer.index(x, y);
const nowNum = this.mapArray[index];
if (nowNum !== 0) {
logger.error(46, x.toString(), y.toString());
@@ -608,10 +523,18 @@ export class MapLayer
this.layerDirty = !this.isEqualToRef();
}
protected createController(
hook: Partial<IMapLayerHooks>
): IMapLayerHookController {
return new MapLayerHookController(this, hook);
/**
* 裁剪超出新图层范围的点事件,并按新宽度重建索引
* @param width 新图层宽度
* @param height 新图层高度
*/
private cropPointEvents(width: number, height: number): void {
for (const [index, eventView] of [...this.pointEvents]) {
const { x, y } = this.indexer.locator(index);
if (x < width && y < height) {
this.pointEvents.set(index, eventView);
}
}
}
@shouldReplay('Resizing map layer should be replayed.')
@@ -619,8 +542,9 @@ export class MapLayer
if (this.width === width && this.height === height) {
return;
}
// 图层数组
this.layerDirty = true;
this.cropPointEvents(width, height);
this.mapData.expired = true;
const before = this.mapArray;
const beforeWidth = this.width;
@@ -631,7 +555,6 @@ export class MapLayer
const area = width * height;
const newArray = new Uint32Array(area);
this.mapArray = newArray;
this.staticTileCache.clear();
if (beforeArea > area) {
for (let ny = 0; ny < height; ny++) {
const begin = ny * beforeWidth;
@@ -640,16 +563,18 @@ export class MapLayer
} else {
for (let ny = 0; ny < beforeHeight; ny++) {
const begin = ny * beforeWidth;
newArray.set(
before.subarray(begin, begin + beforeWidth),
ny * width
);
const end = begin + beforeWidth;
newArray.set(before.subarray(begin, end), ny * width);
}
}
this.mapData = {
expired: false,
array: this.mapArray
};
// 其他杂项清理
this.cropPointEvents(width, height);
this.staticTileCache.clear();
this.forEachHook(hook => {
hook.onResize?.(width, height);
});
@@ -658,23 +583,27 @@ export class MapLayer
@shouldReplay('Resizing map layer should be replayed.')
resize2(width: number, height: number): void {
this.layerDirty = true;
this.pointEvents.clear();
if (this.width === width && this.height === height) {
this.empty = true;
this.mapArray.fill(0);
this.staticTileCache.clear();
return;
}
// 图层数组
this.mapData.expired = true;
this.width = width;
this.height = height;
this.mapArray = new Uint32Array(width * height);
this.staticTileCache.clear();
this.mapData = {
expired: false,
array: this.mapArray
};
this.empty = true;
// 其他杂项清理
this.pointEvents.clear();
this.staticTileCache.clear();
this.forEachHook(hook => {
hook.onResize?.(width, height);
});
@@ -686,31 +615,33 @@ export class MapLayer
/**
* 保存静态图块实例
* @param compression 压缩级别
*/
private saveStatics(): Map<number, IStaticBlockSave> {
private saveStaticTiles(
compression: SaveCompression
): Map<number, IStaticBlockSave> {
const blocks = new Map<number, IStaticBlockSave>();
for (const location of this.iterateBlocks()) {
const tile = location.static;
if (!tile || !tile.shouldSave()) continue;
const index = this.map.indexer.locaterToIndex(location.locator);
blocks.set(index, tile.saveState(SaveCompression.NoCompression));
const index = this.map.indexer.locatorToIndex(location.locator);
blocks.set(index, tile.saveState(compression));
}
return blocks;
}
/**
* 保存动态图块实例
* @param compression 压缩级别
*/
private saveDynamics(): Map<number, IDynamicBlockSave[]> {
private saveDynamicTiles(
compression: SaveCompression
): Map<number, IDynamicBlockSave[]> {
const blocks = new Map<number, IDynamicBlockSave[]>();
for (const tile of this.iterateDynamicTiles()) {
const index = this.map.indexer.locaterToIndex(tile.locator);
let list = blocks.get(index);
if (!list) {
list = [];
blocks.set(index, list);
}
list.push(tile.saveState(SaveCompression.NoCompression));
const index = this.map.indexer.locatorToIndex(tile.locator);
const list = blocks.getOrInsert(index, []);
list.push(tile.saveState(compression));
}
return blocks;
}
@@ -742,8 +673,8 @@ export class MapLayer
width: this.width,
height: this.height,
fullMap: new Uint32Array(this.mapArray),
staticBlocks: this.saveStatics(),
dynamicBlocks: this.saveDynamics(),
staticBlocks: this.saveStaticTiles(SaveCompression.NoCompression),
dynamicBlocks: this.saveDynamicTiles(SaveCompression.NoCompression),
pointEvents: this.savePointEvents()
};
}
@@ -752,23 +683,23 @@ export class MapLayer
* 以低压缩方式序列化当前图层
*/
private saveLowCompression(): IMapLayerSave {
const staticBlocks = this.saveStatics();
const dynamicBlocks = this.saveDynamics();
const statics = this.saveStaticTiles(SaveCompression.LowCompression);
const dynamics = this.saveDynamicTiles(SaveCompression.LowCompression);
if (this.layerDirty && (!this.refArray || !this.isEqualToRef())) {
return {
width: this.width,
height: this.height,
fullMap: new Uint32Array(this.mapArray),
staticBlocks,
dynamicBlocks,
staticBlocks: statics,
dynamicBlocks: dynamics,
pointEvents: this.savePointEvents()
};
} else {
return {
width: this.width,
height: this.height,
staticBlocks,
dynamicBlocks,
staticBlocks: statics,
dynamicBlocks: dynamics,
pointEvents: this.savePointEvents()
};
}
@@ -778,16 +709,16 @@ export class MapLayer
* 以高压缩方式序列化当前图层
*/
private saveHighCompression(): IMapLayerSave {
const staticBlocks = this.saveStatics();
const dynamicBlocks = this.saveDynamics();
const statics = this.saveStaticTiles(SaveCompression.HighCompression);
const dynamics = this.saveDynamicTiles(SaveCompression.HighCompression);
if (this.layerDirty) {
if (this.refArray) {
return {
width: this.width,
height: this.height,
rows: this.diffRows(this.refArray),
staticBlocks,
dynamicBlocks,
staticBlocks: statics,
dynamicBlocks: dynamics,
pointEvents: this.savePointEvents()
};
} else {
@@ -795,8 +726,8 @@ export class MapLayer
width: this.width,
height: this.height,
fullMap: new Uint32Array(this.mapArray),
staticBlocks,
dynamicBlocks,
staticBlocks: statics,
dynamicBlocks: dynamics,
pointEvents: this.savePointEvents()
};
}
@@ -804,8 +735,8 @@ export class MapLayer
return {
width: this.width,
height: this.height,
staticBlocks,
dynamicBlocks,
staticBlocks: statics,
dynamicBlocks: dynamics,
pointEvents: this.savePointEvents()
};
}
@@ -824,26 +755,34 @@ export class MapLayer
/**
* 读取静态图块实例存档数据
* @param save 静态图块实例存档
* @param compression 压缩级别
*/
private loadStatics(save: ReadonlyMap<number, IStaticBlockSave>): void {
private loadStaticTiles(
save: ReadonlyMap<number, IStaticBlockSave>,
compression: SaveCompression
): void {
for (const [index, tileSave] of save) {
const { x, y } = this.map.indexer.indexToLocator(index);
const { x, y } = this.map.indexer.locator(index);
const location = this.getLocationData(x, y);
if (!location?.static) continue;
location.static.loadState(tileSave, SaveCompression.NoCompression);
location.static.loadState(tileSave, compression);
}
}
/**
* 读取动态图块实例存档数据
* @param save 动态图块实例存档
* @param compression 压缩级别
*/
private loadDynamics(save: ReadonlyMap<number, IDynamicBlockSave[]>): void {
private loadDynamics(
save: ReadonlyMap<number, IDynamicBlockSave[]>,
compression: SaveCompression
): void {
for (const [index, dynamics] of save) {
const { x, y } = this.map.indexer.indexToLocator(index);
const { x, y } = this.map.indexer.locator(index);
for (const block of dynamics) {
const tile = this.createDynamic(block.num, x, y);
tile.loadState(block, SaveCompression.NoCompression);
tile.loadState(block, compression);
}
}
}
@@ -855,12 +794,45 @@ export class MapLayer
private clearDynamics(): void {
const tiles = [...this.iterateDynamicTiles()];
for (const tile of tiles) {
this.syncStaticEvent(tile, false);
this.removeDynamic(tile, tile.x, tile.y);
this.forEachHook(hook => hook.onDeleteDynamic?.(tile));
}
}
/**
* 收集需要保存的点事件并复制其内部 Map
*/
private savePointEvents(): Map<number, ReadonlyMap<number, string>> {
const pointEvents = new Map<number, ReadonlyMap<number, string>>();
for (const [index, eventView] of this.pointEvents) {
if (!eventView.dirty()) continue;
pointEvents.set(index, new Map(eventView.get()));
}
return pointEvents;
}
/**
* 在图层基准上叠加点事件存档
* @param save 点事件存档,可省略
*/
private loadPointEvents(
save?: ReadonlyMap<number, ReadonlyMap<number, string>>
): void {
this.resetPointEvents();
if (!save) return;
for (const [index, events] of save) {
const { x, y } = this.indexer.locator(index);
const eventView = this.event(x, y);
if (!eventView) continue;
eventView.clear();
for (const [priority, id] of events) {
eventView.set(priority, id);
}
}
}
// TODO: 不同大小的图层读取
/**
* 以无压缩方式读取当前图层
* @param save 图层存档
@@ -870,10 +842,16 @@ export class MapLayer
this.setMapRef(new Uint32Array(save.fullMap));
}
if (save.staticBlocks) {
this.loadStatics(save.staticBlocks);
this.loadStaticTiles(
save.staticBlocks,
SaveCompression.NoCompression
);
}
if (save.dynamicBlocks) {
this.loadDynamics(save.dynamicBlocks);
this.loadDynamics(
save.dynamicBlocks,
SaveCompression.NoCompression
);
}
this.layerDirty = !this.isEqualToRef();
}
@@ -894,10 +872,16 @@ export class MapLayer
}
if (save.staticBlocks) {
this.loadStatics(save.staticBlocks);
this.loadStaticTiles(
save.staticBlocks,
SaveCompression.LowCompression
);
}
if (save.dynamicBlocks) {
this.loadDynamics(save.dynamicBlocks);
this.loadDynamics(
save.dynamicBlocks,
SaveCompression.LowCompression
);
}
}
@@ -925,10 +909,16 @@ export class MapLayer
}
if (save.staticBlocks) {
this.loadStatics(save.staticBlocks);
this.loadStaticTiles(
save.staticBlocks,
SaveCompression.HighCompression
);
}
if (save.dynamicBlocks) {
this.loadDynamics(save.dynamicBlocks);
this.loadDynamics(
save.dynamicBlocks,
SaveCompression.HighCompression
);
}
}
+9
View File
@@ -13,6 +13,7 @@ import {
ILayerEventView,
IMapBlockSaveBase,
IMapLayer,
IReadonlyTileBase,
ITileBase
} from './types';
@@ -81,6 +82,14 @@ export abstract class MapTileBase<TSave extends IMapBlockSaveBase>
return this.layer.event(this.locator.x, this.locator.y);
}
syncTileEvent(origin: IReadonlyTileBase): void {
const ev = this.tileEvents;
ev.clear();
for (const [priority, id] of origin.tileEvent()) {
ev.set(priority, id);
}
}
abstract saveState(): Readonly<TSave>;
abstract loadState(save: Readonly<TSave>): void;
+12 -9
View File
@@ -35,6 +35,8 @@ export interface IDynamicBlockSave extends IMapBlockSaveBase {
}
export interface IReadonlyEventView {
[Symbol.iterator](): Iterator<[priority: number, event: string]>;
/**
* 获取所有的事件
* @returns 键表示优先级,值表示事件在 `IGameEventStore` 的索引
@@ -131,6 +133,12 @@ export interface ITileBase extends IReadonlyTileBase {
* 获取此图块所在位置的点事件
*/
pointEvent(): ILayerEventView | null;
/**
* 复制某个图块的图块事件至当前图块
* @param origin 要从哪个图块复制
*/
syncTileEvent(origin: IReadonlyTileBase): void;
}
export interface IStaticTile
@@ -359,7 +367,7 @@ interface ILayerStatic {
setStaticDirection(x: number, y: number, direction: FaceDirection): number;
/**
* 迭代所有的图块
* 迭代所有的非空图块。非空定义为此格图块数字不为 0,因此哪怕有事件也会视为空图块
*/
iterateBlocks(): Iterable<ILayerLocation>;
}
@@ -423,14 +431,6 @@ interface ILayerDynamic {
*/
iterateDynamicTiles(): Iterable<IDynamicTile>;
/**
* 设置动态图块的朝向
* @param tile 要设置朝向的动态图块
* @param direction 目标朝向
* @returns 更新朝向后该动态图块的图块数字
*/
setDynamicDirection(tile: IDynamicTile, direction: FaceDirection): number;
/**
* 更新动态图块位置,用于在图块发生移动时更新内部存储,一般不需要手动调用
* @param tile 动态图块
@@ -477,6 +477,9 @@ export interface IMapLayer
ILayerStatic,
ILayerDynamic,
ISaveableContent<IMapLayerSave> {
/** 地图使用的坐标索引器 */
readonly indexer: ILocationHelper;
/** 地图宽度 */
readonly width: number;
/** 地图高度 */