feat: Adding should replay flag

This commit is contained in:
AncTe committed 2026-09-23 13:09:43 +08:00
1 parent 7743795948
commit d74cc35c8e
16 files changed
+86 -186

No files matched your search

@@ -3,7 +3,8 @@ import {
FaceDirection,
IMoverController,
IObjectMover,
ITileRawData
ITileRawData,
shouldReplay
} from '@user/data-common';
import {
IDynamicBlockSave,
@@ -53,6 +54,7 @@ export class DynamicTile
return this.tileRaw;
}
@shouldReplay('Setting dynamic block num should be replayed.')
set(num: number): void {
this.tileNum = num;
const data = this.state.tileStore.getData(num);
@@ -65,6 +67,7 @@ export class DynamicTile
this.restoreDefaultEvents();
}
@shouldReplay('Setting dynamic block position should be replayed.')
setPos(x: number, y: number): void {
this.x = x;
this.y = y;
@@ -81,10 +84,12 @@ export class DynamicTile
}
}
@shouldReplay('Transfering dynamic tile to static should be replayed.')
toStatic(): IStaticTile | null {
return this.layer.transferToStatic(this);
}
@shouldReplay('Transfering dynamic tile to static should be replayed.')
toStaticIfSafe(): IStaticTile | null {
return this.layer.transferToStaticIfSafe(this);
}
@@ -1,5 +1,6 @@
import { logger } from '@motajs/common';
import { ILayerEventView } from './types';
import { shouldReplay } from '@user/data-common';
export class LayerEventView implements ILayerEventView {
/** 当前绑定的事件 */
@@ -43,6 +44,7 @@ export class LayerEventView implements ILayerEventView {
}
}
@shouldReplay('Setting layer event view should be replayed.')
set(priority: number, event: string): void {
if (this.store.has(priority)) {
logger.warn(136, priority.toString());
@@ -52,6 +54,7 @@ export class LayerEventView implements ILayerEventView {
this.updateDirtyEntry(priority, before);
}
@shouldReplay('Deleting layer event view should be replayed.')
delete(priority: number): void {
const before = this.isEntryDirty(priority);
this.store.delete(priority);
+6 -1
View File
@@ -19,7 +19,8 @@ import {
ILocationIndexer,
ITileStore,
MapLocIndexer,
SaveCompression
SaveCompression,
shouldReplay
} from '@user/data-common';
import { MapLayer } from './mapLayer';
@@ -56,6 +57,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
this.indexer.setWidth(width);
}
@shouldReplay('Adding game map layer should be replayed.')
addLayer(): IMapLayer {
const array = new Uint32Array(this.width * this.height);
const layer = new MapLayer(array, this.width, this.height, this);
@@ -69,6 +71,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
return layer;
}
@shouldReplay('Removing game map layer should be replayed.')
removeLayer(layer: IMapLayer): void {
this.layerList.delete(layer as IResizableMapLayer);
const alias = this.layerAliasMap.get(layer);
@@ -109,6 +112,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
return this.layerAliasMap.get(layer);
}
@shouldReplay('Resizing game map layer should be replayed.')
resizeLayer(
width: number,
height: number,
@@ -141,6 +145,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
this.active = active;
}
@shouldReplay("Setting game map's event layer should be replayed.")
setEventLayer(layer: IMapLayer | null): void {
if (!layer) {
this.eventLayer = null;
+19 -1
View File
@@ -19,7 +19,8 @@ import {
FaceDirection,
IDataCommon,
IRoleFaceBinder,
SaveCompression
SaveCompression,
shouldReplay
} from '@user/data-common';
import { DynamicTile } from './dynamicTile';
import { LayerEventView } from './eventView';
@@ -233,6 +234,7 @@ export class MapLayer
return x >= 0 && y >= 0 && x < this.width && y < this.height;
}
@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;
@@ -254,6 +256,7 @@ export class MapLayer
return this.mapArray[y * this.width + x];
}
@shouldReplay('Removing map layer block should be replayed.')
removeBlock(x: number, y: number): number {
if (!this.inMap(x, y)) {
return -1;
@@ -287,6 +290,7 @@ export class MapLayer
};
}
@shouldReplay('Setting map layer data should be replayed.')
putMapData(array: Uint32Array, x: number, y: number, width: number): void {
if (array.length % width !== 0) {
logger.warn(8);
@@ -394,6 +398,7 @@ export class MapLayer
return this.mapData;
}
@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;
@@ -415,6 +420,7 @@ export class MapLayer
//#region 动态图层操作
@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);
@@ -432,6 +438,7 @@ export class MapLayer
return tile;
}
@shouldReplay('Transfering static tile to dynamic should be replayed.')
transferToDynamic(
x: number,
y: number,
@@ -457,6 +464,7 @@ export class MapLayer
return tile;
}
@shouldReplay('Transfering dynamic tile to static should be replayed.')
transferToStatic(
tile: IDynamicTile,
keepEvent: boolean = true
@@ -477,6 +485,7 @@ export class MapLayer
return this.getTile(x, y);
}
@shouldReplay('Transfering dynamic tile to static should be replayed.')
transferToStaticIfSafe(
tile: IDynamicTile,
keepEvent: boolean = true
@@ -495,6 +504,7 @@ export class MapLayer
return this.getTile(x, y);
}
@shouldReplay('Deleting dynamic tile should be replayed.')
async deleteDynamic(tile: IDynamicTile): Promise<void> {
if (!this.posTileMap.has(tile)) {
logger.warn(130);
@@ -513,6 +523,7 @@ export class MapLayer
return this.posTileMap.keys();
}
@shouldReplay('Setting map layer block direction should be replayed.')
setDynamicDirection(tile: IDynamicTile, direction: FaceDirection): number {
const numBefore = tile.num();
tile.setFaceDirection(direction);
@@ -524,6 +535,7 @@ export class MapLayer
return tile.num();
}
@shouldReplay('Updating dynamic tile position should be replayed.')
updateDynamicTile(tile: IDynamicTile): void {
const oldPos = this.posTileMap.get(tile);
if (oldPos) {
@@ -542,6 +554,7 @@ export class MapLayer
//#region 开关门
@shouldReplay('Opening door should be replayed.')
async openDoor(x: number, y: number): Promise<void> {
const index = y * this.width + x;
const num = this.mapArray[index];
@@ -554,6 +567,7 @@ export class MapLayer
this.setBlock(0, x, y);
}
@shouldReplay('Closing door should be replayed.')
async closeDoor(num: number, x: number, y: number): Promise<void> {
const index = y * this.width + x;
const nowNum = this.mapArray[index];
@@ -573,10 +587,12 @@ export class MapLayer
//#region 图层操作
@shouldReplay('Setting z-index of map layer should be replayed.')
setZIndex(zIndex: number): void {
this.zIndex = zIndex;
}
@shouldReplay('Setting role face binder of map layer should be replayed.')
setFaceBinder(binder: IRoleFaceBinder | null): void {
if (!binder) return;
this.faceBinder = binder;
@@ -616,6 +632,7 @@ export class MapLayer
return new MapLayerHookController(this, hook);
}
@shouldReplay('Resizing map layer should be replayed.')
resize(width: number, height: number): void {
if (this.width === width && this.height === height) {
return;
@@ -656,6 +673,7 @@ export class MapLayer
});
}
@shouldReplay('Resizing map layer should be replayed.')
resize2(width: number, height: number): void {
this.layerDirty = true;
this.pointEvents.clear();
+12 -154
View File
@@ -1,5 +1,9 @@
import { uniq } from 'lodash-es';
import { IDataCommon, IMapRawData, SaveCompression } from '@user/data-common';
import {
IDataCommon,
IMapRawData,
SaveCompression,
shouldReplay
} from '@user/data-common';
import { ITileStore } from '@user/data-common';
import {
IGameMap,
@@ -35,160 +39,9 @@ export class MapState implements IMapState {
public readonly state: IDataCommon
) {}
/**
* 判断原始数据中的值是否为可枚举的对象容器
* @param value 待判断的运行时值
*/
private isRecord(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === 'object' && !Array.isArray(value);
}
/**
* 判断字符串键是否能无歧义地转换为有限数字
* @param key 原始对象键
*/
private isNumericKey(key: string): boolean {
return key.trim() !== '' && Number.isFinite(Number(key));
}
/**
* 在创建地图前验证外部原始地图及事件结构
* @param raw 待验证的楼层原始数据
*/
private validateRaw(raw: IMapRawData): boolean {
const rawMap: unknown = raw.map;
const rawEvents: unknown = raw.events;
const rawAliases: unknown = raw.layerAlias;
if (!this.isRecord(rawMap)) {
logger.error(63, 'map', raw.floorId);
return false;
}
if (!this.isRecord(rawEvents)) {
logger.error(63, 'events', raw.floorId);
return false;
}
if (!this.isRecord(rawAliases)) {
logger.error(63, 'layerAlias', raw.floorId);
return false;
}
if (!Number.isInteger(raw.width) || raw.width <= 0) {
logger.error(64, 'width', raw.floorId);
return false;
}
let length = 0;
for (const [zIndex, map] of Object.entries(rawMap)) {
if (!this.isNumericKey(zIndex)) {
logger.error(
62,
'layer',
raw.floorId,
'IMapRawData.map',
zIndex
);
return false;
}
if (!Array.isArray(map)) {
logger.error(64, 'map layer', raw.floorId);
return false;
}
if (
!map.every(
value =>
typeof value === 'number' &&
Number.isFinite(value) &&
Number.isInteger(value) &&
value >= 0
)
) {
logger.error(64, 'map value', raw.floorId);
return false;
}
if (length > 0 && map.length !== length) {
logger.error(
60,
map.length.toString(),
length.toString(),
raw.floorId
);
return false;
}
length = map.length;
const alias = rawAliases[zIndex];
if (typeof alias !== 'string') {
logger.error(64, 'layer alias', raw.floorId);
return false;
}
const events = rawEvents[zIndex];
if (!this.isRecord(events)) {
logger.error(63, `events layer ${zIndex}`, raw.floorId);
return false;
}
for (const [index, tileEvents] of Object.entries(events)) {
if (!this.isNumericKey(index)) {
logger.error(
62,
'event',
raw.floorId,
'IMapRawData.events',
index
);
return false;
}
const indexNum = Number(index);
if (
!Number.isInteger(indexNum) ||
indexNum < 0 ||
indexNum >= length
) {
logger.error(64, 'event position', raw.floorId);
return false;
}
if (!this.isRecord(tileEvents)) {
logger.error(63, `events position ${index}`, raw.floorId);
return false;
}
for (const [priority, id] of Object.entries(tileEvents)) {
if (!this.isNumericKey(priority)) {
logger.error(
62,
'event',
raw.floorId,
'IMapRawData.events',
priority
);
return false;
}
if (typeof id !== 'string') {
logger.error(64, 'event id', raw.floorId);
return false;
}
}
}
}
if (length % raw.width !== 0) {
logger.error(
61,
length.toString(),
raw.width.toString(),
raw.floorId
);
return false;
}
for (const zIndex of Object.keys(rawEvents)) {
if (!this.isNumericKey(zIndex) || !Object.hasOwn(rawMap, zIndex)) {
logger.error(64, 'event layer', raw.floorId);
return false;
}
}
return true;
}
//#region 楼层管理
fromRaw(raw: IMapRawData): IGameMap | null {
if (!this.validateRaw(raw)) return null;
let length = 0;
const entries = Object.entries(raw.map);
for (const [_, map] of Object.entries(raw.map)) {
@@ -246,6 +99,7 @@ export class MapState implements IMapState {
return state;
}
@shouldReplay('Creating map should be replayed.')
createMap(id: string, width: number, height: number): IGameMap {
if (this.mapData.has(id)) {
logger.warn(121, id);
@@ -262,9 +116,10 @@ export class MapState implements IMapState {
return state;
}
@shouldReplay('Setting map list should be replayed.')
setMapList(maps: string[]): void {
this.maps.length = 0;
this.maps.push(...uniq(maps));
this.maps.push(...maps);
}
getMap(id: string): IGameMap | null {
@@ -285,6 +140,7 @@ export class MapState implements IMapState {
this.areaList = areas;
}
@shouldReplay('Activating area should be replayed.')
activeArea(id: string): void {
const idx = this.maps.indexOf(id);
if (idx === -1) return;
@@ -293,6 +149,7 @@ export class MapState implements IMapState {
this.setAreaActive(area, true);
}
@shouldReplay('Deactivating area should be replayed.')
deactiveArea(id: string): void {
const idx = this.maps.indexOf(id);
if (idx === -1) return;
@@ -357,6 +214,7 @@ export class MapState implements IMapState {
return this.mapData.get(id)?.active ?? false;
}
@shouldReplay('Setting map active status should be replayed.')
setMapActiveStatus(id: string, active: boolean): void {
this.mapData.get(id)?.setActiveStatus(active);
}
+3 -1
View File
@@ -3,7 +3,8 @@ import {
getFaceMovement,
ObjectMover,
ObjectMoveStep,
ObjectMoveType
ObjectMoveType,
shouldReplay
} from '@user/data-common';
import { IDynamicTile } from './types';
import { DYNAMIC_MOVER_FACE } from '../shared';
@@ -31,6 +32,7 @@ export class DynamicTileMover extends ObjectMover<IDynamicTile> {
return Promise.resolve(DynamicMoveCode.Success);
}
@shouldReplay('Dynamic tile moving step should be replayed.')
protected onStepEnd(
code: number,
step: ObjectMoveStep,
@@ -1,4 +1,4 @@
import { ITileRawData } from '@user/data-common';
import { ITileRawData, shouldReplay } from '@user/data-common';
import {
IDynamicTile,
IMapLayer,
@@ -24,11 +24,13 @@ export class StaticTile
return this.state.tileStore.getData(this.num());
}
@shouldReplay('Setting static tile num should be replayed.')
set(num: number): void {
this.layer.setBlock(num, this.locator.x, this.locator.y);
this.restoreDefaultEvents();
}
@shouldReplay('Transfering static tile to dynamic should be replayed.')
toDynamic(): IDynamicTile {
return this.layer.transferToDynamic(this.locator.x, this.locator.y)!;
}
+3 -1
View File
@@ -4,7 +4,8 @@ import {
IDataCommon,
IDataCommonExtended,
ISaveableContent,
ITileRawData
ITileRawData,
shouldReplay
} from '@user/data-common';
import { LayerEventView } from './eventView';
import {
@@ -52,6 +53,7 @@ export abstract class MapTileBase<TSave extends IMapBlockSaveBase>
eventView.markPure();
}
@shouldReplay('Setting map tile face direction should be replayed.')
setFaceDirection(direction: FaceDirection): number {
const cur = this.num();
const next = this.layer.faceBinder.getFaceOf(cur, direction);