refactor: 调整录像命名

This commit is contained in:
unanmed 2026-09-16 21:51:19 +08:00
parent 513a9c354c
commit 49aacb087a
11 changed files with 171 additions and 203 deletions

View File

@ -1,5 +1,5 @@
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { IDataCommon, ReplayCommandCode } from '@user/data-common'; import { IDataCommon, ReplayCode } from '@user/data-common';
import { import {
EquipStatus, EquipStatus,
IEquipmentState, IEquipmentState,
@ -192,7 +192,7 @@ export class HeroEquipment<THero> implements IHeroEquipment<THero> {
// 最后恢复录像记录并记录录像 // 最后恢复录像记录并记录录像
replay.revert(); replay.revert();
replay.route.add(ReplayCommandCode.Equip, [uid]); replay.route.add(ReplayCode.Equip, [uid]);
return curr; return curr;
} }
@ -209,7 +209,7 @@ export class HeroEquipment<THero> implements IHeroEquipment<THero> {
// 记录录像 // 记录录像
const replay = this.state.replaySystem; const replay = this.state.replaySystem;
replay.route.add(ReplayCommandCode.Unequip, [slot]); replay.route.add(ReplayCode.Unequip, [slot]);
this.unloadEquipEffect(state); this.unloadEquipEffect(state);
this.equips.delete(slot); this.equips.delete(slot);

View File

@ -2,7 +2,7 @@ import { isNil } from 'lodash-es';
import { import {
IDataCommon, IDataCommon,
ItemCategory, ItemCategory,
ReplayCommandCode, ReplayCode,
SaveCompression SaveCompression
} from '@user/data-common'; } from '@user/data-common';
import { HeroEquipsStore } from './equipStore'; import { HeroEquipsStore } from './equipStore';
@ -127,7 +127,7 @@ export class HeroItems<THero> implements IHeroItems<THero> {
if (!raw.effect.canUse(raw)) return false; if (!raw.effect.canUse(raw)) return false;
const replay = this.state.replaySystem; const replay = this.state.replaySystem;
replay.route.add(ReplayCommandCode.UseItem, [raw.num]); replay.route.add(ReplayCode.UseItem, [raw.num]);
raw.effect.useEffect(raw); raw.effect.useEffect(raw);

View File

@ -7,7 +7,7 @@ import {
ObjectMover, ObjectMover,
ObjectMoveType, ObjectMoveType,
IDataCommon, IDataCommon,
ReplayCommandCode ReplayCode
} from '@user/data-common'; } from '@user/data-common';
import { import {
HeroMoveCode, HeroMoveCode,
@ -176,16 +176,16 @@ export class HeroMover<T extends IHeroLocation>
const replay = this.state.replaySystem; const replay = this.state.replaySystem;
switch (handler.direction) { switch (handler.direction) {
case FaceDirection.Up: case FaceDirection.Up:
replay.route.add(ReplayCommandCode.Up, []); replay.route.add(ReplayCode.Up, []);
break; break;
case FaceDirection.Right: case FaceDirection.Right:
replay.route.add(ReplayCommandCode.Right, []); replay.route.add(ReplayCode.Right, []);
break; break;
case FaceDirection.Left: case FaceDirection.Left:
replay.route.add(ReplayCommandCode.Left, []); replay.route.add(ReplayCode.Left, []);
break; break;
case FaceDirection.Down: case FaceDirection.Down:
replay.route.add(ReplayCommandCode.Down, []); replay.route.add(ReplayCode.Down, []);
break; break;
default: default:
logger.warn(176); logger.warn(176);

View File

@ -2,7 +2,7 @@ import { IHookable, IHookBase } from '@motajs/common';
import { ISaveableContent } from '../save'; import { ISaveableContent } from '../save';
/** 录像指令码,数值属于录像格式的一部分 */ /** 录像指令码,数值属于录像格式的一部分 */
export const enum ReplayCommandCode { export const enum ReplayCode {
/** 向上移动一步 */ /** 向上移动一步 */
Up, Up,
/** 向右移动一步 */ /** 向右移动一步 */

View File

@ -25,7 +25,7 @@ import {
MapStore, MapStore,
IReplaySystem, IReplaySystem,
ReplaySystem, ReplaySystem,
ReplayCommandCode ReplayCode
} from '@user/data-common'; } from '@user/data-common';
import { import {
EnemyManager, EnemyManager,
@ -84,11 +84,11 @@ import { isNil } from 'lodash-es';
import { DefaultHeroMoveTopImpl, DefaultPassPredicateImpl } from './hero'; import { DefaultHeroMoveTopImpl, DefaultPassPredicateImpl } from './hero';
import { createEventRegistrations } from './event/registrations'; import { createEventRegistrations } from './event/registrations';
import { import {
ReplayEquipCommand, ReplayEquip,
ReplayMoveCommand, ReplayMove,
ReplayTeleportCommand, ReplayTeleport,
ReplayUnequipCommand, ReplayUnequip,
ReplayUseItemCommand ReplayUseItem
} from './replay'; } from './replay';
export class CoreState implements ICoreState { export class CoreState implements ICoreState {
@ -282,38 +282,25 @@ export class CoreState implements ICoreState {
* 注册全部录像指令 * 注册全部录像指令
*/ */
private registerReplayCommands() { private registerReplayCommands() {
this.replaySystem.registerCommand( const replay = this.replaySystem;
ReplayCommandCode.Up,
new ReplayMoveCommand(this, FaceDirection.Up) const up = new ReplayMove(this, FaceDirection.Up);
); const right = new ReplayMove(this, FaceDirection.Right);
this.replaySystem.registerCommand( const down = new ReplayMove(this, FaceDirection.Down);
ReplayCommandCode.Right, const left = new ReplayMove(this, FaceDirection.Left);
new ReplayMoveCommand(this, FaceDirection.Right) const teleport = new ReplayTeleport(this);
); const useItem = new ReplayUseItem(this);
this.replaySystem.registerCommand( const equip = new ReplayEquip(this);
ReplayCommandCode.Down, const unequip = new ReplayUnequip(this);
new ReplayMoveCommand(this, FaceDirection.Down)
); replay.registerCommand(ReplayCode.Up, up);
this.replaySystem.registerCommand( replay.registerCommand(ReplayCode.Right, right);
ReplayCommandCode.Left, replay.registerCommand(ReplayCode.Down, down);
new ReplayMoveCommand(this, FaceDirection.Left) replay.registerCommand(ReplayCode.Left, left);
); replay.registerCommand(ReplayCode.Teleport, teleport);
this.replaySystem.registerCommand( replay.registerCommand(ReplayCode.UseItem, useItem);
ReplayCommandCode.Teleport, replay.registerCommand(ReplayCode.Equip, equip);
new ReplayTeleportCommand(this) replay.registerCommand(ReplayCode.Unequip, unequip);
);
this.replaySystem.registerCommand(
ReplayCommandCode.UseItem,
new ReplayUseItemCommand(this)
);
this.replaySystem.registerCommand(
ReplayCommandCode.Equip,
new ReplayEquipCommand(this)
);
this.replaySystem.registerCommand(
ReplayCommandCode.Unequip,
new ReplayUnequipCommand(this)
);
} }
/** /**

View File

@ -8,30 +8,30 @@ import {
endReplaySafetyCollection, endReplaySafetyCollection,
logReplaySafetyDetail, logReplaySafetyDetail,
shouldReplay, shouldReplay,
ReplayCommandCode ReplayCode
} from '@user/data-common'; } from '@user/data-common';
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import { describe, expect, it, vi } from 'vitest'; import { describe, expect, it, vi } from 'vitest';
import { createCoreState } from '../core'; import { createCoreState } from '../core';
import { ReplaySystem } from '../../../data-common/src/replay/system'; import { ReplaySystem } from '../../../data-common/src/replay/system';
import { import {
ReplayEquipCommand, ReplayEquip,
ReplayMoveCommand, ReplayMove,
ReplayTeleportCommand, ReplayTeleport,
ReplayUnequipCommand, ReplayUnequip,
ReplayUseItemCommand ReplayUseItem
} from './commands'; } from './commands';
/** 供测试读取的稳定指令码顺序 */ /** 供测试读取的稳定指令码顺序 */
export const REPLAY_COMMAND_ORDER: readonly ReplayCommandCode[] = [ export const REPLAY_COMMAND_ORDER: readonly ReplayCode[] = [
ReplayCommandCode.Up, ReplayCode.Up,
ReplayCommandCode.Right, ReplayCode.Right,
ReplayCommandCode.Down, ReplayCode.Down,
ReplayCommandCode.Left, ReplayCode.Left,
ReplayCommandCode.Teleport, ReplayCode.Teleport,
ReplayCommandCode.UseItem, ReplayCode.UseItem,
ReplayCommandCode.Equip, ReplayCode.Equip,
ReplayCommandCode.Unequip ReplayCode.Unequip
]; ];
function step( function step(
@ -61,14 +61,14 @@ describe('replay commands', () => {
it('registers the eight stable commands in order on every CoreState', () => { it('registers the eight stable commands in order on every CoreState', () => {
const state = createCoreState(); const state = createCoreState();
expect(REPLAY_COMMAND_ORDER).toEqual([ expect(REPLAY_COMMAND_ORDER).toEqual([
ReplayCommandCode.Up, ReplayCode.Up,
ReplayCommandCode.Right, ReplayCode.Right,
ReplayCommandCode.Down, ReplayCode.Down,
ReplayCommandCode.Left, ReplayCode.Left,
ReplayCommandCode.Teleport, ReplayCode.Teleport,
ReplayCommandCode.UseItem, ReplayCode.UseItem,
ReplayCommandCode.Equip, ReplayCode.Equip,
ReplayCommandCode.Unequip ReplayCode.Unequip
]); ]);
expect( expect(
REPLAY_COMMAND_ORDER.every(code => REPLAY_COMMAND_ORDER.every(code =>
@ -113,11 +113,11 @@ describe('replay commands', () => {
const start = vi const start = vi
.spyOn(mover, 'start') .spyOn(mover, 'start')
.mockReturnValueOnce(controller(first.promise)); .mockReturnValueOnce(controller(first.promise));
const command = new ReplayMoveCommand(state, FaceDirection.Right); const command = new ReplayMove(state, FaceDirection.Right);
await expect( await expect(command.execute(step(ReplayCode.Right, []))).resolves.toBe(
command.execute(step(ReplayCommandCode.Right, [])) true
).resolves.toBe(true); );
expect(move).toHaveBeenCalledWith(FaceDirection.Right); expect(move).toHaveBeenCalledWith(FaceDirection.Right);
expect(start).not.toHaveBeenCalled(); expect(start).not.toHaveBeenCalled();
@ -139,10 +139,10 @@ describe('replay commands', () => {
const error = vi.spyOn(logger, 'error'); const error = vi.spyOn(logger, 'error');
const mover = state.hero.location.mover; const mover = state.hero.location.mover;
(mover as unknown as { moving: boolean }).moving = true; (mover as unknown as { moving: boolean }).moving = true;
const command = new ReplayMoveCommand(state, FaceDirection.Up); const command = new ReplayMove(state, FaceDirection.Up);
await expect( await expect(command.execute(step(ReplayCode.Up, []))).resolves.toBe(
command.execute(step(ReplayCommandCode.Up, [])) false
).resolves.toBe(false); );
expect(error).toHaveBeenCalledWith(2003); expect(error).toHaveBeenCalledWith(2003);
(mover as unknown as { moving: boolean }).moving = false; (mover as unknown as { moving: boolean }).moving = false;
@ -162,11 +162,11 @@ describe('replay commands', () => {
controller: controller(first.promise), controller: controller(first.promise),
path: [] path: []
}); });
const command = new ReplayTeleportCommand(state); const command = new ReplayTeleport(state);
let result: boolean | undefined; let result: boolean | undefined;
const pending = command const pending = command
.execute(step(ReplayCommandCode.Teleport, [2, 3])) .execute(step(ReplayCode.Teleport, [2, 3]))
.then(value => { .then(value => {
result = value; result = value;
}); });
@ -180,7 +180,7 @@ describe('replay commands', () => {
const error = vi.spyOn(logger, 'error'); const error = vi.spyOn(logger, 'error');
teleport.mockReturnValueOnce(null); teleport.mockReturnValueOnce(null);
await expect( await expect(
command.execute(step(ReplayCommandCode.Teleport, [4, 5])) command.execute(step(ReplayCode.Teleport, [4, 5]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(error).toHaveBeenCalledWith(2005, '4', '5'); expect(error).toHaveBeenCalledWith(2005, '4', '5');
error.mockRestore(); error.mockRestore();
@ -194,13 +194,13 @@ describe('replay commands', () => {
.spyOn(state.hero.items, 'useItem') .spyOn(state.hero.items, 'useItem')
.mockReturnValueOnce(true) .mockReturnValueOnce(true)
.mockReturnValueOnce(false); .mockReturnValueOnce(false);
const command = new ReplayUseItemCommand(state); const command = new ReplayUseItem(state);
await expect( await expect(
command.execute(step(ReplayCommandCode.UseItem, [12])) command.execute(step(ReplayCode.UseItem, [12]))
).resolves.toBe(true); ).resolves.toBe(true);
await expect( await expect(
command.execute(step(ReplayCommandCode.UseItem, [34])) command.execute(step(ReplayCode.UseItem, [34]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(useItem).toHaveBeenNthCalledWith(1, 12); expect(useItem).toHaveBeenNthCalledWith(1, 12);
expect(error).toHaveBeenCalledWith(2006, '34'); expect(error).toHaveBeenCalledWith(2006, '34');
@ -217,19 +217,19 @@ describe('replay commands', () => {
const equip = vi const equip = vi
.spyOn(equipment, 'equip') .spyOn(equipment, 'equip')
.mockImplementation(() => undefined); .mockImplementation(() => undefined);
const command = new ReplayEquipCommand(state); const command = new ReplayEquip(state);
// 装备后槽位为指定 uid // 装备后槽位为指定 uid
getEquipped.mockReturnValueOnce(99); getEquipped.mockReturnValueOnce(99);
await expect( await expect(
command.execute(step(ReplayCommandCode.Equip, [99, 0, true])) command.execute(step(ReplayCode.Equip, [99, 0, true]))
).resolves.toBe(true); ).resolves.toBe(true);
expect(equip).toHaveBeenCalledWith(99, 0, true); expect(equip).toHaveBeenCalledWith(99, 0, true);
// 装备后槽位未变为指定 uid // 装备后槽位未变为指定 uid
getEquipped.mockReturnValueOnce(undefined); getEquipped.mockReturnValueOnce(undefined);
await expect( await expect(
command.execute(step(ReplayCommandCode.Equip, [99, 1, false])) command.execute(step(ReplayCode.Equip, [99, 1, false]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(error).toHaveBeenCalledWith(2007, '99', '1'); expect(error).toHaveBeenCalledWith(2007, '99', '1');
@ -245,19 +245,19 @@ describe('replay commands', () => {
const unequip = vi const unequip = vi
.spyOn(equipment, 'unequip') .spyOn(equipment, 'unequip')
.mockImplementation(() => undefined); .mockImplementation(() => undefined);
const command = new ReplayUnequipCommand(state); const command = new ReplayUnequip(state);
// 卸下后槽位为空 // 卸下后槽位为空
getEquipped.mockReturnValueOnce(undefined); getEquipped.mockReturnValueOnce(undefined);
await expect( await expect(
command.execute(step(ReplayCommandCode.Unequip, [0])) command.execute(step(ReplayCode.Unequip, [0]))
).resolves.toBe(true); ).resolves.toBe(true);
expect(unequip).toHaveBeenCalledWith(0); expect(unequip).toHaveBeenCalledWith(0);
// 卸下后槽位仍有装备 // 卸下后槽位仍有装备
getEquipped.mockReturnValueOnce(88); getEquipped.mockReturnValueOnce(88);
await expect( await expect(
command.execute(step(ReplayCommandCode.Unequip, [1])) command.execute(step(ReplayCode.Unequip, [1]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(error).toHaveBeenCalledWith(2008, '1'); expect(error).toHaveBeenCalledWith(2008, '1');
@ -277,8 +277,8 @@ describe('replay commands', () => {
.spyOn(state.hero.items, 'useItem') .spyOn(state.hero.items, 'useItem')
.mockReturnValueOnce(true); .mockReturnValueOnce(true);
const replay = state.replaySystem; const replay = state.replaySystem;
replay.record(ReplayCommandCode.Right); replay.record(ReplayCode.Right);
replay.record(ReplayCommandCode.UseItem, 5); replay.record(ReplayCode.UseItem, 5);
const sandbox = replay.createReplaySandbox({ const sandbox = replay.createReplaySandbox({
route: replay.route, route: replay.route,
reseter: { reset: () => {} } reseter: { reset: () => {} }
@ -302,20 +302,20 @@ describe('replay commands', () => {
// 验证参数数量或类型不符的指令以 false 结束 // 验证参数数量或类型不符的指令以 false 结束
it('returns false for invalid command parameters', async () => { it('returns false for invalid command parameters', async () => {
const state = createCoreState(); const state = createCoreState();
const move = new ReplayMoveCommand(state, FaceDirection.Up); const move = new ReplayMove(state, FaceDirection.Up);
const teleport = new ReplayTeleportCommand(state); const teleport = new ReplayTeleport(state);
const useItem = new ReplayUseItemCommand(state); const useItem = new ReplayUseItem(state);
const equip = new ReplayEquipCommand(state); const equip = new ReplayEquip(state);
const unequip = new ReplayUnequipCommand(state); const unequip = new ReplayUnequip(state);
const invalid = [ const invalid = [
move.execute(step(ReplayCommandCode.Up, [1])), move.execute(step(ReplayCode.Up, [1])),
teleport.execute(step(ReplayCommandCode.Teleport, ['x', 1])), teleport.execute(step(ReplayCode.Teleport, ['x', 1])),
teleport.execute(step(ReplayCommandCode.Teleport, [1])), teleport.execute(step(ReplayCode.Teleport, [1])),
useItem.execute(step(ReplayCommandCode.UseItem, [])), useItem.execute(step(ReplayCode.UseItem, [])),
useItem.execute(step(ReplayCommandCode.UseItem, ['id'])), useItem.execute(step(ReplayCode.UseItem, ['id'])),
equip.execute(step(ReplayCommandCode.Equip, [1, 0])), equip.execute(step(ReplayCode.Equip, [1, 0])),
equip.execute(step(ReplayCommandCode.Equip, [1, 0, 'x'])), equip.execute(step(ReplayCode.Equip, [1, 0, 'x'])),
unequip.execute(step(ReplayCommandCode.Unequip, ['slot'])) unequip.execute(step(ReplayCode.Unequip, ['slot']))
]; ];
await expect(Promise.all(invalid)).resolves.toEqual([ await expect(Promise.all(invalid)).resolves.toEqual([
false, false,

View File

@ -70,10 +70,7 @@ export abstract class BaseReplayCommand implements IReplayCommand {
//#region 移动指令 //#region 移动指令
export class ReplayMoveCommand export class ReplayMove extends BaseReplayCommand implements IReplayCommand {
extends BaseReplayCommand
implements IReplayCommand
{
protected readonly name: string = 'move'; protected readonly name: string = 'move';
protected readonly paramTypes: readonly string[] = []; protected readonly paramTypes: readonly string[] = [];
@ -112,7 +109,7 @@ export class ReplayMoveCommand
//#region 瞬移指令 //#region 瞬移指令
export class ReplayTeleportCommand export class ReplayTeleport
extends BaseReplayCommand extends BaseReplayCommand
implements IReplayCommand implements IReplayCommand
{ {
@ -136,10 +133,7 @@ export class ReplayTeleportCommand
//#region 使用物品指令 //#region 使用物品指令
export class ReplayUseItemCommand export class ReplayUseItem extends BaseReplayCommand implements IReplayCommand {
extends BaseReplayCommand
implements IReplayCommand
{
protected readonly name: string = 'use-item'; protected readonly name: string = 'use-item';
protected readonly paramTypes: readonly string[] = ['number']; protected readonly paramTypes: readonly string[] = ['number'];
@ -158,10 +152,7 @@ export class ReplayUseItemCommand
//#region 装备指令 //#region 装备指令
export class ReplayEquipCommand export class ReplayEquip extends BaseReplayCommand implements IReplayCommand {
extends BaseReplayCommand
implements IReplayCommand
{
protected readonly name: string = 'equip'; protected readonly name: string = 'equip';
protected readonly paramTypes: readonly string[] = [ protected readonly paramTypes: readonly string[] = [
'number', 'number',
@ -190,10 +181,7 @@ export class ReplayEquipCommand
//#region 卸下装备指令 //#region 卸下装备指令
export class ReplayUnequipCommand export class ReplayUnequip extends BaseReplayCommand implements IReplayCommand {
extends BaseReplayCommand
implements IReplayCommand
{
protected readonly name: string = 'unequip'; protected readonly name: string = 'unequip';
protected readonly paramTypes: readonly string[] = ['number']; protected readonly paramTypes: readonly string[] = ['number'];

View File

@ -7,7 +7,7 @@ import {
ItemCategory, ItemCategory,
ReplaySystem, ReplaySystem,
SaveCompression, SaveCompression,
ReplayCommandCode, ReplayCode,
TileType TileType
} from '@user/data-common'; } from '@user/data-common';
import { IEnemy, IReadonlyHeroAttribute } from '@user/data-base'; import { IEnemy, IReadonlyHeroAttribute } from '@user/data-base';
@ -17,15 +17,15 @@ import { MainDamageCalculator } from '../src/enemy/calculator';
import { createClosedLoopFixture } from './fixtures/closed-loop'; import { createClosedLoopFixture } from './fixtures/closed-loop';
/** 供测试读取的稳定指令码顺序 */ /** 供测试读取的稳定指令码顺序 */
export const REPLAY_COMMAND_ORDER: readonly ReplayCommandCode[] = [ export const REPLAY_COMMAND_ORDER: readonly ReplayCode[] = [
ReplayCommandCode.Up, ReplayCode.Up,
ReplayCommandCode.Right, ReplayCode.Right,
ReplayCommandCode.Down, ReplayCode.Down,
ReplayCommandCode.Left, ReplayCode.Left,
ReplayCommandCode.Teleport, ReplayCode.Teleport,
ReplayCommandCode.UseItem, ReplayCode.UseItem,
ReplayCommandCode.Equip, ReplayCode.Equip,
ReplayCommandCode.Unequip ReplayCode.Unequip
]; ];
vi.hoisted(() => { vi.hoisted(() => {
@ -258,19 +258,19 @@ describe('DATA-01 closure', () => {
) )
).toBe(true); ).toBe(true);
expect(REPLAY_COMMAND_ORDER).toEqual([ expect(REPLAY_COMMAND_ORDER).toEqual([
ReplayCommandCode.Up, ReplayCode.Up,
ReplayCommandCode.Right, ReplayCode.Right,
ReplayCommandCode.Down, ReplayCode.Down,
ReplayCommandCode.Left, ReplayCode.Left,
ReplayCommandCode.Teleport, ReplayCode.Teleport,
ReplayCommandCode.UseItem, ReplayCode.UseItem,
ReplayCommandCode.Equip, ReplayCode.Equip,
ReplayCommandCode.Unequip ReplayCode.Unequip
]); ]);
state.replaySystem.record(ReplayCommandCode.Right, 4, 'route'); state.replaySystem.record(ReplayCode.Right, 4, 'route');
expect(state.replaySystem.route.get(0)).toMatchObject({ expect(state.replaySystem.route.get(0)).toMatchObject({
command: ReplayCommandCode.Right, command: ReplayCode.Right,
params: [4, 'route'] params: [4, 'route']
}); });

View File

@ -8,7 +8,7 @@ import {
IReplayArray, IReplayArray,
IEnemyAttr, IEnemyAttr,
IHeroAttr, IHeroAttr,
ReplayCommandCode, ReplayCode,
SaveCompression, SaveCompression,
TileType TileType
} from '@user/data-common'; } from '@user/data-common';
@ -168,7 +168,7 @@ export function createClosedLoopFixture(): IClosedLoopFixture {
state.hero.location.mover.setFaceDir(FaceDirection.Right); state.hero.location.mover.setFaceDir(FaceDirection.Right);
const replay = state.replaySystem; const replay = state.replaySystem;
replay.record(ReplayCommandCode.Right); replay.record(ReplayCode.Right);
const route = replay.route; const route = replay.route;
const initialState: IClosedLoopInitialState = { const initialState: IClosedLoopInitialState = {

View File

@ -3,7 +3,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { import {
FaceDirection, FaceDirection,
ReplayCommandCode, ReplayCode,
SaveCompression, SaveCompression,
TileType, TileType,
type IReplayArray, type IReplayArray,
@ -14,11 +14,11 @@ import {
import { type CoreState, createCoreState } from '../src/core'; import { type CoreState, createCoreState } from '../src/core';
import { DefaultPassPredicateImpl } from '../src/hero/predicate'; import { DefaultPassPredicateImpl } from '../src/hero/predicate';
import { import {
ReplayEquipCommand, ReplayEquip,
ReplayMoveCommand, ReplayMove,
ReplayTeleportCommand, ReplayTeleport,
ReplayUnequipCommand, ReplayUnequip,
ReplayUseItemCommand ReplayUseItem
} from '../src/replay/commands'; } from '../src/replay/commands';
vi.hoisted(() => { vi.hoisted(() => {
@ -235,16 +235,16 @@ describe('replay recording and route read-back', () => {
const replay = state.replaySystem; const replay = state.replaySystem;
replay.disable(); replay.disable();
replay.record(ReplayCommandCode.Right); replay.record(ReplayCode.Right);
expect(replay.route.length).toBe(0); expect(replay.route.length).toBe(0);
replay.disable(); replay.disable();
replay.revert(); replay.revert();
replay.record(ReplayCommandCode.Right); replay.record(ReplayCode.Right);
expect(replay.route.length).toBe(0); expect(replay.route.length).toBe(0);
replay.revert(); replay.revert();
replay.record(ReplayCommandCode.Right); replay.record(ReplayCode.Right);
expect(replay.route.length).toBe(1); expect(replay.route.length).toBe(1);
}); });
@ -252,31 +252,31 @@ describe('replay recording and route read-back', () => {
it('reads back every stable command code and its params', () => { it('reads back every stable command code and its params', () => {
const state = createCoreState(); const state = createCoreState();
const replay = state.replaySystem; const replay = state.replaySystem;
replay.record(ReplayCommandCode.Up); replay.record(ReplayCode.Up);
replay.record(ReplayCommandCode.Teleport, 3, 4); replay.record(ReplayCode.Teleport, 3, 4);
replay.record(ReplayCommandCode.UseItem, 12); replay.record(ReplayCode.UseItem, 12);
replay.record(ReplayCommandCode.Equip, 99, 1, true); replay.record(ReplayCode.Equip, 99, 1, true);
replay.record(ReplayCommandCode.Unequip, 1); replay.record(ReplayCode.Unequip, 1);
expect(replay.route.length).toBe(5); expect(replay.route.length).toBe(5);
expect(replay.route.get(0)).toMatchObject({ expect(replay.route.get(0)).toMatchObject({
command: ReplayCommandCode.Up, command: ReplayCode.Up,
params: [] params: []
}); });
expect(replay.route.get(1)).toMatchObject({ expect(replay.route.get(1)).toMatchObject({
command: ReplayCommandCode.Teleport, command: ReplayCode.Teleport,
params: [3, 4] params: [3, 4]
}); });
expect(replay.route.get(2)).toMatchObject({ expect(replay.route.get(2)).toMatchObject({
command: ReplayCommandCode.UseItem, command: ReplayCode.UseItem,
params: [12] params: [12]
}); });
expect(replay.route.get(3)).toMatchObject({ expect(replay.route.get(3)).toMatchObject({
command: ReplayCommandCode.Equip, command: ReplayCode.Equip,
params: [99, 1, true] params: [99, 1, true]
}); });
expect(replay.route.get(4)).toMatchObject({ expect(replay.route.get(4)).toMatchObject({
command: ReplayCommandCode.Unequip, command: ReplayCode.Unequip,
params: [1] params: [1]
}); });
}); });
@ -292,7 +292,7 @@ describe('replay recording and route read-back', () => {
expect(state.replaySystem.route.length).toBe(1); expect(state.replaySystem.route.length).toBe(1);
expect(state.replaySystem.route.get(0)).toMatchObject({ expect(state.replaySystem.route.get(0)).toMatchObject({
command: ReplayCommandCode.Right, command: ReplayCode.Right,
params: [] params: []
}); });
}); });
@ -322,18 +322,18 @@ describe('small-map replay playback and second recording', () => {
await runHeroStep(state, FaceDirection.Right); await runHeroStep(state, FaceDirection.Right);
await runHeroStep(state, FaceDirection.Right); await runHeroStep(state, FaceDirection.Right);
replay.record(ReplayCommandCode.Teleport, 1, 0); replay.record(ReplayCode.Teleport, 1, 0);
replay.record(ReplayCommandCode.Up); replay.record(ReplayCode.Up);
const firstSteps = snapshotRoute(replay.route); const firstSteps = snapshotRoute(replay.route);
expect(firstSteps.map(item => item.code)).toEqual([ expect(firstSteps.map(item => item.code)).toEqual([
ReplayCommandCode.Right, ReplayCode.Right,
ReplayCommandCode.Right, ReplayCode.Right,
ReplayCommandCode.Teleport, ReplayCode.Teleport,
ReplayCommandCode.Up ReplayCode.Up
]); ]);
expect(replay.route.get(2)).toMatchObject({ expect(replay.route.get(2)).toMatchObject({
command: ReplayCommandCode.Teleport, command: ReplayCode.Teleport,
params: [1, 0] params: [1, 0]
}); });
@ -347,8 +347,8 @@ describe('small-map replay playback and second recording', () => {
await runHeroStep(state, FaceDirection.Right); await runHeroStep(state, FaceDirection.Right);
await runHeroStep(state, FaceDirection.Right); await runHeroStep(state, FaceDirection.Right);
replay.record(ReplayCommandCode.Teleport, 1, 0); replay.record(ReplayCode.Teleport, 1, 0);
replay.record(ReplayCommandCode.Up); replay.record(ReplayCode.Up);
const secondSteps = snapshotRoute(replay.route); const secondSteps = snapshotRoute(replay.route);
expectReplayEqual(secondSteps, firstSteps); expectReplayEqual(secondSteps, firstSteps);
@ -368,7 +368,7 @@ describe('small-map replay playback and second recording', () => {
); );
state.pathfinding.finder.useMapLayer(map.eventLayer); state.pathfinding.finder.useMapLayer(map.eventLayer);
await runHeroStep(state, FaceDirection.Right); await runHeroStep(state, FaceDirection.Right);
replay.record(ReplayCommandCode.Teleport, 2, 0); replay.record(ReplayCode.Teleport, 2, 0);
await playRoute(state); await playRoute(state);
@ -382,16 +382,16 @@ describe('replay playback error codes 2001-2008', () => {
it('warns 2001 and 2002 for parameter count and type mismatches', async () => { it('warns 2001 and 2002 for parameter count and type mismatches', async () => {
const state = createCoreState(); const state = createCoreState();
const error = vi.spyOn(logger, 'error'); const error = vi.spyOn(logger, 'error');
const move = new ReplayMoveCommand(state, FaceDirection.Up); const move = new ReplayMove(state, FaceDirection.Up);
const teleport = new ReplayTeleportCommand(state); const teleport = new ReplayTeleport(state);
await expect( await expect(move.execute(step(ReplayCode.Up, [1]))).resolves.toBe(
move.execute(step(ReplayCommandCode.Up, [1])) false
).resolves.toBe(false); );
expect(error).toHaveBeenCalledWith(2001, 'move', '0', '1'); expect(error).toHaveBeenCalledWith(2001, 'move', '0', '1');
await expect( await expect(
teleport.execute(step(ReplayCommandCode.Teleport, ['x', 1])) teleport.execute(step(ReplayCode.Teleport, ['x', 1]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(error).toHaveBeenCalledWith( expect(error).toHaveBeenCalledWith(
2002, 2002,
@ -407,12 +407,12 @@ describe('replay playback error codes 2001-2008', () => {
const state = createCoreState(); const state = createCoreState();
const error = vi.spyOn(logger, 'error'); const error = vi.spyOn(logger, 'error');
const mover = state.hero.location.mover; const mover = state.hero.location.mover;
const move = new ReplayMoveCommand(state, FaceDirection.Up); const move = new ReplayMove(state, FaceDirection.Up);
(mover as unknown as { moving: boolean }).moving = true; (mover as unknown as { moving: boolean }).moving = true;
await expect( await expect(move.execute(step(ReplayCode.Up, []))).resolves.toBe(
move.execute(step(ReplayCommandCode.Up, [])) false
).resolves.toBe(false); );
expect(error).toHaveBeenCalledWith(2003); expect(error).toHaveBeenCalledWith(2003);
(mover as unknown as { moving: boolean }).moving = false; (mover as unknown as { moving: boolean }).moving = false;
@ -426,10 +426,10 @@ describe('replay playback error codes 2001-2008', () => {
const state = createCoreState(); const state = createCoreState();
const error = vi.spyOn(logger, 'error'); const error = vi.spyOn(logger, 'error');
vi.spyOn(state.pathfinding, 'teleportTo').mockReturnValueOnce(null); vi.spyOn(state.pathfinding, 'teleportTo').mockReturnValueOnce(null);
const teleport = new ReplayTeleportCommand(state); const teleport = new ReplayTeleport(state);
await expect( await expect(
teleport.execute(step(ReplayCommandCode.Teleport, [4, 5])) teleport.execute(step(ReplayCode.Teleport, [4, 5]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(error).toHaveBeenCalledWith(2005, '4', '5'); expect(error).toHaveBeenCalledWith(2005, '4', '5');
}); });
@ -439,10 +439,10 @@ describe('replay playback error codes 2001-2008', () => {
const state = createCoreState(); const state = createCoreState();
const error = vi.spyOn(logger, 'error'); const error = vi.spyOn(logger, 'error');
vi.spyOn(state.hero.items, 'useItem').mockReturnValueOnce(false); vi.spyOn(state.hero.items, 'useItem').mockReturnValueOnce(false);
const useItem = new ReplayUseItemCommand(state); const useItem = new ReplayUseItem(state);
await expect( await expect(
useItem.execute(step(ReplayCommandCode.UseItem, [34])) useItem.execute(step(ReplayCode.UseItem, [34]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(error).toHaveBeenCalledWith(2006, '34'); expect(error).toHaveBeenCalledWith(2006, '34');
}); });
@ -454,10 +454,10 @@ describe('replay playback error codes 2001-2008', () => {
const equipment = state.hero.equip; const equipment = state.hero.equip;
vi.spyOn(equipment, 'equip').mockImplementation(() => undefined); vi.spyOn(equipment, 'equip').mockImplementation(() => undefined);
vi.spyOn(equipment, 'getEquipped').mockReturnValueOnce(undefined); vi.spyOn(equipment, 'getEquipped').mockReturnValueOnce(undefined);
const equip = new ReplayEquipCommand(state); const equip = new ReplayEquip(state);
await expect( await expect(
equip.execute(step(ReplayCommandCode.Equip, [99, 1, false])) equip.execute(step(ReplayCode.Equip, [99, 1, false]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(error).toHaveBeenCalledWith(2007, '99', '1'); expect(error).toHaveBeenCalledWith(2007, '99', '1');
}); });
@ -469,10 +469,10 @@ describe('replay playback error codes 2001-2008', () => {
const equipment = state.hero.equip; const equipment = state.hero.equip;
vi.spyOn(equipment, 'unequip').mockImplementation(() => undefined); vi.spyOn(equipment, 'unequip').mockImplementation(() => undefined);
vi.spyOn(equipment, 'getEquipped').mockReturnValueOnce(88); vi.spyOn(equipment, 'getEquipped').mockReturnValueOnce(88);
const unequip = new ReplayUnequipCommand(state); const unequip = new ReplayUnequip(state);
await expect( await expect(
unequip.execute(step(ReplayCommandCode.Unequip, [1])) unequip.execute(step(ReplayCode.Unequip, [1]))
).resolves.toBe(false); ).resolves.toBe(false);
expect(error).toHaveBeenCalledWith(2008, '1'); expect(error).toHaveBeenCalledWith(2008, '1');
}); });

View File

@ -1,9 +1,5 @@
import { ITileLocator, logger } from '@motajs/common'; import { ITileLocator, logger } from '@motajs/common';
import { import { IObjectMovable, IObjectMover, ReplayCode } from '@user/data-common';
IObjectMovable,
IObjectMover,
ReplayCommandCode
} from '@user/data-common';
import { HeroMover, IStateBase } from '@user/data-base'; import { HeroMover, IStateBase } from '@user/data-base';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { PathfindingFinder } from './finder'; import { PathfindingFinder } from './finder';
@ -103,10 +99,7 @@ export class PathfindingSystem implements IPathfindingSystem {
// TODO:后续需要调整设计方式,最好不用 instanceof // TODO:后续需要调整设计方式,最好不用 instanceof
if (this.mover instanceof HeroMover) { if (this.mover instanceof HeroMover) {
const replay = this.state.replaySystem; const replay = this.state.replaySystem;
replay.route.add(ReplayCommandCode.Teleport, [ replay.route.add(ReplayCode.Teleport, [target.x, target.y]);
target.x,
target.y
]);
} }
return this.startMove(path, true); return this.startMove(path, true);
} }