feat: 开关门动画

This commit is contained in:
unanmed 2024-08-26 22:47:24 +08:00
parent e5afdb73fd
commit 053de036ec
6 changed files with 241 additions and 10 deletions

View File

@ -1,6 +1,6 @@
import { FloorItemDetail } from '@/plugin/fx/itemDetail';
import { FloorDamageExtends } from './preset/damage';
import { LayerGroupFloorBinder } from './preset/floor';
import { LayerDoorAnimate, LayerGroupFloorBinder } from './preset/floor';
import { HeroRenderer } from './preset/hero';
import { LayerGroup, FloorLayer } from './preset/layer';
import { MotaRenderer } from './render';
@ -26,9 +26,11 @@ Mota.require('var', 'loading').once('loaded', () => {
const damage = new FloorDamageExtends();
const hero = new HeroRenderer();
const detail = new FloorItemDetail();
const door = new LayerDoorAnimate();
layer.extends(damage);
layer.extends(detail);
layer.getLayer('event')?.extends(hero);
layer.getLayer('event')?.extends(door);
render.appendChild(layer);
});

View File

@ -4,9 +4,12 @@ import {
ILayerGroupRenderExtends,
ILayerRenderExtends,
Layer,
LayerGroup
LayerGroup,
LayerMovingRenderable
} from './layer';
import { texture } from '../cache';
import { sleep } from 'mutate-animate';
import { RenderAdapter } from '../adapter';
const hook = Mota.require('var', 'hook');
@ -281,16 +284,105 @@ export class LayerFloorBinder implements ILayerRenderExtends {
}
}
export class LayerOpenDoorAnimate implements ILayerRenderExtends {
id: string = 'open-door-animate';
interface DoorAnimateRenderable {
renderable: LayerMovingRenderable;
count: number;
perTime: number;
}
export class LayerDoorAnimate implements ILayerRenderExtends {
id: string = 'door-animate';
layer!: Layer;
private moving: Set<LayerMovingRenderable> = new Set();
private getRenderable(block: Block): DoorAnimateRenderable | null {
const { x, y, id } = block;
const renderable = texture.getRenderable(id);
if (!renderable) return null;
const image = renderable.autotile
? renderable.image[0]
: renderable.image;
const time = block.event.doorInfo?.time ?? 160;
const frame = renderable.render.length;
const perTime = time / frame;
const data: LayerMovingRenderable = {
x,
y,
zIndex: y,
image,
autotile: false,
animate: 0,
frame,
bigImage: false,
render: renderable.render
};
return { renderable: data, count: frame, perTime };
}
/**
*
* @param block
*/
async openDoor(block: Block) {
const renderable = this.getRenderable(block);
if (!renderable) return Promise.reject();
const { renderable: data, count: frame, perTime } = renderable;
data.animate = 0;
this.moving.add(data);
let now = 0;
while (now < frame) {
await sleep(perTime);
data.animate = ++now;
this.layer.update(this.layer);
}
this.moving.delete(data);
return Promise.resolve();
}
/**
*
* @param block
*/
async closeDoor(block: Block) {
const renderable = this.getRenderable(block);
if (!renderable) return Promise.reject();
const { renderable: data, count: frame, perTime } = renderable;
data.animate = frame - 1;
this.moving.add(data);
let now = 0;
while (now >= 0) {
await sleep(perTime);
data.animate = --now;
this.layer.update(this.layer);
}
this.moving.delete(data);
return Promise.resolve();
}
awake(layer: Layer) {
this.layer = layer;
doorAdapter.add(this);
}
openDoor(x: number, y: number) {}
closeDoor(x: number, y: number) {}
onMovingUpdate(layer: Layer, renderable: LayerMovingRenderable[]): void {
renderable.push(...this.moving);
}
onDestroy(layer: Layer): void {
doorAdapter.remove(this);
}
}
const doorAdapter = new RenderAdapter<LayerDoorAnimate>('door-animate');
doorAdapter.recieve('openDoor', (item, block: Block) => {
return item.openDoor(block);
});
doorAdapter.recieve('closeDoor', (item, block: Block) => {
return item.closeDoor(block);
});

View File

@ -1,19 +1,26 @@
import type { RenderAdapter } from '@/core/render/adapter';
import type { LayerDoorAnimate } from '@/core/render/preset/floor';
import type { HeroRenderer } from '@/core/render/preset/hero';
import { hook } from '@/game/game';
interface Adapters {
'hero-adapter'?: RenderAdapter<HeroRenderer>;
'door-animate'?: RenderAdapter<LayerDoorAnimate>;
}
const adapters: Adapters = {};
export function init() {
const hook = Mota.require('var', 'hook');
let fallbackIds: number = 1e8;
if (!main.replayChecking && main.mode === 'play') {
const Adapter = Mota.require('module', 'Render').RenderAdapter;
const hero = Adapter.get<HeroRenderer>('hero-adapter');
const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate');
adapters['hero-adapter'] = hero;
adapters['door-animate'] = doorAnimate;
}
let moving: boolean = false;
@ -280,6 +287,134 @@ export function init() {
moveDir = core.status.hero.loc.direction;
stepDir = moveDir;
});
// ----- 开关门
events.prototype.openDoor = function (
x: number,
y: number,
needKey: boolean,
callback?: () => void
) {
var block = core.getBlock(x, y);
core.saveAndStopAutomaticRoute();
if (!this._openDoor_check(block, x, y, needKey)) {
var locked = core.status.lockControl;
core.waitHeroToStop(function () {
if (!locked) core.unlockControl();
if (callback) callback();
});
return;
}
if (core.status.replay.speed === 24) {
core.status.replay.animate = true;
core.removeBlock(x, y);
setTimeout(function () {
core.status.replay.animate = false;
Mota.require('var', 'hook').emit(
'afterOpenDoor',
block.event.id,
x,
y
);
if (callback) callback();
}, 1); // +1是为了录像检测系统
} else {
const locked = core.status.lockControl;
core.lockControl();
core.status.replay.animate = true;
core.removeBlock(x, y);
const cb = () => {
core.maps._removeBlockFromMap(core.status.floorId, block);
if (!locked) core.unlockControl();
core.status.replay.animate = false;
hook.emit('afterOpenDoor', block.event.id, x, y);
callback?.();
};
adapters['door-animate']?.all('openDoor', block).then(cb);
const animate = fallbackIds++;
core.animateFrame.lastAsyncId = animate;
core.animateFrame.asyncId[animate] = cb;
this._openDoor_animate(block, x, y, callback);
}
};
events.prototype.closeDoor = function (
x: number,
y: number,
id: AllIds,
callback?: () => void
) {
id = id || '';
if (
// @ts-ignore
(core.material.icons.animates[id] == null &&
// @ts-ignore
core.material.icons.npc48[id] == null) ||
core.getBlock(x, y) != null
) {
if (callback) callback();
return;
}
var block = core.getBlockById(id);
var doorInfo = (block.event || {}).doorInfo;
if (!doorInfo) {
if (callback) callback();
return;
}
core.playSound(doorInfo.closeSound);
const locked = core.status.lockControl;
core.lockControl();
core.status.replay.animate = true;
const cb = function () {
if (!locked) core.unlockControl();
core.status.replay.animate = false;
core.setBlock(id, x, y);
core.showBlock(x, y);
callback?.();
};
if (core.status.replay.speed === 24) {
cb();
} else {
adapters['door-animate']?.all('closeDoor', block).then(() => {
cb();
});
const animate = fallbackIds++;
core.animateFrame.lastAsyncId = animate;
core.animateFrame.asyncId[animate] = cb;
this._openDoor_animate(block, x, y, callback);
}
// var blockInfo = core.getBlockInfo(block);
// var speed = (doorInfo.time || 160) / 4;
// blockInfo.posX = 3;
// core.maps._drawBlockInfo(blockInfo, x, y);
// var animate = window.setInterval(
// function () {
// blockInfo.posX--;
// if (blockInfo.posX < 0) {
// clearInterval(animate);
// delete core.animateFrame.asyncId[animate];
// cb();
// return;
// }
// core.maps._drawBlockInfo(blockInfo, x, y);
// },
// core.status.replay.speed == 24
// ? 1
// : speed / Math.max(core.status.replay.speed, 1)
// );
// core.animateFrame.lastAsyncId = animate;
// core.animateFrame.asyncId[animate] = cb;
};
});
return { readyMove, endMove, move };

2
src/types/core.d.ts vendored
View File

@ -275,7 +275,7 @@ interface AnimateFrame {
/**
* id
*/
readonly lastAsyncId: number;
lastAsyncId: number;
}
interface Weather {

2
src/types/map.d.ts vendored
View File

@ -1403,6 +1403,8 @@ interface Maps {
floorId: FloorIds,
noCache?: boolean
): number[][];
_removeBlockFromMap(floorId: FloorIds, block: Block): void;
}
declare const maps: new () => Maps;

View File

@ -661,7 +661,7 @@ interface InitGameStatus {
/**
*
*/
replay: DeepReadonly<ReplayStatus>;
replay: ReplayStatus;
/**
*