mirror of
https://github.com/motajs/template.git
synced 2026-10-12 15:50:16 +08:00
feat: Adding should replay flag
This commit is contained in:
parent
7743795948
commit
d74cc35c8e
@ -1,3 +1,4 @@
|
|||||||
|
import { shouldReplay } from '@user/data-common';
|
||||||
import { IFlagCommonField, IFlagSystem } from './types';
|
import { IFlagCommonField, IFlagSystem } from './types';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
|
|
||||||
@ -16,10 +17,12 @@ export class FlagCommonField<T> implements IFlagCommonField<T> {
|
|||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Flag field value set should be replayed.')
|
||||||
set(value: T): void {
|
set(value: T): void {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Flag field value set should be replayed.')
|
||||||
add(value: number): void {
|
add(value: number): void {
|
||||||
if (typeof this.value !== 'number') {
|
if (typeof this.value !== 'number') {
|
||||||
logger.warn(111, String(this.key));
|
logger.warn(111, String(this.key));
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { IFlagCommonField, IFlagSystem, IFlagSystemSave } from './types';
|
import { IFlagCommonField, IFlagSystem, IFlagSystemSave } from './types';
|
||||||
import { FlagCommonField } from './field';
|
import { FlagCommonField } from './field';
|
||||||
|
import { shouldReplay } from '@user/data-common';
|
||||||
|
|
||||||
export class FlagSystem implements IFlagSystem {
|
export class FlagSystem implements IFlagSystem {
|
||||||
private readonly fieldMap: Map<PropertyKey, IFlagCommonField<any>> =
|
private readonly fieldMap: Map<PropertyKey, IFlagCommonField<any>> =
|
||||||
@ -9,6 +10,7 @@ export class FlagSystem implements IFlagSystem {
|
|||||||
return this.fieldMap.has(field);
|
return this.fieldMap.has(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Flag field set should be replayed.')
|
||||||
setField<T>(field: PropertyKey, value: T): IFlagCommonField<T> {
|
setField<T>(field: PropertyKey, value: T): IFlagCommonField<T> {
|
||||||
return this.getOrInsert(field, value);
|
return this.getOrInsert(field, value);
|
||||||
}
|
}
|
||||||
@ -34,6 +36,7 @@ export class FlagSystem implements IFlagSystem {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Flag field deleting should be replayed.')
|
||||||
deleteField(field: PropertyKey): void {
|
deleteField(field: PropertyKey): void {
|
||||||
this.fieldMap.delete(field);
|
this.fieldMap.delete(field);
|
||||||
}
|
}
|
||||||
@ -63,26 +66,11 @@ export class FlagSystem implements IFlagSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadState(state: IFlagSystemSave): void {
|
loadState(state: IFlagSystemSave): void {
|
||||||
// 按 key 复用现有字段原地写值,使外部持有的引用跨读档仍然有效
|
this.fieldMap.clear();
|
||||||
for (const [key, data] of state.fields) {
|
for (const [key, data] of state.fields) {
|
||||||
const existing = this.fieldMap.get(key);
|
const field = new FlagCommonField(this, key, void 0);
|
||||||
if (existing) {
|
field.fromStructured(data);
|
||||||
existing.fromStructured(data);
|
this.fieldMap.set(key, field);
|
||||||
} else {
|
|
||||||
const field = new FlagCommonField<unknown>(
|
|
||||||
this,
|
|
||||||
key,
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
field.fromStructured(data);
|
|
||||||
this.fieldMap.set(key, field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 以存档为准:存档中不存在的字段一律删除
|
|
||||||
for (const key of this.fieldMap.keys()) {
|
|
||||||
if (!state.fields.has(key)) {
|
|
||||||
this.fieldMap.delete(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import {
|
|||||||
IDataCommon,
|
IDataCommon,
|
||||||
ItemCategory,
|
ItemCategory,
|
||||||
ReplayCode,
|
ReplayCode,
|
||||||
SaveCompression
|
SaveCompression,
|
||||||
|
shouldReplay
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import { HeroEquipsStore } from './equipStore';
|
import { HeroEquipsStore } from './equipStore';
|
||||||
import {
|
import {
|
||||||
@ -75,6 +76,7 @@ export class HeroItems<THero> implements IHeroItems<THero> {
|
|||||||
return this.getItemState(item)?.count ?? 0;
|
return this.getItemState(item)?.count ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Adding item to hero should be replayed.')
|
||||||
addItem(item: number | string, count: number = 1): void {
|
addItem(item: number | string, count: number = 1): void {
|
||||||
const num = this.resolveNum(item);
|
const num = this.resolveNum(item);
|
||||||
if (isNil(num)) return;
|
if (isNil(num)) return;
|
||||||
@ -113,6 +115,7 @@ export class HeroItems<THero> implements IHeroItems<THero> {
|
|||||||
this.addItem(item, 1);
|
this.addItem(item, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Using item should be replayed.')
|
||||||
useItem(item: number | string): boolean {
|
useItem(item: number | string): boolean {
|
||||||
const state = this.internalGetItemState(item);
|
const state = this.internalGetItemState(item);
|
||||||
if (!state) return false;
|
if (!state) return false;
|
||||||
|
|||||||
@ -7,7 +7,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
FaceDirection,
|
FaceDirection,
|
||||||
IDataCommon,
|
IDataCommon,
|
||||||
IFacedTileLocator
|
IFacedTileLocator,
|
||||||
|
shouldReplay
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import { HeroMover } from './mover';
|
import { HeroMover } from './mover';
|
||||||
import {
|
import {
|
||||||
@ -50,12 +51,14 @@ export class HeroLocation
|
|||||||
return new HookController(this, hook);
|
return new HookController(this, hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting hero floor should be replayed.')
|
||||||
setFloor(map: IGameMap): void {
|
setFloor(map: IGameMap): void {
|
||||||
this.floorId = map.floorId;
|
this.floorId = map.floorId;
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.forEachHook(hook => hook.onSetFloor?.(map));
|
this.forEachHook(hook => hook.onSetFloor?.(map));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting hero position should be replayed.')
|
||||||
setPos(x: number, y: number): void {
|
setPos(x: number, y: number): void {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
|
|||||||
@ -6,7 +6,8 @@ import {
|
|||||||
ObjectMover,
|
ObjectMover,
|
||||||
ObjectMoveType,
|
ObjectMoveType,
|
||||||
IDataCommon,
|
IDataCommon,
|
||||||
ReplayCode
|
ReplayCode,
|
||||||
|
shouldReplay
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import {
|
import {
|
||||||
HeroMoveCode,
|
HeroMoveCode,
|
||||||
@ -152,6 +153,7 @@ export class HeroMover<T extends IHeroLocation>
|
|||||||
|
|
||||||
protected async onMoveEnd(): Promise<void> {}
|
protected async onMoveEnd(): Promise<void> {}
|
||||||
|
|
||||||
|
@shouldReplay('Hero moving step should be replayed.')
|
||||||
protected async onStepStart(
|
protected async onStepStart(
|
||||||
step: Readonly<ObjectMoveStep>,
|
step: Readonly<ObjectMoveStep>,
|
||||||
tile: IHeroLocation
|
tile: IHeroLocation
|
||||||
@ -238,6 +240,7 @@ export class HeroMover<T extends IHeroLocation>
|
|||||||
return HeroMoveCode.Step;
|
return HeroMoveCode.Step;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Hero moving step should be replayed.')
|
||||||
protected async onStepEnd(
|
protected async onStepEnd(
|
||||||
code: number,
|
code: number,
|
||||||
step: Readonly<ObjectMoveStep>,
|
step: Readonly<ObjectMoveStep>,
|
||||||
@ -279,6 +282,7 @@ export class HeroMover<T extends IHeroLocation>
|
|||||||
return { x: tile.x, y: tile.y };
|
return { x: tile.x, y: tile.y };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Hero moving step should be replayed.')
|
||||||
protected async onStepSettled(
|
protected async onStepSettled(
|
||||||
before: ITileLocator,
|
before: ITileLocator,
|
||||||
curr: ITileLocator,
|
curr: ITileLocator,
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import {
|
|||||||
FaceDirection,
|
FaceDirection,
|
||||||
IMoverController,
|
IMoverController,
|
||||||
IObjectMover,
|
IObjectMover,
|
||||||
ITileRawData
|
ITileRawData,
|
||||||
|
shouldReplay
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import {
|
import {
|
||||||
IDynamicBlockSave,
|
IDynamicBlockSave,
|
||||||
@ -53,6 +54,7 @@ export class DynamicTile
|
|||||||
return this.tileRaw;
|
return this.tileRaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting dynamic block num should be replayed.')
|
||||||
set(num: number): void {
|
set(num: number): void {
|
||||||
this.tileNum = num;
|
this.tileNum = num;
|
||||||
const data = this.state.tileStore.getData(num);
|
const data = this.state.tileStore.getData(num);
|
||||||
@ -65,6 +67,7 @@ export class DynamicTile
|
|||||||
this.restoreDefaultEvents();
|
this.restoreDefaultEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting dynamic block position should be replayed.')
|
||||||
setPos(x: number, y: number): void {
|
setPos(x: number, y: number): void {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
@ -81,10 +84,12 @@ export class DynamicTile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Transfering dynamic tile to static should be replayed.')
|
||||||
toStatic(): IStaticTile | null {
|
toStatic(): IStaticTile | null {
|
||||||
return this.layer.transferToStatic(this);
|
return this.layer.transferToStatic(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Transfering dynamic tile to static should be replayed.')
|
||||||
toStaticIfSafe(): IStaticTile | null {
|
toStaticIfSafe(): IStaticTile | null {
|
||||||
return this.layer.transferToStaticIfSafe(this);
|
return this.layer.transferToStaticIfSafe(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { ILayerEventView } from './types';
|
import { ILayerEventView } from './types';
|
||||||
|
import { shouldReplay } from '@user/data-common';
|
||||||
|
|
||||||
export class LayerEventView implements ILayerEventView {
|
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 {
|
set(priority: number, event: string): void {
|
||||||
if (this.store.has(priority)) {
|
if (this.store.has(priority)) {
|
||||||
logger.warn(136, priority.toString());
|
logger.warn(136, priority.toString());
|
||||||
@ -52,6 +54,7 @@ export class LayerEventView implements ILayerEventView {
|
|||||||
this.updateDirtyEntry(priority, before);
|
this.updateDirtyEntry(priority, before);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Deleting layer event view should be replayed.')
|
||||||
delete(priority: number): void {
|
delete(priority: number): void {
|
||||||
const before = this.isEntryDirty(priority);
|
const before = this.isEntryDirty(priority);
|
||||||
this.store.delete(priority);
|
this.store.delete(priority);
|
||||||
|
|||||||
@ -19,7 +19,8 @@ import {
|
|||||||
ILocationIndexer,
|
ILocationIndexer,
|
||||||
ITileStore,
|
ITileStore,
|
||||||
MapLocIndexer,
|
MapLocIndexer,
|
||||||
SaveCompression
|
SaveCompression,
|
||||||
|
shouldReplay
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import { MapLayer } from './mapLayer';
|
import { MapLayer } from './mapLayer';
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
|
|||||||
this.indexer.setWidth(width);
|
this.indexer.setWidth(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Adding game map layer should be replayed.')
|
||||||
addLayer(): IMapLayer {
|
addLayer(): IMapLayer {
|
||||||
const array = new Uint32Array(this.width * this.height);
|
const array = new Uint32Array(this.width * this.height);
|
||||||
const layer = new MapLayer(array, this.width, this.height, this);
|
const layer = new MapLayer(array, this.width, this.height, this);
|
||||||
@ -69,6 +71,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
|
|||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Removing game map layer should be replayed.')
|
||||||
removeLayer(layer: IMapLayer): void {
|
removeLayer(layer: IMapLayer): void {
|
||||||
this.layerList.delete(layer as IResizableMapLayer);
|
this.layerList.delete(layer as IResizableMapLayer);
|
||||||
const alias = this.layerAliasMap.get(layer);
|
const alias = this.layerAliasMap.get(layer);
|
||||||
@ -109,6 +112,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
|
|||||||
return this.layerAliasMap.get(layer);
|
return this.layerAliasMap.get(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Resizing game map layer should be replayed.')
|
||||||
resizeLayer(
|
resizeLayer(
|
||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
@ -141,6 +145,7 @@ export class GameMap extends Hookable<IGameMapHooks> implements IGameMap {
|
|||||||
this.active = active;
|
this.active = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay("Setting game map's event layer should be replayed.")
|
||||||
setEventLayer(layer: IMapLayer | null): void {
|
setEventLayer(layer: IMapLayer | null): void {
|
||||||
if (!layer) {
|
if (!layer) {
|
||||||
this.eventLayer = null;
|
this.eventLayer = null;
|
||||||
|
|||||||
@ -19,7 +19,8 @@ import {
|
|||||||
FaceDirection,
|
FaceDirection,
|
||||||
IDataCommon,
|
IDataCommon,
|
||||||
IRoleFaceBinder,
|
IRoleFaceBinder,
|
||||||
SaveCompression
|
SaveCompression,
|
||||||
|
shouldReplay
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import { DynamicTile } from './dynamicTile';
|
import { DynamicTile } from './dynamicTile';
|
||||||
import { LayerEventView } from './eventView';
|
import { LayerEventView } from './eventView';
|
||||||
@ -233,6 +234,7 @@ export class MapLayer
|
|||||||
return x >= 0 && y >= 0 && x < this.width && y < this.height;
|
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 {
|
setBlock(block: number, x: number, y: number): void {
|
||||||
if (!this.inMap(x, y)) return;
|
if (!this.inMap(x, y)) return;
|
||||||
const index = y * this.width + x;
|
const index = y * this.width + x;
|
||||||
@ -254,6 +256,7 @@ export class MapLayer
|
|||||||
return this.mapArray[y * this.width + x];
|
return this.mapArray[y * this.width + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Removing map layer block should be replayed.')
|
||||||
removeBlock(x: number, y: number): number {
|
removeBlock(x: number, y: number): number {
|
||||||
if (!this.inMap(x, y)) {
|
if (!this.inMap(x, y)) {
|
||||||
return -1;
|
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 {
|
putMapData(array: Uint32Array, x: number, y: number, width: number): void {
|
||||||
if (array.length % width !== 0) {
|
if (array.length % width !== 0) {
|
||||||
logger.warn(8);
|
logger.warn(8);
|
||||||
@ -394,6 +398,7 @@ export class MapLayer
|
|||||||
return this.mapData;
|
return this.mapData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting map layer block direction should be replayed.')
|
||||||
setStaticDirection(x: number, y: number, direction: FaceDirection): number {
|
setStaticDirection(x: number, y: number, direction: FaceDirection): number {
|
||||||
const tile = this.getTile(x, y);
|
const tile = this.getTile(x, y);
|
||||||
if (!tile) return -1;
|
if (!tile) return -1;
|
||||||
@ -415,6 +420,7 @@ export class MapLayer
|
|||||||
|
|
||||||
//#region 动态图层操作
|
//#region 动态图层操作
|
||||||
|
|
||||||
|
@shouldReplay('Creating dynamic tile should be replayed.')
|
||||||
createDynamic(num: number, x: number, y: number): IDynamicTile {
|
createDynamic(num: number, x: number, y: number): IDynamicTile {
|
||||||
const tile = new DynamicTile(num, x, y, this);
|
const tile = new DynamicTile(num, x, y, this);
|
||||||
const location = this.getLocationData(x, y);
|
const location = this.getLocationData(x, y);
|
||||||
@ -432,6 +438,7 @@ export class MapLayer
|
|||||||
return tile;
|
return tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Transfering static tile to dynamic should be replayed.')
|
||||||
transferToDynamic(
|
transferToDynamic(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
@ -457,6 +464,7 @@ export class MapLayer
|
|||||||
return tile;
|
return tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Transfering dynamic tile to static should be replayed.')
|
||||||
transferToStatic(
|
transferToStatic(
|
||||||
tile: IDynamicTile,
|
tile: IDynamicTile,
|
||||||
keepEvent: boolean = true
|
keepEvent: boolean = true
|
||||||
@ -477,6 +485,7 @@ export class MapLayer
|
|||||||
return this.getTile(x, y);
|
return this.getTile(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Transfering dynamic tile to static should be replayed.')
|
||||||
transferToStaticIfSafe(
|
transferToStaticIfSafe(
|
||||||
tile: IDynamicTile,
|
tile: IDynamicTile,
|
||||||
keepEvent: boolean = true
|
keepEvent: boolean = true
|
||||||
@ -495,6 +504,7 @@ export class MapLayer
|
|||||||
return this.getTile(x, y);
|
return this.getTile(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Deleting dynamic tile should be replayed.')
|
||||||
async deleteDynamic(tile: IDynamicTile): Promise<void> {
|
async deleteDynamic(tile: IDynamicTile): Promise<void> {
|
||||||
if (!this.posTileMap.has(tile)) {
|
if (!this.posTileMap.has(tile)) {
|
||||||
logger.warn(130);
|
logger.warn(130);
|
||||||
@ -513,6 +523,7 @@ export class MapLayer
|
|||||||
return this.posTileMap.keys();
|
return this.posTileMap.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting map layer block direction should be replayed.')
|
||||||
setDynamicDirection(tile: IDynamicTile, direction: FaceDirection): number {
|
setDynamicDirection(tile: IDynamicTile, direction: FaceDirection): number {
|
||||||
const numBefore = tile.num();
|
const numBefore = tile.num();
|
||||||
tile.setFaceDirection(direction);
|
tile.setFaceDirection(direction);
|
||||||
@ -524,6 +535,7 @@ export class MapLayer
|
|||||||
return tile.num();
|
return tile.num();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Updating dynamic tile position should be replayed.')
|
||||||
updateDynamicTile(tile: IDynamicTile): void {
|
updateDynamicTile(tile: IDynamicTile): void {
|
||||||
const oldPos = this.posTileMap.get(tile);
|
const oldPos = this.posTileMap.get(tile);
|
||||||
if (oldPos) {
|
if (oldPos) {
|
||||||
@ -542,6 +554,7 @@ export class MapLayer
|
|||||||
|
|
||||||
//#region 开关门
|
//#region 开关门
|
||||||
|
|
||||||
|
@shouldReplay('Opening door should be replayed.')
|
||||||
async openDoor(x: number, y: number): Promise<void> {
|
async openDoor(x: number, y: number): Promise<void> {
|
||||||
const index = y * this.width + x;
|
const index = y * this.width + x;
|
||||||
const num = this.mapArray[index];
|
const num = this.mapArray[index];
|
||||||
@ -554,6 +567,7 @@ export class MapLayer
|
|||||||
this.setBlock(0, x, y);
|
this.setBlock(0, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Closing door should be replayed.')
|
||||||
async closeDoor(num: number, x: number, y: number): Promise<void> {
|
async closeDoor(num: number, x: number, y: number): Promise<void> {
|
||||||
const index = y * this.width + x;
|
const index = y * this.width + x;
|
||||||
const nowNum = this.mapArray[index];
|
const nowNum = this.mapArray[index];
|
||||||
@ -573,10 +587,12 @@ export class MapLayer
|
|||||||
|
|
||||||
//#region 图层操作
|
//#region 图层操作
|
||||||
|
|
||||||
|
@shouldReplay('Setting z-index of map layer should be replayed.')
|
||||||
setZIndex(zIndex: number): void {
|
setZIndex(zIndex: number): void {
|
||||||
this.zIndex = zIndex;
|
this.zIndex = zIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting role face binder of map layer should be replayed.')
|
||||||
setFaceBinder(binder: IRoleFaceBinder | null): void {
|
setFaceBinder(binder: IRoleFaceBinder | null): void {
|
||||||
if (!binder) return;
|
if (!binder) return;
|
||||||
this.faceBinder = binder;
|
this.faceBinder = binder;
|
||||||
@ -616,6 +632,7 @@ export class MapLayer
|
|||||||
return new MapLayerHookController(this, hook);
|
return new MapLayerHookController(this, hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Resizing map layer should be replayed.')
|
||||||
resize(width: number, height: number): void {
|
resize(width: number, height: number): void {
|
||||||
if (this.width === width && this.height === height) {
|
if (this.width === width && this.height === height) {
|
||||||
return;
|
return;
|
||||||
@ -656,6 +673,7 @@ export class MapLayer
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Resizing map layer should be replayed.')
|
||||||
resize2(width: number, height: number): void {
|
resize2(width: number, height: number): void {
|
||||||
this.layerDirty = true;
|
this.layerDirty = true;
|
||||||
this.pointEvents.clear();
|
this.pointEvents.clear();
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import { uniq } from 'lodash-es';
|
import {
|
||||||
import { IDataCommon, IMapRawData, SaveCompression } from '@user/data-common';
|
IDataCommon,
|
||||||
|
IMapRawData,
|
||||||
|
SaveCompression,
|
||||||
|
shouldReplay
|
||||||
|
} from '@user/data-common';
|
||||||
import { ITileStore } from '@user/data-common';
|
import { ITileStore } from '@user/data-common';
|
||||||
import {
|
import {
|
||||||
IGameMap,
|
IGameMap,
|
||||||
@ -35,160 +39,9 @@ export class MapState implements IMapState {
|
|||||||
public readonly state: IDataCommon
|
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 楼层管理
|
//#region 楼层管理
|
||||||
|
|
||||||
fromRaw(raw: IMapRawData): IGameMap | null {
|
fromRaw(raw: IMapRawData): IGameMap | null {
|
||||||
if (!this.validateRaw(raw)) return null;
|
|
||||||
let length = 0;
|
let length = 0;
|
||||||
const entries = Object.entries(raw.map);
|
const entries = Object.entries(raw.map);
|
||||||
for (const [_, map] of Object.entries(raw.map)) {
|
for (const [_, map] of Object.entries(raw.map)) {
|
||||||
@ -246,6 +99,7 @@ export class MapState implements IMapState {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Creating map should be replayed.')
|
||||||
createMap(id: string, width: number, height: number): IGameMap {
|
createMap(id: string, width: number, height: number): IGameMap {
|
||||||
if (this.mapData.has(id)) {
|
if (this.mapData.has(id)) {
|
||||||
logger.warn(121, id);
|
logger.warn(121, id);
|
||||||
@ -262,9 +116,10 @@ export class MapState implements IMapState {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting map list should be replayed.')
|
||||||
setMapList(maps: string[]): void {
|
setMapList(maps: string[]): void {
|
||||||
this.maps.length = 0;
|
this.maps.length = 0;
|
||||||
this.maps.push(...uniq(maps));
|
this.maps.push(...maps);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMap(id: string): IGameMap | null {
|
getMap(id: string): IGameMap | null {
|
||||||
@ -285,6 +140,7 @@ export class MapState implements IMapState {
|
|||||||
this.areaList = areas;
|
this.areaList = areas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Activating area should be replayed.')
|
||||||
activeArea(id: string): void {
|
activeArea(id: string): void {
|
||||||
const idx = this.maps.indexOf(id);
|
const idx = this.maps.indexOf(id);
|
||||||
if (idx === -1) return;
|
if (idx === -1) return;
|
||||||
@ -293,6 +149,7 @@ export class MapState implements IMapState {
|
|||||||
this.setAreaActive(area, true);
|
this.setAreaActive(area, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Deactivating area should be replayed.')
|
||||||
deactiveArea(id: string): void {
|
deactiveArea(id: string): void {
|
||||||
const idx = this.maps.indexOf(id);
|
const idx = this.maps.indexOf(id);
|
||||||
if (idx === -1) return;
|
if (idx === -1) return;
|
||||||
@ -357,6 +214,7 @@ export class MapState implements IMapState {
|
|||||||
return this.mapData.get(id)?.active ?? false;
|
return this.mapData.get(id)?.active ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting map active status should be replayed.')
|
||||||
setMapActiveStatus(id: string, active: boolean): void {
|
setMapActiveStatus(id: string, active: boolean): void {
|
||||||
this.mapData.get(id)?.setActiveStatus(active);
|
this.mapData.get(id)?.setActiveStatus(active);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import {
|
|||||||
getFaceMovement,
|
getFaceMovement,
|
||||||
ObjectMover,
|
ObjectMover,
|
||||||
ObjectMoveStep,
|
ObjectMoveStep,
|
||||||
ObjectMoveType
|
ObjectMoveType,
|
||||||
|
shouldReplay
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import { IDynamicTile } from './types';
|
import { IDynamicTile } from './types';
|
||||||
import { DYNAMIC_MOVER_FACE } from '../shared';
|
import { DYNAMIC_MOVER_FACE } from '../shared';
|
||||||
@ -31,6 +32,7 @@ export class DynamicTileMover extends ObjectMover<IDynamicTile> {
|
|||||||
return Promise.resolve(DynamicMoveCode.Success);
|
return Promise.resolve(DynamicMoveCode.Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Dynamic tile moving step should be replayed.')
|
||||||
protected onStepEnd(
|
protected onStepEnd(
|
||||||
code: number,
|
code: number,
|
||||||
step: ObjectMoveStep,
|
step: ObjectMoveStep,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ITileRawData } from '@user/data-common';
|
import { ITileRawData, shouldReplay } from '@user/data-common';
|
||||||
import {
|
import {
|
||||||
IDynamicTile,
|
IDynamicTile,
|
||||||
IMapLayer,
|
IMapLayer,
|
||||||
@ -24,11 +24,13 @@ export class StaticTile
|
|||||||
return this.state.tileStore.getData(this.num());
|
return this.state.tileStore.getData(this.num());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting static tile num should be replayed.')
|
||||||
set(num: number): void {
|
set(num: number): void {
|
||||||
this.layer.setBlock(num, this.locator.x, this.locator.y);
|
this.layer.setBlock(num, this.locator.x, this.locator.y);
|
||||||
this.restoreDefaultEvents();
|
this.restoreDefaultEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Transfering static tile to dynamic should be replayed.')
|
||||||
toDynamic(): IDynamicTile {
|
toDynamic(): IDynamicTile {
|
||||||
return this.layer.transferToDynamic(this.locator.x, this.locator.y)!;
|
return this.layer.transferToDynamic(this.locator.x, this.locator.y)!;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,8 @@ import {
|
|||||||
IDataCommon,
|
IDataCommon,
|
||||||
IDataCommonExtended,
|
IDataCommonExtended,
|
||||||
ISaveableContent,
|
ISaveableContent,
|
||||||
ITileRawData
|
ITileRawData,
|
||||||
|
shouldReplay
|
||||||
} from '@user/data-common';
|
} from '@user/data-common';
|
||||||
import { LayerEventView } from './eventView';
|
import { LayerEventView } from './eventView';
|
||||||
import {
|
import {
|
||||||
@ -52,6 +53,7 @@ export abstract class MapTileBase<TSave extends IMapBlockSaveBase>
|
|||||||
eventView.markPure();
|
eventView.markPure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Setting map tile face direction should be replayed.')
|
||||||
setFaceDirection(direction: FaceDirection): number {
|
setFaceDirection(direction: FaceDirection): number {
|
||||||
const cur = this.num();
|
const cur = this.num();
|
||||||
const next = this.layer.faceBinder.getFaceOf(cur, direction);
|
const next = this.layer.faceBinder.getFaceOf(cur, direction);
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import {
|
|||||||
IReadonlyEnemy,
|
IReadonlyEnemy,
|
||||||
IStateBase
|
IStateBase
|
||||||
} from '@user/data-base';
|
} from '@user/data-base';
|
||||||
|
import { shouldReplay } from '@user/data-common';
|
||||||
|
|
||||||
export class CombatFlow<TEnemy, THero>
|
export class CombatFlow<TEnemy, THero>
|
||||||
extends Hookable<ICombatFlowHooks<TEnemy, THero>>
|
extends Hookable<ICombatFlowHooks<TEnemy, THero>>
|
||||||
@ -193,6 +194,7 @@ export class CombatFlow<TEnemy, THero>
|
|||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Battle with enemy should be replayed.')
|
||||||
battle(
|
battle(
|
||||||
enemy: IEnemyView<TEnemy>
|
enemy: IEnemyView<TEnemy>
|
||||||
): Promise<IEnemyDamageInfo<TEnemy, THero> | null> {
|
): Promise<IEnemyDamageInfo<TEnemy, THero> | null> {
|
||||||
@ -214,6 +216,7 @@ export class CombatFlow<TEnemy, THero>
|
|||||||
return this.combatFlow(handler, eHandler);
|
return this.combatFlow(handler, eHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Battle with enemy should be replayed.')
|
||||||
battleComputed(
|
battleComputed(
|
||||||
enemy: IReadonlyEnemy<TEnemy>
|
enemy: IReadonlyEnemy<TEnemy>
|
||||||
): Promise<IEnemyDamageInfo<TEnemy, THero> | null> {
|
): Promise<IEnemyDamageInfo<TEnemy, THero> | null> {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { IGameEventStore, ignoreReplay } from '@user/data-common';
|
import { IGameEventStore, shouldReplay } from '@user/data-common';
|
||||||
import { AnonTokyoInterpreter } from '@motajs/anon-tokyo';
|
import { AnonTokyoInterpreter } from '@motajs/anon-tokyo';
|
||||||
import {
|
import {
|
||||||
EventExecuteMode,
|
EventExecuteMode,
|
||||||
@ -52,8 +52,7 @@ export class EventExecutor implements IGameEventExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error 泛型无法推导
|
@shouldReplay('Executing event should be replayed.')
|
||||||
@ignoreReplay('事件一般由其他动作被动触发,不应计入录像')
|
|
||||||
async execute<R = void>(
|
async execute<R = void>(
|
||||||
events: IGameEventInvocation[],
|
events: IGameEventInvocation[],
|
||||||
param: IBlockEventParam
|
param: IBlockEventParam
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ITileLocator, logger } from '@motajs/common';
|
import { ITileLocator, logger } from '@motajs/common';
|
||||||
import { IObjectMovable, IObjectMover } from '@user/data-common';
|
import { IObjectMovable, IObjectMover, shouldReplay } from '@user/data-common';
|
||||||
import { IStateBase } from '@user/data-base';
|
import { IStateBase } from '@user/data-base';
|
||||||
import { PathFinder } from './finder';
|
import { PathFinder } from './finder';
|
||||||
import {
|
import {
|
||||||
@ -79,12 +79,14 @@ export class PathfindingSystem implements IPathfindingSystem {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Moving hero by path finding system should be replayed.')
|
||||||
moveTo(target: ITileLocator): IPathfindingController | null {
|
moveTo(target: ITileLocator): IPathfindingController | null {
|
||||||
const path = this.getPath(target);
|
const path = this.getPath(target);
|
||||||
if (path.status !== PathfindingStatus.Success) return null;
|
if (path.status !== PathfindingStatus.Success) return null;
|
||||||
return this.startMove(path, false);
|
return this.startMove(path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@shouldReplay('Teleporting hero by path finding system should be replayed.')
|
||||||
teleportTo(target: ITileLocator): IPathfindingController | null {
|
teleportTo(target: ITileLocator): IPathfindingController | null {
|
||||||
const path = this.getPath(target);
|
const path = this.getPath(target);
|
||||||
if (path.status !== PathfindingStatus.Success) return null;
|
if (path.status !== PathfindingStatus.Success) return null;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user