mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-02-28 17:37:07 +08:00
refactor: 勇士移动
This commit is contained in:
parent
b9ae439cd9
commit
3de6aac6a1
@ -72,6 +72,7 @@ import { RenderAdapter } from './render/adapter';
|
||||
import { getMainRenderer } from './render';
|
||||
import { Layer } from './render/preset/layer';
|
||||
import { LayerGroupFloorBinder } from './render/preset/floor';
|
||||
import { HeroKeyMover } from './main/action/move';
|
||||
|
||||
// ----- 类注册
|
||||
Mota.register('class', 'AudioPlayer', AudioPlayer);
|
||||
@ -162,6 +163,9 @@ Mota.register('module', 'Render', {
|
||||
Layer,
|
||||
LayerGroupFloorBinder
|
||||
});
|
||||
Mota.register('module', 'Action', {
|
||||
HeroKeyMover
|
||||
});
|
||||
|
||||
main.renderLoaded = true;
|
||||
Mota.require('var', 'hook').emit('renderLoaded');
|
||||
|
159
src/core/main/action/move.ts
Normal file
159
src/core/main/action/move.ts
Normal file
@ -0,0 +1,159 @@
|
||||
import { KeyCode } from '@/plugin/keyCodes';
|
||||
import { Hotkey, HotkeyData } from '../custom/hotkey';
|
||||
import type { HeroMover, IMoveController } from '@/game/state/move';
|
||||
import { Ticker } from 'mutate-animate';
|
||||
import { mainScope } from '../init/hotkey';
|
||||
|
||||
type MoveKey = Record<Dir, HotkeyData>;
|
||||
type MoveKeyConfig = Record<Dir, string>;
|
||||
|
||||
export class HeroKeyMover {
|
||||
/** 当前按下的键 */
|
||||
private pressedKey: Set<Dir> = new Set();
|
||||
/** 当前的移动方向 */
|
||||
private moveDir: Dir = 'down';
|
||||
/** 当前是否正在使用按键移动 */
|
||||
private moving: boolean = false;
|
||||
/** 当前移动的控制器 */
|
||||
private controller?: IMoveController;
|
||||
|
||||
/** 按键接续ticker */
|
||||
private ticker = new Ticker();
|
||||
|
||||
/** 当前移动实例绑定的热键 */
|
||||
hotkey: Hotkey;
|
||||
/** 当前热键的移动按键信息 */
|
||||
hotkeyData: MoveKey;
|
||||
/** 移动实例 */
|
||||
mover: HeroMover;
|
||||
/** 移动可触发的作用域 */
|
||||
scope: symbol = mainScope;
|
||||
|
||||
constructor(hotkey: Hotkey, mover: HeroMover, config?: MoveKeyConfig) {
|
||||
this.hotkey = hotkey;
|
||||
this.mover = mover;
|
||||
hotkey.on('press', this.onPressKey);
|
||||
hotkey.on('release', this.onReleaseKey);
|
||||
|
||||
const data = hotkey.data;
|
||||
|
||||
this.hotkeyData = {
|
||||
left: data[config?.left ?? 'moveLeft'],
|
||||
right: data[config?.right ?? 'moveRight'],
|
||||
up: data[config?.up ?? 'moveUp'],
|
||||
down: data[config?.down ?? 'moveDown']
|
||||
};
|
||||
|
||||
this.ticker.add(() => {
|
||||
if (!this.moving) {
|
||||
if (this.pressedKey.size > 0) {
|
||||
const dir = [...this.pressedKey].at(-1);
|
||||
if (!dir) return;
|
||||
this.moveDir = dir;
|
||||
this.tryStartMove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onPressKey = (code: KeyCode) => {
|
||||
if (code === this.hotkeyData.left.key) this.press('left');
|
||||
else if (code === this.hotkeyData.right.key) this.press('right');
|
||||
else if (code === this.hotkeyData.up.key) this.press('up');
|
||||
else if (code === this.hotkeyData.down.key) this.press('down');
|
||||
};
|
||||
|
||||
private onReleaseKey = (code: KeyCode) => {
|
||||
if (code === this.hotkeyData.left.key) this.release('left');
|
||||
else if (code === this.hotkeyData.right.key) this.release('right');
|
||||
else if (code === this.hotkeyData.up.key) this.release('up');
|
||||
else if (code === this.hotkeyData.down.key) this.release('down');
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置按键触发作用域
|
||||
*/
|
||||
setScope(scope: symbol) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按下某个方向键
|
||||
* @param dir 移动方向
|
||||
*/
|
||||
press(dir: Dir) {
|
||||
if (this.hotkey.scope !== this.scope || core.status.lockControl) return;
|
||||
this.pressedKey.add(dir);
|
||||
this.moveDir = dir;
|
||||
if (!this.moving) {
|
||||
this.tryStartMove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 松开方向键
|
||||
* @param dir 移动方向
|
||||
*/
|
||||
release(dir: Dir) {
|
||||
this.pressedKey.delete(dir);
|
||||
if (this.pressedKey.size > 0) {
|
||||
this.moveDir = [...this.pressedKey][0];
|
||||
} else {
|
||||
this.endMove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试开始移动
|
||||
* @returns 是否成功开始移动
|
||||
*/
|
||||
tryStartMove() {
|
||||
if (this.moving || core.status.lockControl) return false;
|
||||
|
||||
this.mover.oneStep(this.moveDir);
|
||||
const controller = this.mover.startMove();
|
||||
if (!controller) return;
|
||||
|
||||
this.controller = controller;
|
||||
controller.onEnd.then(() => {
|
||||
this.moving = false;
|
||||
this.controller = void 0;
|
||||
this.mover.off('stepEnd', this.onStepEnd);
|
||||
});
|
||||
this.moving = true;
|
||||
|
||||
this.mover.on('stepEnd', this.onStepEnd);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止本次按键移动
|
||||
*/
|
||||
endMove() {
|
||||
this.controller?.stop();
|
||||
}
|
||||
|
||||
private onStepEnd = () => {
|
||||
const con = this.controller;
|
||||
if (!con) return;
|
||||
if (!this.moving) {
|
||||
con.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.pressedKey.size > 0) {
|
||||
if (con.queue.length === 0) {
|
||||
con.push({ type: 'dir', value: this.moveDir });
|
||||
}
|
||||
} else {
|
||||
con.stop();
|
||||
}
|
||||
};
|
||||
|
||||
destroy() {
|
||||
this.hotkey.off('press', this.onPressKey);
|
||||
this.hotkey.off('release', this.onReleaseKey);
|
||||
this.mover.off('stepEnd', this.onStepEnd);
|
||||
this.ticker.destroy();
|
||||
}
|
||||
}
|
@ -251,7 +251,7 @@ export class Danmaku extends EventEmitter<DanmakuEvent> {
|
||||
} else {
|
||||
logger.error(
|
||||
8,
|
||||
`Post not allowed css danmaku. Allow info: ${allow.join(',')}`
|
||||
`Post danmaku with not allowed css. Info: ${allow.join(',')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ interface RegisterHotkeyData extends Partial<AssistHoykey> {
|
||||
defaults: KeyCode;
|
||||
}
|
||||
|
||||
interface HotkeyData extends Required<RegisterHotkeyData> {
|
||||
export interface HotkeyData extends Required<RegisterHotkeyData> {
|
||||
key: KeyCode;
|
||||
emits: Map<symbol, HotkeyEmitData>;
|
||||
group?: string;
|
||||
|
@ -5,10 +5,10 @@ import { logger } from '@/core/common/logger';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { texture } from '../cache';
|
||||
import { TimingFn } from 'mutate-animate';
|
||||
import { backDir } from '@/plugin/game/utils';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
||||
type HeroMovingStatus = 'stop' | 'moving' | 'moving-as';
|
||||
// type HeroMovingStatus = 'stop' | 'moving' | 'moving-as';
|
||||
// export const enum HeroMovingStatus {}
|
||||
|
||||
interface HeroRenderEvent {
|
||||
stepEnd: [];
|
||||
@ -31,25 +31,27 @@ export class HeroRenderer
|
||||
renderable?: LayerMovingRenderable;
|
||||
layer!: Layer;
|
||||
|
||||
// hero?: Hero;
|
||||
|
||||
/** 勇士移动状态 */
|
||||
status: HeroMovingStatus = 'stop';
|
||||
/** 当前移动帧数 */
|
||||
movingFrame: number = 0;
|
||||
/** 是否正在移动 */
|
||||
moving: boolean = false;
|
||||
/** 是否正在播放动画,与移动分开以实现原地踏步 */
|
||||
animate: boolean = false;
|
||||
|
||||
/** 勇士移动速度 */
|
||||
speed: number = 100;
|
||||
/** 当前勇士朝向 */
|
||||
// todo: 删了这个属性
|
||||
dir: Dir = 'down';
|
||||
|
||||
/** 当前的移动方向 */
|
||||
moveDir: Dir2 = 'down';
|
||||
/** 当前移动的勇士显示方向 */
|
||||
showDir: Dir = 'down';
|
||||
/** 帧动画是否反向播放,例如后退时就应该设为true */
|
||||
animateReverse: boolean = false;
|
||||
|
||||
/** 勇士移动定时器id */
|
||||
private moveId: number = -1;
|
||||
/** 上一次帧数切换的时间 */
|
||||
private lastFrameTime: number = 0;
|
||||
/** 当前的移动方向 */
|
||||
moveDir: Move2 = 'down';
|
||||
/** 上一步走到格子上的时间 */
|
||||
private lastStepTime: number = 0;
|
||||
/** 执行当前步移动的Promise */
|
||||
@ -63,8 +65,6 @@ export class HeroRenderer
|
||||
stepDir: Dir2 = 'down';
|
||||
/** 每步的格子增量 */
|
||||
private stepDelta: Loc = { x: 0, y: 1 };
|
||||
/** 动画显示的方向,用于适配后退 */
|
||||
// private animateDir: Dir = 'down';
|
||||
|
||||
/**
|
||||
* 设置勇士所用的图片资源
|
||||
@ -76,6 +76,10 @@ export class HeroRenderer
|
||||
this.layer.update(this.layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置移动的移动速度
|
||||
* @param speed 移动速度
|
||||
*/
|
||||
setMoveSpeed(speed: number) {
|
||||
this.speed = speed;
|
||||
}
|
||||
@ -102,7 +106,7 @@ export class HeroRenderer
|
||||
zIndex: core.status.hero.loc.y,
|
||||
autotile: false,
|
||||
bigImage: true,
|
||||
render: this.getRenderFromDir(this.moveDir),
|
||||
render: this.getRenderFromDir(this.showDir),
|
||||
animate: 0
|
||||
};
|
||||
}
|
||||
@ -111,53 +115,90 @@ export class HeroRenderer
|
||||
* 根据方向获取勇士的裁切信息
|
||||
* @param dir 方向
|
||||
*/
|
||||
getRenderFromDir(dir: Move2): [number, number, number, number][] {
|
||||
getRenderFromDir(dir: Dir): [number, number, number, number][] {
|
||||
if (!this.cellWidth || !this.cellHeight) return [];
|
||||
let resolved: Dir2;
|
||||
if (dir === 'forward') resolved = this.dir;
|
||||
else if (dir === 'backward') resolved = backDir(this.dir);
|
||||
else resolved = dir;
|
||||
const index = texture.characterDirection[resolved];
|
||||
const index = texture.characterDirection[dir];
|
||||
const y = index * this.cellHeight;
|
||||
const res: [number, number, number, number][] = [0, 1, 2, 3].map(v => {
|
||||
return [v * this.cellWidth!, y, this.cellWidth!, this.cellHeight!];
|
||||
});
|
||||
if (dir === 'backward') return res.reverse();
|
||||
if (this.animateReverse) return res.reverse();
|
||||
else return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前勇士
|
||||
* @param hero 绑定的勇士
|
||||
* 开始勇士帧动画
|
||||
*/
|
||||
// setHero(hero: Hero) {
|
||||
// this.hero = hero;
|
||||
// }
|
||||
startAnimate() {
|
||||
this.animate = true;
|
||||
this.lastFrameTime = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备开始移动
|
||||
* 结束勇士帧动画
|
||||
*/
|
||||
readyMove() {
|
||||
if (this.status !== 'stop') return;
|
||||
this.status = 'moving';
|
||||
this.lastFrameTime = Date.now();
|
||||
endAnimate() {
|
||||
this.animate = false;
|
||||
this.resetRenderable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置帧动画是否反向播放
|
||||
* @param reverse 帧动画是否反向播放
|
||||
*/
|
||||
setAnimateReversed(reverse: boolean) {
|
||||
this.animateReverse = reverse;
|
||||
this.resetRenderable(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置勇士的动画显示朝向
|
||||
* @param dir 显示朝向
|
||||
*/
|
||||
setAnimateDir(dir: Dir) {
|
||||
if (dir !== this.showDir) {
|
||||
this.showDir = dir;
|
||||
this.resetRenderable(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置renderable状态,恢复至第一帧状态
|
||||
* @param getInfo 是否重新获取渲染信息
|
||||
*/
|
||||
resetRenderable(getInfo: boolean) {
|
||||
this.movingFrame = 0;
|
||||
console.trace();
|
||||
|
||||
if (this.renderable) {
|
||||
this.renderable.animate = 0;
|
||||
if (getInfo) {
|
||||
this.renderable.render = this.getRenderFromDir(this.showDir);
|
||||
}
|
||||
}
|
||||
this.layer.update(this.layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 勇士帧动画定时器
|
||||
*/
|
||||
private animateTick(time: number) {
|
||||
if (!this.animate) return;
|
||||
if (time - this.lastFrameTime > this.speed) {
|
||||
this.lastFrameTime = time;
|
||||
this.movingFrame++;
|
||||
this.movingFrame %= 4;
|
||||
if (this.renderable) this.renderable.animate = this.movingFrame;
|
||||
}
|
||||
this.layer.update(this.layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 勇士移动定时器
|
||||
*/
|
||||
private moveTick(time: number) {
|
||||
if (this.status !== 'moving') return;
|
||||
if (!this.renderable) {
|
||||
this.emit('stepEnd');
|
||||
return;
|
||||
}
|
||||
|
||||
if (time - this.lastFrameTime > this.speed) {
|
||||
this.lastFrameTime = time;
|
||||
this.movingFrame++;
|
||||
this.movingFrame %= 4;
|
||||
}
|
||||
if (!this.moving) return;
|
||||
if (!this.renderable) return;
|
||||
|
||||
const progress = (time - this.lastStepTime) / this.speed;
|
||||
|
||||
@ -174,7 +215,6 @@ export class HeroRenderer
|
||||
this.renderable.y = ry;
|
||||
}
|
||||
this.emit('moveTick', this.renderable.x, this.renderable.y);
|
||||
this.renderable.animate = this.movingFrame;
|
||||
this.layer.update(this.layer);
|
||||
}
|
||||
|
||||
@ -182,18 +222,23 @@ export class HeroRenderer
|
||||
* 进行下一步的移动准备,设置移动信息
|
||||
*/
|
||||
private step() {
|
||||
if (this.moveDir === 'backward') this.stepDir = backDir(this.stepDir);
|
||||
else if (this.moveDir !== 'forward') this.stepDir = this.moveDir;
|
||||
this.lastStepTime = Date.now();
|
||||
this.stepDelta = core.utils.scan2[this.stepDir];
|
||||
this.turn(this.stepDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备开始移动
|
||||
*/
|
||||
readyMove() {
|
||||
this.moving = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动勇士
|
||||
*/
|
||||
move(dir: Move2): Promise<void> {
|
||||
if (this.status !== 'moving') {
|
||||
move(dir: Dir2): Promise<void> {
|
||||
if (!this.moving) {
|
||||
logger.error(
|
||||
12,
|
||||
`Cannot move while status is not 'moving'. Call 'readyMove' first.`
|
||||
@ -202,16 +247,16 @@ export class HeroRenderer
|
||||
}
|
||||
|
||||
this.moveDir = dir;
|
||||
|
||||
if (this.moveDetached) return this.moveDetached;
|
||||
else {
|
||||
this.step();
|
||||
return (this.moveDetached = new Promise<void>(resolve => {
|
||||
this.moveDetached = new Promise(res => {
|
||||
this.once('stepEnd', () => {
|
||||
this.moveDetached = void 0;
|
||||
resolve();
|
||||
res();
|
||||
});
|
||||
}));
|
||||
});
|
||||
return this.moveDetached;
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,14 +264,13 @@ export class HeroRenderer
|
||||
* 结束勇士的移动过程
|
||||
*/
|
||||
endMove(): Promise<void> {
|
||||
if (this.status !== 'moving') return Promise.reject();
|
||||
if (!this.moving) return Promise.reject();
|
||||
if (this.moveEnding) return this.moveEnding;
|
||||
else {
|
||||
const promise = new Promise<void>(resolve => {
|
||||
this.once('stepEnd', () => {
|
||||
this.moveEnding = void 0;
|
||||
this.status = 'stop';
|
||||
this.movingFrame = 0;
|
||||
this.moving = false;
|
||||
const { x, y } = core.status.hero.loc;
|
||||
this.setHeroLoc(x, y);
|
||||
this.render();
|
||||
@ -259,7 +303,7 @@ export class HeroRenderer
|
||||
this.stepDir = dir;
|
||||
|
||||
if (!this.renderable) return;
|
||||
this.renderable.render = this.getRenderFromDir(this.moveDir);
|
||||
this.renderable.render = this.getRenderFromDir(this.showDir);
|
||||
this.layer.update(this.layer);
|
||||
}
|
||||
|
||||
@ -291,9 +335,8 @@ export class HeroRenderer
|
||||
* 因为此举会导致层级的重新排序,降低渲染性能。
|
||||
*/
|
||||
moveAs(x: number, y: number, time: number, fn: TimingFn<3>): Promise<void> {
|
||||
if (this.status !== 'stop') return Promise.reject();
|
||||
if (!this.moving) return Promise.reject();
|
||||
if (!this.renderable) return Promise.reject();
|
||||
this.status = 'moving-as';
|
||||
let nowZIndex = fn(0)[2];
|
||||
let startTime = Date.now();
|
||||
return new Promise(res => {
|
||||
@ -307,18 +350,15 @@ export class HeroRenderer
|
||||
this.renderable.y = ny;
|
||||
this.renderable.zIndex = nz;
|
||||
if (nz !== nowZIndex) {
|
||||
this.layer.movingRenderable.sort(
|
||||
(a, b) => a.zIndex - b.zIndex
|
||||
);
|
||||
this.layer.sortMovingRenderable();
|
||||
}
|
||||
this.emit('moveTick', this.renderable.x, this.renderable.y);
|
||||
this.layer.update(this.layer);
|
||||
},
|
||||
time,
|
||||
() => {
|
||||
this.status = 'stop';
|
||||
this.moving = false;
|
||||
if (!this.renderable) return res();
|
||||
this.renderable.animate = 0;
|
||||
this.renderable.x = x;
|
||||
this.renderable.y = y;
|
||||
this.emit('moveTick', this.renderable.x, this.renderable.y);
|
||||
@ -334,7 +374,7 @@ export class HeroRenderer
|
||||
*/
|
||||
render() {
|
||||
if (!this.renderable) return;
|
||||
if (this.status === 'stop') {
|
||||
if (!this.animate) {
|
||||
this.renderable.animate = -1;
|
||||
} else {
|
||||
this.renderable.animate = this.movingFrame;
|
||||
@ -346,7 +386,9 @@ export class HeroRenderer
|
||||
this.layer = layer;
|
||||
adapter.add(this);
|
||||
this.moveId = layer.delegateTicker(() => {
|
||||
this.moveTick(Date.now());
|
||||
const time = Date.now();
|
||||
this.animateTick(time);
|
||||
this.moveTick(time);
|
||||
});
|
||||
}
|
||||
|
||||
@ -380,10 +422,6 @@ adapter.receive(
|
||||
return item.moveAs(x, y, time, fn);
|
||||
}
|
||||
);
|
||||
adapter.receive('setMoveSpeed', (item, speed: number) => {
|
||||
item.setMoveSpeed(speed);
|
||||
return Promise.resolve();
|
||||
});
|
||||
adapter.receive('setHeroLoc', (item, x?: number, y?: number) => {
|
||||
item.setHeroLoc(x, y);
|
||||
return Promise.resolve();
|
||||
@ -392,11 +430,28 @@ adapter.receive('turn', (item, dir: Dir2) => {
|
||||
item.turn(dir);
|
||||
return Promise.resolve();
|
||||
});
|
||||
adapter.receive('setImage', (item, image: SizedCanvasImageSource) => {
|
||||
|
||||
// 同步适配函数,这些函数用于同步设置信息等
|
||||
adapter.receiveSync('setImage', (item, image: SizedCanvasImageSource) => {
|
||||
item.setImage(image);
|
||||
return Promise.resolve();
|
||||
});
|
||||
// 同步fallback,用于适配现在的样板,之后会删除
|
||||
adapter.receiveSync('setMoveSpeed', (item, speed: number) => {
|
||||
item.setMoveSpeed(speed);
|
||||
});
|
||||
adapter.receiveSync('setAnimateReversed', (item, reverse: boolean) => {
|
||||
item.setAnimateReversed(reverse);
|
||||
});
|
||||
adapter.receiveSync('startAnimate', item => {
|
||||
item.startAnimate();
|
||||
});
|
||||
adapter.receiveSync('endAnimate', item => {
|
||||
item.endAnimate();
|
||||
});
|
||||
adapter.receiveSync('setAnimateDir', (item, dir: Dir) => {
|
||||
item.setAnimateDir(dir);
|
||||
});
|
||||
|
||||
// 不分同步fallback,用于适配现在的样板,之后会删除
|
||||
adapter.receiveSync('setHeroLoc', (item, x?: number, y?: number) => {
|
||||
item.setHeroLoc(x, y);
|
||||
});
|
||||
|
@ -1123,6 +1123,13 @@ export class Layer extends Container {
|
||||
for (const ex of this.extend.values()) {
|
||||
ex.onMovingUpdate?.(this, this.movingRenderable);
|
||||
}
|
||||
this.sortMovingRenderable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对移动层按照z坐标排序
|
||||
*/
|
||||
sortMovingRenderable() {
|
||||
this.movingRenderable.sort((a, b) => a.zIndex - b.zIndex);
|
||||
}
|
||||
|
||||
@ -1277,7 +1284,7 @@ export class Layer extends Container {
|
||||
* 渲染移动/大怪物层
|
||||
*/
|
||||
protected renderMoving(transform: Transform) {
|
||||
const frame = (RenderItem.animatedFrame % 4) + 1;
|
||||
const frame = RenderItem.animatedFrame;
|
||||
const cell = this.cellSize;
|
||||
const halfCell = cell / 2;
|
||||
const { ctx } = this.movingMap;
|
||||
|
@ -11,6 +11,7 @@ import * as miscMechanism from './mechanism/misc';
|
||||
import * as study from './mechanism/study';
|
||||
import { registerPresetState } from './state/preset';
|
||||
import { ItemState } from './state/item';
|
||||
import { HeroMover, ObjectMoverBase } from './state/move';
|
||||
|
||||
// ----- 类注册
|
||||
Mota.register('class', 'DamageEnemy', damage.DamageEnemy);
|
||||
@ -34,7 +35,9 @@ Mota.register('module', 'Mechanism', {
|
||||
Study: study
|
||||
});
|
||||
Mota.register('module', 'State', {
|
||||
ItemState
|
||||
ItemState,
|
||||
HeroMover,
|
||||
ObjectMoverBase
|
||||
});
|
||||
|
||||
main.loading = loading;
|
||||
|
397
src/game/state/move.ts
Normal file
397
src/game/state/move.ts
Normal file
@ -0,0 +1,397 @@
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { backDir, toDir } from './utils';
|
||||
import { loading } from '../game';
|
||||
import type { RenderAdapter } from '@/core/render/adapter';
|
||||
import type { HeroRenderer } from '@/core/render/preset/hero';
|
||||
import type { FloorViewport } from '@/core/render/preset/viewport';
|
||||
import type { KeyCode } from '@/plugin/keyCodes';
|
||||
|
||||
interface MoveStepDir {
|
||||
type: 'dir';
|
||||
value: Move2;
|
||||
}
|
||||
|
||||
interface MoveStepSpeed {
|
||||
type: 'speed';
|
||||
value: number;
|
||||
}
|
||||
|
||||
type MoveStep = MoveStepDir | MoveStepSpeed;
|
||||
|
||||
export interface IMoveController {
|
||||
/** 本次移动是否完成 */
|
||||
done: boolean;
|
||||
/** 当前移动队列 */
|
||||
queue: MoveStep[];
|
||||
/** 当本次移动完成时兑现 */
|
||||
onEnd: Promise<void>;
|
||||
|
||||
/**
|
||||
* 停止本次移动,停止后将不再能继续移动
|
||||
*/
|
||||
stop(): Promise<void>;
|
||||
|
||||
/**
|
||||
* 向队列末尾添加移动信息
|
||||
* @param step 移动信息
|
||||
*/
|
||||
push(...step: MoveStep[]): void;
|
||||
}
|
||||
|
||||
interface EObjectMovingEvent {
|
||||
stepEnd: [step: MoveStep];
|
||||
moveEnd: [];
|
||||
moveStart: [queue: MoveStep[]];
|
||||
}
|
||||
|
||||
export abstract class ObjectMoverBase extends EventEmitter<EObjectMovingEvent> {
|
||||
/** 移动队列 */
|
||||
private moveQueue: MoveStep[] = [];
|
||||
|
||||
/** 当前的移动速度 */
|
||||
moveSpeed: number = 100;
|
||||
/** 当前的移动方向 */
|
||||
moveDir: Dir2 = 'down';
|
||||
/** 当前是否正在移动 */
|
||||
moving: boolean = false;
|
||||
|
||||
/**
|
||||
* 当本次移动开始时执行的函数
|
||||
* @param controller 本次移动的控制对象
|
||||
*/
|
||||
protected abstract onMoveStart(controller: IMoveController): Promise<void>;
|
||||
|
||||
/**
|
||||
* 当本次移动结束时执行的函数
|
||||
* @param controller 本次移动的控制对象
|
||||
*/
|
||||
protected abstract onMoveEnd(controller: IMoveController): Promise<void>;
|
||||
|
||||
/**
|
||||
* 当这一步开始移动时执行的函数,可以用来播放移动动画等
|
||||
* @param step 这一步的移动信息
|
||||
* @param controller 本次移动的控制对象
|
||||
* @returns 一个Promise,其值表示本次移动的代码,并将其值传入这一步移动后的操作,即{@link onStepEnd}
|
||||
*/
|
||||
protected abstract onStepStart(
|
||||
step: MoveStepDir,
|
||||
controller: IMoveController
|
||||
): Promise<number>;
|
||||
|
||||
/**
|
||||
* 当这一步移动结束后执行的函数(即{@link onStepStart}成功兑现后),可以用于设置物体坐标等
|
||||
* @param step 这一步的移动信息
|
||||
* @param code 本步移动的代码,即onMoveStart的返回值
|
||||
* @param controller 本次移动的控制对象
|
||||
* @returns 一个Promise,当其兑现时,本步移动全部完成,将会开始下一步的移动
|
||||
*/
|
||||
protected abstract onStepEnd(
|
||||
step: MoveStepDir,
|
||||
code: number,
|
||||
controller: IMoveController
|
||||
): Promise<void>;
|
||||
|
||||
private getMoveDir(move: Move2): Dir2 {
|
||||
if (move === 'forward') return this.moveDir;
|
||||
if (move === 'backward') return backDir(this.moveDir);
|
||||
return move;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始移动,这个操作后,本次移动将不能通过{@link insertMove}添加移动步骤,
|
||||
* 只能通过{@link IMoveController.push}添加,而通过前者添加的移动步骤会在下次移动生效
|
||||
*/
|
||||
startMove(): IMoveController | null {
|
||||
if (this.moving) return null;
|
||||
const queue = this.moveQueue.slice();
|
||||
this.clearMoveQueue();
|
||||
|
||||
this.moving = true;
|
||||
let stopMove = false;
|
||||
|
||||
const start = async () => {
|
||||
// 等待宏任务执行完成,不然controller会在首次调用中未定义
|
||||
await new Promise<void>(res => res());
|
||||
this.emit('moveStart', queue);
|
||||
await this.onMoveStart(controller);
|
||||
while (queue.length > 0) {
|
||||
if (stopMove || !this.moving) break;
|
||||
const step = queue.shift();
|
||||
if (!step) break;
|
||||
if (step.type === 'dir') {
|
||||
this.moveDir = this.getMoveDir(step.value);
|
||||
const code = await this.onStepStart(step, controller);
|
||||
await this.onStepEnd(step, code, controller);
|
||||
} else {
|
||||
this.moveSpeed = step.value;
|
||||
}
|
||||
this.emit('stepEnd', step);
|
||||
}
|
||||
this.moving = false;
|
||||
this.emit('moveEnd');
|
||||
|
||||
await this.onMoveEnd(controller);
|
||||
};
|
||||
|
||||
const onEnd = start();
|
||||
const controller: IMoveController = {
|
||||
done: false,
|
||||
onEnd,
|
||||
queue,
|
||||
push(...step) {
|
||||
queue.push(...step);
|
||||
},
|
||||
stop() {
|
||||
stopMove = true;
|
||||
return onEnd;
|
||||
}
|
||||
};
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向移动队列末尾插入移动实例
|
||||
* @param move 移动实例
|
||||
*/
|
||||
insertMove(...move: MoveStep[]) {
|
||||
this.moveQueue.push(...move);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空移动队列
|
||||
*/
|
||||
clearMoveQueue() {
|
||||
this.moveQueue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 向移动队列末尾插入一个移动操作
|
||||
* @param step 移动方向
|
||||
*/
|
||||
oneStep(step: Move2) {
|
||||
this.moveQueue.push({ type: 'dir', value: step });
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照传入的数组移动物体,插入至移动队列末尾
|
||||
* @param steps 移动步骤
|
||||
*/
|
||||
moveAs(steps: MoveStep[]) {
|
||||
this.moveQueue.push(...steps);
|
||||
}
|
||||
}
|
||||
|
||||
interface CanMoveStatus {
|
||||
/** 由CannotIn和CannotOut计算出的信息,不可移动时不会触发触发器 */
|
||||
canMove: boolean;
|
||||
/** 由图块的noPass计算出的信息,不可移动时会触发触发器 */
|
||||
noPass: boolean;
|
||||
}
|
||||
|
||||
const enum HeroMoveCode {
|
||||
Step,
|
||||
Stop,
|
||||
/** 不能移动,并撞击前面一格的图块,触发其触发器 */
|
||||
Hit,
|
||||
/** 不能移动,同时当前格有CannotOut,或目标格有CannotIn,不会触发前面一格的触发器 */
|
||||
CannotMove
|
||||
}
|
||||
|
||||
export class HeroMover extends ObjectMoverBase {
|
||||
/** 勇士渲染适配器,用于等待动画等操作 */
|
||||
static adapter?: RenderAdapter<HeroRenderer>;
|
||||
/** 视角适配器 */
|
||||
static viewport?: RenderAdapter<FloorViewport>;
|
||||
|
||||
/** 当前移动是否忽略地形 */
|
||||
private ignoreTerrain: boolean = false;
|
||||
/** 当前移动是否不计入录像 */
|
||||
private noRoute: boolean = false;
|
||||
/** 当前移动是否是在lockControl条件下开始的 */
|
||||
private inLockControl: boolean = false;
|
||||
|
||||
override startMove(
|
||||
ignoreTerrain: boolean = false,
|
||||
noRoute: boolean = false,
|
||||
inLockControl: boolean = false
|
||||
): IMoveController | null {
|
||||
this.ignoreTerrain = ignoreTerrain;
|
||||
this.noRoute = noRoute;
|
||||
this.inLockControl = inLockControl;
|
||||
return super.startMove();
|
||||
}
|
||||
|
||||
protected async onMoveStart(controller: IMoveController): Promise<void> {
|
||||
const adapter = HeroMover.adapter;
|
||||
if (!adapter) return;
|
||||
await adapter.all('readyMove');
|
||||
adapter.sync('startAnimate');
|
||||
}
|
||||
|
||||
protected async onMoveEnd(controller: IMoveController): Promise<void> {
|
||||
const adapter = HeroMover.adapter;
|
||||
if (!adapter) return;
|
||||
await adapter.all('endMove');
|
||||
adapter.sync('endAnimate');
|
||||
}
|
||||
|
||||
protected async onStepStart(
|
||||
step: MoveStepDir,
|
||||
controller: IMoveController
|
||||
): Promise<HeroMoveCode> {
|
||||
const showDir = toDir(this.moveDir);
|
||||
core.setHeroLoc('direction', showDir);
|
||||
const adapter = HeroMover.adapter;
|
||||
adapter?.sync('setAnimateDir', showDir);
|
||||
|
||||
const { x, y } = core.status.hero.loc;
|
||||
const { x: nx, y: ny } = this.nextLoc(x, y, this.moveDir);
|
||||
|
||||
if (!this.inLockControl && core.status.lockControl) {
|
||||
controller.stop();
|
||||
return HeroMoveCode.Stop;
|
||||
}
|
||||
|
||||
if (!this.ignoreTerrain || !this.noRoute) {
|
||||
this.moveDir = showDir;
|
||||
}
|
||||
|
||||
const dir = this.moveDir;
|
||||
if (!this.ignoreTerrain) {
|
||||
const { noPass, canMove } = this.checkCanMove(x, y, showDir);
|
||||
|
||||
// 不能移动
|
||||
if (noPass || !canMove) {
|
||||
if (!canMove) return HeroMoveCode.CannotMove;
|
||||
else return HeroMoveCode.Hit;
|
||||
}
|
||||
}
|
||||
|
||||
// 可以移动,显示移动动画
|
||||
await this.moveAnimate(nx, ny, showDir, dir);
|
||||
|
||||
return HeroMoveCode.Step;
|
||||
}
|
||||
|
||||
protected async onStepEnd(
|
||||
step: MoveStepDir,
|
||||
code: HeroMoveCode,
|
||||
controller: IMoveController
|
||||
): Promise<void> {
|
||||
const { x, y } = core.status.hero.loc;
|
||||
const { x: nx, y: ny } = this.nextLoc(x, y, this.moveDir);
|
||||
const showDir = toDir(this.moveDir);
|
||||
|
||||
// 前方不能移动
|
||||
if (code === HeroMoveCode.CannotMove || code === HeroMoveCode.Hit) {
|
||||
controller.stop();
|
||||
this.onCannotMove(showDir);
|
||||
if (code === HeroMoveCode.Hit) {
|
||||
core.trigger(nx, ny);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 本次移动停止
|
||||
if (code === HeroMoveCode.Stop) {
|
||||
controller.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// 本次移动正常完成
|
||||
if (code === HeroMoveCode.Step) {
|
||||
core.setHeroLoc('x', nx, true);
|
||||
core.setHeroLoc('y', ny, true);
|
||||
|
||||
const direction = core.getHeroLoc('direction');
|
||||
core.control._moveAction_popAutomaticRoute();
|
||||
if (!this.noRoute) core.status.route.push(direction);
|
||||
|
||||
core.moveOneStep();
|
||||
core.checkRouteFolding();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动动画
|
||||
* @param x 目标横坐标
|
||||
* @param y 目标纵坐标
|
||||
* @param showDir 显示方向
|
||||
* @param moveDir 移动方向
|
||||
*/
|
||||
private async moveAnimate(
|
||||
x: number,
|
||||
y: number,
|
||||
showDir: Dir,
|
||||
moveDir: Dir2
|
||||
) {
|
||||
const adapter = HeroMover.adapter;
|
||||
const viewport = HeroMover.viewport;
|
||||
if (!adapter || !viewport) return;
|
||||
viewport.all('moveTo', x, y);
|
||||
await adapter.all('move', moveDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前方一格不能走时,执行的函数
|
||||
* @param dir 移动方向
|
||||
*/
|
||||
private onCannotMove(dir: Dir) {
|
||||
if (!this.noRoute) core.status.route.push(dir);
|
||||
core.status.automaticRoute.moveStepBeforeStop = [];
|
||||
core.status.automaticRoute.lastDirection = dir;
|
||||
|
||||
if (core.status.automaticRoute.moveStepBeforeStop.length == 0) {
|
||||
core.clearContinueAutomaticRoute();
|
||||
core.stopAutomaticRoute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查前方是否可以移动
|
||||
* @param x 当前横坐标
|
||||
* @param y 当前纵坐标
|
||||
* @param dir 移动方向
|
||||
*/
|
||||
private checkCanMove(x: number, y: number, dir: Dir): CanMoveStatus {
|
||||
const { x: nx, y: ny } = this.nextLoc(x, y, dir);
|
||||
const noPass = core.noPass(nx, ny);
|
||||
const canMove = core.canMoveHero(x, y, dir);
|
||||
return { noPass, canMove };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前方一格的坐标
|
||||
* @param x 当前横坐标
|
||||
* @param y 当前纵坐标
|
||||
* @param dir 移动方向
|
||||
*/
|
||||
private nextLoc(x: number, y: number, dir: Dir2): Loc {
|
||||
const { x: dx, y: dy } = core.utils.scan2[dir];
|
||||
const nx = x + dx;
|
||||
const ny = y + dy;
|
||||
return { x: nx, y: ny };
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化基础勇士移动实例
|
||||
export const heroMover = new HeroMover();
|
||||
loading.once('coreInit', () => {
|
||||
// 注册按键操作
|
||||
Mota.r(() => {
|
||||
const { HeroKeyMover } = Mota.require('module', 'Action');
|
||||
const gameKey = Mota.require('var', 'gameKey');
|
||||
const keyMover = new HeroKeyMover(gameKey, heroMover);
|
||||
});
|
||||
});
|
||||
|
||||
// Adapter初始化
|
||||
loading.once('coreInit', () => {
|
||||
if (main.replayChecking || main.mode === 'editor') return;
|
||||
const Adapter = Mota.require('module', 'Render').RenderAdapter;
|
||||
const adapter = Adapter.get<HeroRenderer>('hero-adapter');
|
||||
const viewport = Adapter.get<FloorViewport>('viewport');
|
||||
HeroMover.adapter = adapter;
|
||||
HeroMover.viewport = viewport;
|
||||
});
|
35
src/game/state/utils.ts
Normal file
35
src/game/state/utils.ts
Normal file
@ -0,0 +1,35 @@
|
||||
const dirMap: Record<Dir2, Dir> = {
|
||||
down: 'down',
|
||||
left: 'left',
|
||||
leftdown: 'left',
|
||||
leftup: 'left',
|
||||
right: 'right',
|
||||
rightdown: 'right',
|
||||
rightup: 'right',
|
||||
up: 'up'
|
||||
};
|
||||
|
||||
/**
|
||||
* 将八方向转换为四方向,一般用于斜角移动时的方向显示
|
||||
* @param dir 八方向
|
||||
*/
|
||||
export function toDir(dir: Dir2): Dir {
|
||||
return dirMap[dir];
|
||||
}
|
||||
|
||||
const backDirMap: Record<Dir2, Dir2> = {
|
||||
up: 'down',
|
||||
down: 'up',
|
||||
left: 'right',
|
||||
right: 'left',
|
||||
leftup: 'rightdown',
|
||||
rightup: 'leftdown',
|
||||
leftdown: 'rightup',
|
||||
rightdown: 'leftup'
|
||||
};
|
||||
|
||||
export function backDir(dir: Dir): Dir;
|
||||
export function backDir(dir: Dir2): Dir2;
|
||||
export function backDir(dir: Dir2): Dir2 {
|
||||
return backDirMap[dir];
|
||||
}
|
@ -37,6 +37,8 @@ import type { RenderAdapter } from '@/core/render/adapter';
|
||||
import type { ItemState } from './state/item';
|
||||
import type { Layer } from '@/core/render/preset/layer';
|
||||
import type { LayerGroupFloorBinder } from '@/core/render/preset/floor';
|
||||
import type { HeroKeyMover } from '@/core/main/action/move';
|
||||
import type { HeroMover, ObjectMoverBase } from './state/move';
|
||||
|
||||
interface ClassInterface {
|
||||
// 渲染进程与游戏进程通用
|
||||
@ -121,6 +123,11 @@ interface ModuleInterface {
|
||||
};
|
||||
State: {
|
||||
ItemState: typeof ItemState;
|
||||
HeroMover: typeof HeroMover;
|
||||
ObjectMoverBase: typeof ObjectMoverBase;
|
||||
};
|
||||
Action: {
|
||||
HeroKeyMover: typeof HeroKeyMover;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ export function init() {
|
||||
}
|
||||
|
||||
let moving: boolean = false;
|
||||
let stopChian: boolean = false;
|
||||
let stopChain: boolean = false;
|
||||
let moveDir: Dir;
|
||||
let stepDir: Dir;
|
||||
let moveEnding: Promise<any[]> = Promise.resolve([]);
|
||||
@ -65,24 +65,24 @@ export function init() {
|
||||
let portal: boolean = false;
|
||||
|
||||
const pressedArrow: Set<Dir> = new Set();
|
||||
Mota.r(() => {
|
||||
const gameKey = Mota.require('var', 'gameKey');
|
||||
const { moveUp, moveDown, moveLeft, moveRight } = gameKey.data;
|
||||
const symbol = Symbol.for('@key_main');
|
||||
gameKey.on('press', code => {
|
||||
if (core.status.lockControl || gameKey.scope !== symbol) return;
|
||||
if (code === moveUp.key) onMoveKeyDown('up');
|
||||
if (code === moveDown.key) onMoveKeyDown('down');
|
||||
if (code === moveLeft.key) onMoveKeyDown('left');
|
||||
if (code === moveRight.key) onMoveKeyDown('right');
|
||||
});
|
||||
gameKey.on('release', code => {
|
||||
if (code === moveUp.key) onMoveKeyUp('up');
|
||||
if (code === moveDown.key) onMoveKeyUp('down');
|
||||
if (code === moveLeft.key) onMoveKeyUp('left');
|
||||
if (code === moveRight.key) onMoveKeyUp('right');
|
||||
});
|
||||
});
|
||||
// Mota.r(() => {
|
||||
// const gameKey = Mota.require('var', 'gameKey');
|
||||
// const { moveUp, moveDown, moveLeft, moveRight } = gameKey.data;
|
||||
// const symbol = Symbol.for('@key_main');
|
||||
// gameKey.on('press', code => {
|
||||
// if (core.status.lockControl || gameKey.scope !== symbol) return;
|
||||
// if (code === moveUp.key) onMoveKeyDown('up');
|
||||
// if (code === moveDown.key) onMoveKeyDown('down');
|
||||
// if (code === moveLeft.key) onMoveKeyDown('left');
|
||||
// if (code === moveRight.key) onMoveKeyDown('right');
|
||||
// });
|
||||
// gameKey.on('release', code => {
|
||||
// if (code === moveUp.key) onMoveKeyUp('up');
|
||||
// if (code === moveDown.key) onMoveKeyUp('down');
|
||||
// if (code === moveLeft.key) onMoveKeyUp('left');
|
||||
// if (code === moveRight.key) onMoveKeyUp('right');
|
||||
// });
|
||||
// });
|
||||
|
||||
function onMoveKeyDown(type: Dir) {
|
||||
pressedArrow.add(type);
|
||||
@ -91,15 +91,15 @@ export function init() {
|
||||
stepDir = moveDir;
|
||||
readyMove();
|
||||
}
|
||||
if (moving && stopChian) {
|
||||
stopChian = false;
|
||||
if (moving && stopChain) {
|
||||
stopChain = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onMoveKeyUp(type: Dir) {
|
||||
pressedArrow.delete(type);
|
||||
if (pressedArrow.size === 0) {
|
||||
stopChian = true;
|
||||
stopChain = true;
|
||||
} else {
|
||||
const arr = [...pressedArrow];
|
||||
moveDir = arr[0];
|
||||
@ -127,7 +127,7 @@ export function init() {
|
||||
}
|
||||
await adapter.all('readyMove');
|
||||
moving = true;
|
||||
stopChian = false;
|
||||
stopChain = false;
|
||||
|
||||
startHeroMoveChain(ignoreTerrain, noRoute, inLockControl, callback);
|
||||
}
|
||||
@ -143,7 +143,7 @@ export function init() {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
if (moving) {
|
||||
stopChian = true;
|
||||
stopChain = true;
|
||||
moveEnding = adapter.all('endMove');
|
||||
await moveEnding;
|
||||
moveEmit.emit('moveEnd');
|
||||
@ -161,7 +161,9 @@ export function init() {
|
||||
function continueAfterEnd() {
|
||||
requestAnimationFrame(() => {
|
||||
if (pressedArrow.size === 0 || moving) {
|
||||
stopChian = true;
|
||||
// console.log(6);
|
||||
|
||||
stopChain = true;
|
||||
return;
|
||||
}
|
||||
if (checkCanMoveStatus()) {
|
||||
@ -185,8 +187,8 @@ export function init() {
|
||||
if (!adapter) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
while (moving || !stopChian) {
|
||||
if (stopChian || (!inLockControl && core.status.lockControl)) {
|
||||
while (moving || !stopChain) {
|
||||
if (stopChain || (!inLockControl && core.status.lockControl)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -215,7 +217,7 @@ export function init() {
|
||||
}
|
||||
|
||||
endMove();
|
||||
stopChian = false;
|
||||
stopChain = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,6 +299,30 @@ export function init() {
|
||||
callback?.();
|
||||
}
|
||||
|
||||
async function moveHeroAs(step: DiredLoc[]) {
|
||||
if (moving) return;
|
||||
// console.log(step);
|
||||
|
||||
let now = step.shift();
|
||||
if (!now) return;
|
||||
|
||||
stepDir = now.direction;
|
||||
await readyMove();
|
||||
while (now) {
|
||||
if (!moving) break;
|
||||
stepDir = now.direction;
|
||||
|
||||
await new Promise<void>(res => {
|
||||
moveEmit.once('stepEnd', () => {
|
||||
now = step.shift();
|
||||
if (!now) endMove();
|
||||
console.log(now?.direction);
|
||||
res();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ----- 移动 - 传送门
|
||||
function checkPortal() {
|
||||
const map = BluePalace.portalMap.get(core.status.floorId);
|
||||
@ -437,8 +463,8 @@ export function init() {
|
||||
}
|
||||
});
|
||||
|
||||
const step0 = steps.find(v => !v.startsWith('speed')) as Move2;
|
||||
return { steps, start: step0 };
|
||||
const step0 = moveSteps.find(v => !v.startsWith('speed')) as Move2;
|
||||
return { steps: moveSteps, start: step0 };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -495,7 +521,7 @@ export function init() {
|
||||
control.prototype.moveAction = async function (callback?: () => void) {
|
||||
// await endMove(false);
|
||||
moveEmit.once('stepEnd', () => {
|
||||
stopChian = true;
|
||||
stopChain = true;
|
||||
endMove(false);
|
||||
});
|
||||
readyMove(false, true, true, callback);
|
||||
@ -570,14 +596,14 @@ export function init() {
|
||||
readyMove(true, true, true);
|
||||
while (++pointer < len) {
|
||||
const dir = moveSteps[pointer];
|
||||
if (dir === 'backward') moveDir = backDir(moveDir);
|
||||
if (dir === 'backward') stepDir = backDir(stepDir);
|
||||
else if (dir.startsWith('speed')) {
|
||||
const speed = parseInt(dir.slice(6));
|
||||
list.forEach(v => v.setMoveSpeed(speed));
|
||||
} else if (dir !== 'forward') {
|
||||
moveDir = dir as Dir;
|
||||
stepDir = dir as Dir;
|
||||
}
|
||||
const { x, y } = core.utils.scan[moveDir];
|
||||
const { x, y } = core.utils.scan[stepDir];
|
||||
nx += x;
|
||||
ny += y;
|
||||
await new Promise<void>(res => {
|
||||
@ -617,7 +643,9 @@ export function init() {
|
||||
control.prototype.waitHeroToStop = function (callback?: () => void) {
|
||||
core.stopAutomaticRoute();
|
||||
core.clearContinueAutomaticRoute();
|
||||
moveEnding.then(() => {
|
||||
|
||||
stopChain = true;
|
||||
stepEnding.then(() => {
|
||||
callback?.();
|
||||
});
|
||||
};
|
||||
@ -639,7 +667,8 @@ export function init() {
|
||||
moveDir = direction;
|
||||
stepDir = direction;
|
||||
await readyMove();
|
||||
stopChian = true;
|
||||
|
||||
stopChain = true;
|
||||
|
||||
callback?.();
|
||||
};
|
||||
@ -647,13 +676,52 @@ export function init() {
|
||||
events.prototype.setHeroIcon = function (name: ImageIds) {
|
||||
const img = core.material.images.images[name];
|
||||
if (!img) return;
|
||||
adapters['hero-adapter']?.all('setImage', img);
|
||||
adapters['hero-adapter']?.sync('setImage', img);
|
||||
};
|
||||
|
||||
control.prototype.isMoving = function () {
|
||||
return moving;
|
||||
};
|
||||
|
||||
////// 设置自动寻路路线 //////
|
||||
control.prototype.setAutomaticRoute = function (
|
||||
destX: number,
|
||||
destY: number,
|
||||
stepPostfix: DiredLoc[]
|
||||
) {
|
||||
if (!core.status.played || core.status.lockControl) return;
|
||||
if (this._setAutomaticRoute_isMoving(destX, destY)) return;
|
||||
if (this._setAutomaticRoute_isTurning(destX, destY, stepPostfix))
|
||||
return;
|
||||
if (
|
||||
this._setAutomaticRoute_clickMoveDirectly(
|
||||
destX,
|
||||
destY,
|
||||
stepPostfix
|
||||
)
|
||||
)
|
||||
return;
|
||||
// 找寻自动寻路路线
|
||||
const moveStep = core.automaticRoute(destX, destY);
|
||||
if (
|
||||
moveStep.length == 0 &&
|
||||
(destX != core.status.hero.loc.x ||
|
||||
destY != core.status.hero.loc.y ||
|
||||
stepPostfix.length == 0)
|
||||
)
|
||||
return;
|
||||
moveStep.push(...stepPostfix);
|
||||
core.status.automaticRoute.destX = destX;
|
||||
core.status.automaticRoute.destY = destY;
|
||||
this._setAutomaticRoute_drawRoute(moveStep);
|
||||
this._setAutomaticRoute_setAutoSteps(moveStep);
|
||||
// 立刻移动
|
||||
// core.setAutoHeroMove();
|
||||
console.log(moveStep);
|
||||
|
||||
moveHeroAs(moveStep.slice());
|
||||
};
|
||||
|
||||
hook.on('reset', () => {
|
||||
moveDir = core.status.hero.loc.direction;
|
||||
stepDir = moveDir;
|
||||
@ -868,10 +936,11 @@ export function init() {
|
||||
|
||||
for (const step of moveSteps) {
|
||||
if (step === 'backward') stepDir = backDir(stepDir);
|
||||
if (step.startsWith('speed')) {
|
||||
else if (step.startsWith('speed')) {
|
||||
time = parseInt(step.slice(6));
|
||||
continue;
|
||||
}
|
||||
} else stepDir = step as Dir2;
|
||||
|
||||
const { x, y } = core.utils.scan2[stepDir];
|
||||
const tx = nx + x;
|
||||
const ty = ny + y;
|
||||
@ -983,8 +1052,10 @@ export function init() {
|
||||
destY: number,
|
||||
ignoreSteps: number
|
||||
) {
|
||||
adapters.viewport?.all('mutateTo', destX, destY);
|
||||
return this.controldata.moveDirectly(destX, destY, ignoreSteps);
|
||||
const data = this.controldata;
|
||||
const success = data.moveDirectly(destX, destY, ignoreSteps);
|
||||
if (success) adapters.viewport?.all('mutateTo', destX, destY);
|
||||
return success;
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { message } from 'ant-design-vue';
|
||||
import { MessageApi } from 'ant-design-vue/lib/message';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { Animation, sleep, TimingFn } from 'mutate-animate';
|
||||
import { ref } from 'vue';
|
||||
import { Ref, ref } from 'vue';
|
||||
import { EVENT_KEY_CODE_MAP, KeyCode } from './keyCodes';
|
||||
import axios from 'axios';
|
||||
import { decompressFromBase64 } from 'lz-string';
|
||||
|
2
src/types/status.d.ts
vendored
2
src/types/status.d.ts
vendored
@ -641,7 +641,7 @@ interface InitGameStatus {
|
||||
/**
|
||||
* 自动寻路状态
|
||||
*/
|
||||
automaticRoute: DeepReadonly<AutomaticRouteStatus>;
|
||||
automaticRoute: AutomaticRouteStatus;
|
||||
|
||||
/**
|
||||
* 按键按下的时间,用于判定双击
|
||||
|
Loading…
Reference in New Issue
Block a user