mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-11-27 13:42:58 +08:00
feat: 开关门动画
This commit is contained in:
parent
3372337fdf
commit
dce4cc5b7d
@ -17,5 +17,6 @@ export async function createMainExtension() {
|
|||||||
const layer = state.layer.getLayerByAlias('event');
|
const layer = state.layer.getLayerByAlias('event');
|
||||||
if (layer) {
|
if (layer) {
|
||||||
mainMapExtension.addHero(state.hero, layer);
|
mainMapExtension.addHero(state.hero, layer);
|
||||||
|
mainMapExtension.addDoor(layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
import {
|
||||||
|
IMapLayer,
|
||||||
|
IMapLayerHookController,
|
||||||
|
IMapLayerHooks
|
||||||
|
} from '@user/data-state';
|
||||||
|
import { IMapDoorRenderer } from './types';
|
||||||
|
import { IMapRenderer } from '../types';
|
||||||
|
import { sleep } from 'mutate-animate';
|
||||||
|
import { DOOR_ANIMATE_INTERVAL } from '../../shared';
|
||||||
|
|
||||||
|
export class MapDoorRenderer implements IMapDoorRenderer {
|
||||||
|
/** 钩子控制器 */
|
||||||
|
readonly controller: IMapLayerHookController;
|
||||||
|
|
||||||
|
/** 动画间隔 */
|
||||||
|
private interval: number = DOOR_ANIMATE_INTERVAL;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly renderer: IMapRenderer,
|
||||||
|
readonly layer: IMapLayer
|
||||||
|
) {
|
||||||
|
this.controller = layer.addHook(new MapDoorHook(this));
|
||||||
|
this.controller.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
setAnimateInterval(interval: number): void {
|
||||||
|
this.interval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
async openDoor(x: number, y: number): Promise<void> {
|
||||||
|
const status = this.renderer.getBlockStatus(this.layer, x, y);
|
||||||
|
if (!status) return;
|
||||||
|
const array = this.layer.getMapRef().array;
|
||||||
|
const index = y * this.layer.width + x;
|
||||||
|
const num = array[index];
|
||||||
|
const data = this.renderer.manager.getIfBigImage(num);
|
||||||
|
if (!data) return;
|
||||||
|
const frames = data.frames;
|
||||||
|
for (let i = 0; i < frames; i++) {
|
||||||
|
status.useSpecifiedFrame(i);
|
||||||
|
await sleep(this.interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeDoor(num: number, x: number, y: number): Promise<void> {
|
||||||
|
const data = this.renderer.manager.getIfBigImage(num);
|
||||||
|
if (!data) return;
|
||||||
|
const moving = this.renderer.addMovingBlock(this.layer, num, x, y);
|
||||||
|
const frames = data.frames;
|
||||||
|
|
||||||
|
for (let i = frames - 1; i >= 0; i--) {
|
||||||
|
moving.useSpecifiedFrame(i);
|
||||||
|
await sleep(this.interval);
|
||||||
|
}
|
||||||
|
moving.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
this.controller.unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapDoorHook implements Partial<IMapLayerHooks> {
|
||||||
|
constructor(readonly renderer: MapDoorRenderer) {}
|
||||||
|
|
||||||
|
onOpenDoor(x: number, y: number): Promise<void> {
|
||||||
|
return this.renderer.openDoor(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloseDoor(num: number, x: number, y: number): Promise<void> {
|
||||||
|
return this.renderer.closeDoor(num, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -156,7 +156,8 @@ export class MapHeroRenderer implements IMapHeroRenderer {
|
|||||||
offset: dirImage.width / 4,
|
offset: dirImage.width / 4,
|
||||||
texture: dirImage,
|
texture: dirImage,
|
||||||
cls: BlockCls.Unknown,
|
cls: BlockCls.Unknown,
|
||||||
frames: 4
|
frames: 4,
|
||||||
|
defaultFrame: 0
|
||||||
};
|
};
|
||||||
this.textureMap.set(v, data);
|
this.textureMap.set(v, data);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,22 +1,30 @@
|
|||||||
import { IHeroState, IMapLayer } from '@user/data-state';
|
import { IHeroState, IMapLayer } from '@user/data-state';
|
||||||
import { IMapExtensionManager, IMapHeroRenderer } from './types';
|
import {
|
||||||
|
IMapDoorRenderer,
|
||||||
|
IMapExtensionManager,
|
||||||
|
IMapHeroRenderer
|
||||||
|
} from './types';
|
||||||
import { IMapRenderer } from '../types';
|
import { IMapRenderer } from '../types';
|
||||||
import { MapHeroRenderer } from './hero';
|
import { MapHeroRenderer } from './hero';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
|
import { MapDoorRenderer } from './door';
|
||||||
|
|
||||||
export class MapExtensionManager implements IMapExtensionManager {
|
export class MapExtensionManager implements IMapExtensionManager {
|
||||||
/** 勇士状态至勇士渲染器的映射 */
|
/** 勇士状态至勇士渲染器的映射 */
|
||||||
readonly heroMap: WeakMap<IHeroState, IMapHeroRenderer> = new WeakMap();
|
readonly heroMap: Map<IHeroState, IMapHeroRenderer> = new Map();
|
||||||
|
/** 地图图层到门渲染器的映射 */
|
||||||
|
readonly doorMap: Map<IMapLayer, IMapDoorRenderer> = new Map();
|
||||||
|
|
||||||
constructor(readonly renderer: IMapRenderer) {}
|
constructor(readonly renderer: IMapRenderer) {}
|
||||||
|
|
||||||
addHero(state: IHeroState, layer: IMapLayer): void {
|
addHero(state: IHeroState, layer: IMapLayer): IMapHeroRenderer | null {
|
||||||
if (this.heroMap.has(state)) {
|
if (this.heroMap.has(state)) {
|
||||||
logger.error(45);
|
logger.error(45, 'hero renderer');
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
const heroRenderer = new MapHeroRenderer(this.renderer, layer, state);
|
const heroRenderer = new MapHeroRenderer(this.renderer, layer, state);
|
||||||
this.heroMap.set(state, heroRenderer);
|
this.heroMap.set(state, heroRenderer);
|
||||||
|
return heroRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeHero(state: IHeroState): void {
|
removeHero(state: IHeroState): void {
|
||||||
@ -25,4 +33,28 @@ export class MapExtensionManager implements IMapExtensionManager {
|
|||||||
renderer.destroy();
|
renderer.destroy();
|
||||||
this.heroMap.delete(state);
|
this.heroMap.delete(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addDoor(layer: IMapLayer): IMapDoorRenderer | null {
|
||||||
|
if (this.doorMap.has(layer)) {
|
||||||
|
logger.error(45, 'door renderer');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const doorRenderer = new MapDoorRenderer(this.renderer, layer);
|
||||||
|
this.doorMap.set(layer, doorRenderer);
|
||||||
|
return doorRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDoor(layer: IMapLayer): void {
|
||||||
|
const renderer = this.doorMap.get(layer);
|
||||||
|
if (!renderer) return;
|
||||||
|
renderer.destroy();
|
||||||
|
this.doorMap.delete(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
this.heroMap.forEach(v => void v.destroy());
|
||||||
|
this.doorMap.forEach(v => void v.destroy());
|
||||||
|
this.heroMap.clear();
|
||||||
|
this.doorMap.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,13 +12,28 @@ export interface IMapExtensionManager {
|
|||||||
* @param state 勇士状态
|
* @param state 勇士状态
|
||||||
* @param layer 勇士所在图层
|
* @param layer 勇士所在图层
|
||||||
*/
|
*/
|
||||||
addHero(state: IHeroState, layer: IMapLayer): void;
|
addHero(state: IHeroState, layer: IMapLayer): IMapHeroRenderer | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除勇士渲染拓展
|
* 移除勇士渲染拓展
|
||||||
* @param state 勇士状态
|
* @param state 勇士状态
|
||||||
*/
|
*/
|
||||||
removeHero(state: IHeroState): void;
|
removeHero(state: IHeroState): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加开门动画拓展
|
||||||
|
*/
|
||||||
|
addDoor(layer: IMapLayer): IMapDoorRenderer | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除开门动画拓展
|
||||||
|
*/
|
||||||
|
removeDoor(layer: IMapLayer): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摧毁这个拓展管理对象,释放相关资源
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapHeroRenderer {
|
export interface IMapHeroRenderer {
|
||||||
@ -119,3 +134,31 @@ export interface IMapHeroRenderer {
|
|||||||
*/
|
*/
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IMapDoorRenderer {
|
||||||
|
/**
|
||||||
|
* 开启指定位置的门,播放开门动画
|
||||||
|
* @param x 门横坐标
|
||||||
|
* @param y 门纵坐标
|
||||||
|
*/
|
||||||
|
openDoor(x: number, y: number): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在指定位置执行关门动画
|
||||||
|
* @param num 门图块数字
|
||||||
|
* @param x 门横坐标
|
||||||
|
* @param y 门纵坐标
|
||||||
|
*/
|
||||||
|
closeDoor(num: number, x: number, y: number): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置开关门动画两帧之间的间隔
|
||||||
|
* @param interval 开门动画间隔
|
||||||
|
*/
|
||||||
|
setAnimateInterval(interval: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摧毁这个门动画拓展,释放相关资源
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
|
|||||||
@ -1348,7 +1348,6 @@ export class MapRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLCanvasElement {
|
render(): HTMLCanvasElement {
|
||||||
// todo: 改为 FBO,最后把 FBO 画到画布上
|
|
||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
const data = this.contextData;
|
const data = this.contextData;
|
||||||
if (!this.assetData) {
|
if (!this.assetData) {
|
||||||
@ -1711,6 +1710,10 @@ export class MapRenderer
|
|||||||
this.needUpdateTransform = true;
|
this.needUpdateTransform = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestUpdate(): void {
|
||||||
|
this.updateRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
needUpdate(): boolean {
|
needUpdate(): boolean {
|
||||||
return (
|
return (
|
||||||
this.updateRequired ||
|
this.updateRequired ||
|
||||||
|
|||||||
@ -41,6 +41,11 @@ export interface IMapDataGetter {
|
|||||||
* @param moving 移动图块对象
|
* @param moving 移动图块对象
|
||||||
*/
|
*/
|
||||||
hasMoving(moving: IMovingBlock): boolean;
|
hasMoving(moving: IMovingBlock): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请更新渲染
|
||||||
|
*/
|
||||||
|
requestUpdate(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BlockMapPos {
|
interface BlockMapPos {
|
||||||
@ -106,7 +111,13 @@ export class MapVertexGenerator
|
|||||||
|
|
||||||
/** 空顶点数组,因为空顶点很常用,所以直接定义一个全局常量 */
|
/** 空顶点数组,因为空顶点很常用,所以直接定义一个全局常量 */
|
||||||
private static readonly EMPTY_VETREX: Float32Array = new Float32Array(
|
private static readonly EMPTY_VETREX: Float32Array = new Float32Array(
|
||||||
INSTANCED_COUNT
|
// prettier-ignore
|
||||||
|
[
|
||||||
|
0, 0, 0, 0, // 顶点坐标
|
||||||
|
0, 0, 0, 0, // 纹理坐标
|
||||||
|
0, 1, 0, 0, // a_tileData,不透明度需要设为 1
|
||||||
|
-1, 0, 0, 0, // a_texData,当前帧数需要设为 -1
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
readonly block: IBlockSplitter<MapVertexBlock>;
|
readonly block: IBlockSplitter<MapVertexBlock>;
|
||||||
@ -1044,7 +1055,7 @@ class MapVertexBlock implements IMapVertexBlock {
|
|||||||
* @param blockHeight 分块高度
|
* @param blockHeight 分块高度
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
readonly renderer: IMapRenderer,
|
readonly renderer: IMapRenderer & IMapDataGetter,
|
||||||
originArray: IMapVertexData,
|
originArray: IMapVertexData,
|
||||||
startIndex: number,
|
startIndex: number,
|
||||||
count: number,
|
count: number,
|
||||||
@ -1074,6 +1085,7 @@ class MapVertexBlock implements IMapVertexBlock {
|
|||||||
*/
|
*/
|
||||||
markRenderDirty() {
|
markRenderDirty() {
|
||||||
this.renderDirty = true;
|
this.renderDirty = true;
|
||||||
|
this.renderer.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
markDirty(
|
markDirty(
|
||||||
@ -1101,6 +1113,7 @@ class MapVertexBlock implements IMapVertexBlock {
|
|||||||
data.dirtyBottom = Math.max(db, data.dirtyBottom);
|
data.dirtyBottom = Math.max(db, data.dirtyBottom);
|
||||||
}
|
}
|
||||||
this.dirty = true;
|
this.dirty = true;
|
||||||
|
this.renderer.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirtyArea(layer: IMapLayer): Readonly<ILayerDirtyData> | null {
|
getDirtyArea(layer: IMapLayer): Readonly<ILayerDirtyData> | null {
|
||||||
|
|||||||
@ -33,6 +33,8 @@ export const DYNAMIC_RESERVE = 16;
|
|||||||
* 调整此值可以调整频率,值越大,越不容易因为数量小于预留数量而减小预留。
|
* 调整此值可以调整频率,值越大,越不容易因为数量小于预留数量而减小预留。
|
||||||
*/
|
*/
|
||||||
export const MOVING_TOLERANCE = 60;
|
export const MOVING_TOLERANCE = 60;
|
||||||
|
/** 开关门动画的动画时长 */
|
||||||
|
export const DOOR_ANIMATE_INTERVAL = 50;
|
||||||
|
|
||||||
//#region 状态栏
|
//#region 状态栏
|
||||||
|
|
||||||
|
|||||||
@ -24,33 +24,32 @@ function createCoreState() {
|
|||||||
|
|
||||||
//#region 图块部分
|
//#region 图块部分
|
||||||
|
|
||||||
loading.once('coreInit', () => {
|
const data = Object.entries(core.maps.blocksInfo);
|
||||||
const data = Object.entries(core.maps.blocksInfo);
|
for (const [key, block] of data) {
|
||||||
for (const [key, block] of data) {
|
const num = Number(key);
|
||||||
const num = Number(key);
|
state.idNumberMap.set(block.id, num);
|
||||||
state.idNumberMap.set(block.id, num);
|
state.numberIdMap.set(num, block.id);
|
||||||
state.numberIdMap.set(num, block.id);
|
}
|
||||||
|
|
||||||
|
for (const [key, block] of data) {
|
||||||
|
if (!block.faceIds) continue;
|
||||||
|
const { down, up, left, right } = block.faceIds;
|
||||||
|
const downNum = state.idNumberMap.get(down);
|
||||||
|
if (downNum !== Number(key)) continue;
|
||||||
|
const upNum = state.idNumberMap.get(up);
|
||||||
|
const leftNum = state.idNumberMap.get(left);
|
||||||
|
const rightNum = state.idNumberMap.get(right);
|
||||||
|
state.roleFace.malloc(downNum, FaceDirection.Down);
|
||||||
|
if (!isNil(upNum)) {
|
||||||
|
state.roleFace.bind(upNum, downNum, FaceDirection.Up);
|
||||||
}
|
}
|
||||||
for (const [key, block] of data) {
|
if (!isNil(leftNum)) {
|
||||||
if (!block.faceIds) continue;
|
state.roleFace.bind(leftNum, downNum, FaceDirection.Left);
|
||||||
const { down, up, left, right } = block.faceIds;
|
|
||||||
const downNum = state.idNumberMap.get(down);
|
|
||||||
if (downNum !== Number(key)) continue;
|
|
||||||
const upNum = state.idNumberMap.get(up);
|
|
||||||
const leftNum = state.idNumberMap.get(left);
|
|
||||||
const rightNum = state.idNumberMap.get(right);
|
|
||||||
state.roleFace.malloc(downNum, FaceDirection.Down);
|
|
||||||
if (!isNil(upNum)) {
|
|
||||||
state.roleFace.bind(upNum, downNum, FaceDirection.Up);
|
|
||||||
}
|
|
||||||
if (!isNil(leftNum)) {
|
|
||||||
state.roleFace.bind(leftNum, downNum, FaceDirection.Left);
|
|
||||||
}
|
|
||||||
if (!isNil(rightNum)) {
|
|
||||||
state.roleFace.bind(rightNum, downNum, FaceDirection.Right);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
if (!isNil(rightNum)) {
|
||||||
|
state.roleFace.bind(rightNum, downNum, FaceDirection.Right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export class LayerState
|
|||||||
this.forEachHook(hook => {
|
this.forEachHook(hook => {
|
||||||
hook.onUpdateLayer?.(this.layerList);
|
hook.onUpdateLayer?.(this.layerList);
|
||||||
});
|
});
|
||||||
const controller = layer.addHook(new StateMapLayerHook(this));
|
const controller = layer.addHook(new StateMapLayerHook(this, layer));
|
||||||
this.layerHookMap.set(layer, controller);
|
this.layerHookMap.set(layer, controller);
|
||||||
controller.load();
|
controller.load();
|
||||||
return layer;
|
return layer;
|
||||||
@ -114,38 +114,26 @@ export class LayerState
|
|||||||
}
|
}
|
||||||
|
|
||||||
class StateMapLayerHook implements Partial<IMapLayerHooks> {
|
class StateMapLayerHook implements Partial<IMapLayerHooks> {
|
||||||
constructor(readonly state: LayerState) {}
|
constructor(
|
||||||
|
readonly state: LayerState,
|
||||||
|
readonly layer: IMapLayer
|
||||||
|
) {}
|
||||||
|
|
||||||
onUpdateArea(
|
onUpdateArea(x: number, y: number, width: number, height: number): void {
|
||||||
controller: IMapLayerHookController,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
width: number,
|
|
||||||
height: number
|
|
||||||
): void {
|
|
||||||
this.state.forEachHook(hook => {
|
this.state.forEachHook(hook => {
|
||||||
hook.onUpdateLayerArea?.(controller.layer, x, y, width, height);
|
hook.onUpdateLayerArea?.(this.layer, x, y, width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateBlock(
|
onUpdateBlock(block: number, x: number, y: number): void {
|
||||||
controller: IMapLayerHookController,
|
|
||||||
block: number,
|
|
||||||
x: number,
|
|
||||||
y: number
|
|
||||||
): void {
|
|
||||||
this.state.forEachHook(hook => {
|
this.state.forEachHook(hook => {
|
||||||
hook.onUpdateLayerBlock?.(controller.layer, block, x, y);
|
hook.onUpdateLayerBlock?.(this.layer, block, x, y);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onResize(
|
onResize(width: number, height: number): void {
|
||||||
controller: IMapLayerHookController,
|
|
||||||
width: number,
|
|
||||||
height: number
|
|
||||||
): void {
|
|
||||||
this.state.forEachHook(hook => {
|
this.state.forEachHook(hook => {
|
||||||
hook.onResizeLayer?.(controller.layer, width, height);
|
hook.onResizeLayer?.(this.layer, width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,8 +72,8 @@ export class MapLayer
|
|||||||
expired: false,
|
expired: false,
|
||||||
array: this.mapArray
|
array: this.mapArray
|
||||||
};
|
};
|
||||||
this.forEachHook((hook, controller) => {
|
this.forEachHook(hook => {
|
||||||
hook.onResize?.(controller, width, height);
|
hook.onResize?.(width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +91,8 @@ export class MapLayer
|
|||||||
array: this.mapArray
|
array: this.mapArray
|
||||||
};
|
};
|
||||||
this.empty = true;
|
this.empty = true;
|
||||||
this.forEachHook((hook, controller) => {
|
this.forEachHook(hook => {
|
||||||
hook.onResize?.(controller, width, height);
|
hook.onResize?.(width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +100,8 @@ export class MapLayer
|
|||||||
const index = y * this.width + x;
|
const index = y * this.width + x;
|
||||||
if (block === this.mapArray[index]) return;
|
if (block === this.mapArray[index]) return;
|
||||||
this.mapArray[index] = block;
|
this.mapArray[index] = block;
|
||||||
this.forEachHook((hook, controller) => {
|
this.forEachHook(hook => {
|
||||||
hook.onUpdateBlock?.(controller, block, x, y);
|
hook.onUpdateBlock?.(block, x, y);
|
||||||
});
|
});
|
||||||
if (block !== 0) {
|
if (block !== 0) {
|
||||||
this.empty = false;
|
this.empty = false;
|
||||||
@ -123,8 +123,8 @@ export class MapLayer
|
|||||||
const height = Math.ceil(array.length / width);
|
const height = Math.ceil(array.length / width);
|
||||||
if (width === this.width && height === this.height) {
|
if (width === this.width && height === this.height) {
|
||||||
this.mapArray.set(array);
|
this.mapArray.set(array);
|
||||||
this.forEachHook((hook, controller) => {
|
this.forEachHook(hook => {
|
||||||
hook.onUpdateArea?.(controller, x, y, width, height);
|
hook.onUpdateArea?.(x, y, width, height);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -151,8 +151,8 @@ export class MapLayer
|
|||||||
}
|
}
|
||||||
this.mapArray.set(array.subarray(start, start + nw), offset);
|
this.mapArray.set(array.subarray(start, start + nw), offset);
|
||||||
}
|
}
|
||||||
this.forEachHook((hook, controller) => {
|
this.forEachHook(hook => {
|
||||||
hook.onUpdateArea?.(controller, x, y, width, height);
|
hook.onUpdateArea?.(x, y, width, height);
|
||||||
});
|
});
|
||||||
this.empty &&= empty;
|
this.empty &&= empty;
|
||||||
}
|
}
|
||||||
@ -214,6 +214,33 @@ export class MapLayer
|
|||||||
setZIndex(zIndex: number): void {
|
setZIndex(zIndex: number): void {
|
||||||
this.zIndex = zIndex;
|
this.zIndex = zIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openDoor(x: number, y: number): Promise<void> {
|
||||||
|
const index = y * this.width + x;
|
||||||
|
const num = this.mapArray[index];
|
||||||
|
if (num === 0) return;
|
||||||
|
await Promise.all(
|
||||||
|
this.forEachHook(hook => {
|
||||||
|
return hook.onOpenDoor?.(x, y);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.setBlock(0, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeDoor(num: number, x: number, y: number): Promise<void> {
|
||||||
|
const index = y * this.width + x;
|
||||||
|
const nowNum = this.mapArray[index];
|
||||||
|
if (nowNum !== 0) {
|
||||||
|
logger.error(46, x.toString(), y.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await Promise.all(
|
||||||
|
this.forEachHook(hook => {
|
||||||
|
return hook.onCloseDoor?.(num, x, y);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.setBlock(num, x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapLayerHookController
|
class MapLayerHookController
|
||||||
|
|||||||
@ -10,45 +10,42 @@ export interface IMapLayerData {
|
|||||||
export interface IMapLayerHooks extends IHookBase {
|
export interface IMapLayerHooks extends IHookBase {
|
||||||
/**
|
/**
|
||||||
* 当地图大小发生变化时执行,如果调用了地图的 `resize` 方法,但是地图大小没变,则不会触发
|
* 当地图大小发生变化时执行,如果调用了地图的 `resize` 方法,但是地图大小没变,则不会触发
|
||||||
* @param controller 拓展控制器
|
|
||||||
* @param width 地图宽度
|
* @param width 地图宽度
|
||||||
* @param height 地图高度
|
* @param height 地图高度
|
||||||
*/
|
*/
|
||||||
onResize(
|
onResize(width: number, height: number): void;
|
||||||
controller: IMapLayerHookController,
|
|
||||||
width: number,
|
|
||||||
height: number
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当更新某个区域的图块时执行
|
* 当更新某个区域的图块时执行
|
||||||
* @param controller 拓展控制器
|
|
||||||
* @param x 更新区域左上角横坐标
|
* @param x 更新区域左上角横坐标
|
||||||
* @param y 更新区域左上角纵坐标
|
* @param y 更新区域左上角纵坐标
|
||||||
* @param width 更新区域宽度
|
* @param width 更新区域宽度
|
||||||
* @param height 更新区域高度
|
* @param height 更新区域高度
|
||||||
*/
|
*/
|
||||||
onUpdateArea(
|
onUpdateArea(x: number, y: number, width: number, height: number): void;
|
||||||
controller: IMapLayerHookController,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
width: number,
|
|
||||||
height: number
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当更新某个点的图块时执行,如果设置的图块与原先一样,则不会触发此方法
|
* 当更新某个点的图块时执行,如果设置的图块与原先一样,则不会触发此方法
|
||||||
* @param controller 拓展控制器
|
|
||||||
* @param block 更新为的图块数字
|
* @param block 更新为的图块数字
|
||||||
* @param x 更新点横坐标
|
* @param x 更新点横坐标
|
||||||
* @param y 更新点纵坐标
|
* @param y 更新点纵坐标
|
||||||
*/
|
*/
|
||||||
onUpdateBlock(
|
onUpdateBlock(block: number, x: number, y: number): void;
|
||||||
controller: IMapLayerHookController,
|
|
||||||
block: number,
|
/**
|
||||||
x: number,
|
* 当开门时触发,返回一个 `Promise`,当相关动画执行完毕后兑现
|
||||||
y: number
|
* @param x 门横坐标
|
||||||
): void;
|
* @param y 门纵坐标
|
||||||
|
*/
|
||||||
|
onOpenDoor(x: number, y: number): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当关门时触发,返回一个 `Promise`,当相关动画执行完毕后兑现
|
||||||
|
* @param num 门的图块数字
|
||||||
|
* @param x 门横坐标
|
||||||
|
* @param y 门纵坐标
|
||||||
|
*/
|
||||||
|
onCloseDoor(num: number, x: number, y: number): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapLayerHookController
|
export interface IMapLayerHookController
|
||||||
@ -143,6 +140,21 @@ export interface IMapLayer
|
|||||||
* @param zIndex 纵深
|
* @param zIndex 纵深
|
||||||
*/
|
*/
|
||||||
setZIndex(zIndex: number): void;
|
setZIndex(zIndex: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开启指定位置的门
|
||||||
|
* @param x 门横坐标
|
||||||
|
* @param y 门纵坐标
|
||||||
|
*/
|
||||||
|
openDoor(x: number, y: number): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在指定位置关门,门的图块数字由参数指定
|
||||||
|
* @param num 门图块数字
|
||||||
|
* @param x 门横坐标
|
||||||
|
* @param y 门纵坐标
|
||||||
|
*/
|
||||||
|
closeDoor(num: number, x: number, y: number): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILayerStateHooks extends IHookBase {
|
export interface ILayerStateHooks extends IHookBase {
|
||||||
|
|||||||
@ -19,7 +19,14 @@ export interface ICoreState {
|
|||||||
/** 图块数字到 id 的映射 */
|
/** 图块数字到 id 的映射 */
|
||||||
readonly numberIdMap: Map<number, string>;
|
readonly numberIdMap: Map<number, string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存状态
|
||||||
|
*/
|
||||||
saveState(): IStateSaveData;
|
saveState(): IStateSaveData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载状态
|
||||||
|
* @param data 状态对象
|
||||||
|
*/
|
||||||
loadState(data: IStateSaveData): void;
|
loadState(data: IStateSaveData): void;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -341,9 +341,9 @@ export function initFallback() {
|
|||||||
const locked = core.status.lockControl;
|
const locked = core.status.lockControl;
|
||||||
core.lockControl();
|
core.lockControl();
|
||||||
core.status.replay.animate = true;
|
core.status.replay.animate = true;
|
||||||
core.removeBlock(x, y);
|
|
||||||
|
|
||||||
const cb = () => {
|
const cb = () => {
|
||||||
|
core.removeBlock(x, y);
|
||||||
core.maps._removeBlockFromMap(
|
core.maps._removeBlockFromMap(
|
||||||
core.status.floorId,
|
core.status.floorId,
|
||||||
block
|
block
|
||||||
@ -359,7 +359,8 @@ export function initFallback() {
|
|||||||
callback?.();
|
callback?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
adapters['door-animate']?.all('openDoor', block).then(cb);
|
const layer = state.layer.getLayerByAlias('event')!;
|
||||||
|
layer.openDoor(x, y).then(cb);
|
||||||
|
|
||||||
const animate = fallbackIds++;
|
const animate = fallbackIds++;
|
||||||
core.animateFrame.lastAsyncId = animate;
|
core.animateFrame.lastAsyncId = animate;
|
||||||
@ -406,11 +407,9 @@ export function initFallback() {
|
|||||||
if (core.status.replay.speed === 24) {
|
if (core.status.replay.speed === 24) {
|
||||||
cb();
|
cb();
|
||||||
} else {
|
} else {
|
||||||
adapters['door-animate']
|
const num = state.idNumberMap.get(id)!;
|
||||||
?.all('closeDoor', block)
|
const layer = state.layer.getLayerByAlias('event')!;
|
||||||
.then(() => {
|
layer.closeDoor(num, x, y).then(cb);
|
||||||
cb();
|
|
||||||
});
|
|
||||||
|
|
||||||
const animate = fallbackIds++;
|
const animate = fallbackIds++;
|
||||||
core.animateFrame.lastAsyncId = animate;
|
core.animateFrame.lastAsyncId = animate;
|
||||||
|
|||||||
@ -44,7 +44,8 @@
|
|||||||
"42": "The '$1' property of map-render element is required.",
|
"42": "The '$1' property of map-render element is required.",
|
||||||
"43": "Cannot bind face direction to main block $1, please call malloc in advance.",
|
"43": "Cannot bind face direction to main block $1, please call malloc in advance.",
|
||||||
"44": "Cannot bind face direction to main block $1, since main direction cannot be override.",
|
"44": "Cannot bind face direction to main block $1, since main direction cannot be override.",
|
||||||
"45": "Cannot add hero renderer, sincehero renderer already exists for the given state.",
|
"45": "Cannot add $1 map renderer extension, since $1 already exists for the given state.",
|
||||||
|
"46": "Cannot execute close door action on $1,$2, since the given position is not empty.",
|
||||||
"1101": "Shadow extension needs 'floor-hero' extension as dependency.",
|
"1101": "Shadow extension needs 'floor-hero' extension as dependency.",
|
||||||
"1201": "Floor-damage extension needs 'floor-binder' extension as dependency.",
|
"1201": "Floor-damage extension needs 'floor-binder' extension as dependency.",
|
||||||
"1301": "Portal extension need 'floor-binder' extension as dependency.",
|
"1301": "Portal extension need 'floor-binder' extension as dependency.",
|
||||||
|
|||||||
@ -3197,8 +3197,8 @@ maps.prototype.hideBlockByIndex = function (index, floorId) {
|
|||||||
this._updateMapArray(floorId, block.x, block.y);
|
this._updateMapArray(floorId, block.x, block.y);
|
||||||
Mota.require('@user/data-base').hook.emit(
|
Mota.require('@user/data-base').hook.emit(
|
||||||
'setBlock',
|
'setBlock',
|
||||||
x,
|
block.x,
|
||||||
y,
|
block.y,
|
||||||
floorId,
|
floorId,
|
||||||
0,
|
0,
|
||||||
block?.id ?? 0
|
block?.id ?? 0
|
||||||
@ -3206,7 +3206,7 @@ maps.prototype.hideBlockByIndex = function (index, floorId) {
|
|||||||
if (floorId === core.status.floorId) {
|
if (floorId === core.status.floorId) {
|
||||||
const { layer } = Mota.require('@user/data-state').state;
|
const { layer } = Mota.require('@user/data-state').state;
|
||||||
const event = layer.getLayerByAlias('event');
|
const event = layer.getLayerByAlias('event');
|
||||||
event.setBlock(0, x, y);
|
event.setBlock(0, block.x, block.y);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3283,7 +3283,7 @@ maps.prototype.removeBlockByIndex = function (index, floorId) {
|
|||||||
if (floorId === core.status.floorId) {
|
if (floorId === core.status.floorId) {
|
||||||
const { layer } = Mota.require('@user/data-state').state;
|
const { layer } = Mota.require('@user/data-state').state;
|
||||||
const event = layer.getLayerByAlias('event');
|
const event = layer.getLayerByAlias('event');
|
||||||
event.setBlock(0, x, y);
|
event.setBlock(0, block.x, block.y);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3639,8 +3639,8 @@ maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) {
|
|||||||
this._updateMapArray(floorId, block.x, block.y);
|
this._updateMapArray(floorId, block.x, block.y);
|
||||||
Mota.require('@user/data-base').hook.emit(
|
Mota.require('@user/data-base').hook.emit(
|
||||||
'setBlock',
|
'setBlock',
|
||||||
x,
|
block.x,
|
||||||
y,
|
block.y,
|
||||||
floorId,
|
floorId,
|
||||||
fromNumber,
|
fromNumber,
|
||||||
toNumber
|
toNumber
|
||||||
@ -3648,7 +3648,7 @@ maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) {
|
|||||||
if (floorId === core.status.floorId) {
|
if (floorId === core.status.floorId) {
|
||||||
const { layer } = Mota.require('@user/data-state').state;
|
const { layer } = Mota.require('@user/data-state').state;
|
||||||
const event = layer.getLayerByAlias('event');
|
const event = layer.getLayerByAlias('event');
|
||||||
event.setBlock(toNumber, x, y);
|
event.setBlock(toNumber, block.x, block.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user