refactor: 修复所有的循环引用

This commit is contained in:
AncTe committed 2025-03-09 19:15:22 +08:00
1 parent 4fda246a9a
commit 23a8c0bf3a
82 files changed
+1015 -2087

No files matched your search

@@ -0,0 +1,114 @@
import {
wrapInstancedComponent,
MotaOffscreenCanvas2D,
RenderItem,
RenderItemPosition,
Transform
} from '@motajs/render';
// 渲染端的向后兼容用,会充当两个版本间过渡的作用
class Change extends RenderItem {
private tips: string[] = [];
/** 当前小贴士 */
private usingTip: string = '';
/** 透明度 */
private backAlpha: number = 0;
private title: string = '';
constructor(type: RenderItemPosition) {
super(type, false);
}
/**
* 设置楼传过程中的小贴士
*/
setTips(tip: string[]) {
this.tips = tip;
}
/**
* 设置标题
*/
setTitle(title: string) {
this.title = title;
}
/**
* 显示楼层切换的从透明变黑的动画
* @param time 动画时长
*/
showChange(time: number) {
const length = this.tips.length;
const tip = this.tips[Math.floor(Math.random() * length)] ?? '';
this.usingTip = tip;
return new Promise<void>(res => {
const start = Date.now();
const id = this.delegateTicker(
() => {
const dt = Date.now() - start;
const progress = dt / time;
if (progress > 1) {
this.backAlpha = 1;
this.removeTicker(id);
} else {
this.backAlpha = progress;
}
this.update();
},
10000,
res
);
});
}
/**
* 显示楼层切换从黑到透明的动画
* @param time 动画时长
*/
async hideChange(time: number) {
return new Promise<void>(res => {
const start = Date.now();
const id = this.delegateTicker(
() => {
const dt = Date.now() - start;
const progress = dt / time;
if (progress > 1) {
this.removeTicker(id);
this.backAlpha = 0;
} else {
this.backAlpha = 1 - progress;
}
this.update();
},
10000,
res
);
});
}
protected render(
canvas: MotaOffscreenCanvas2D,
_transform: Transform
): void {
if (this.backAlpha === 0) return;
const ctx = canvas.ctx;
ctx.globalAlpha = this.backAlpha;
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.textAlign = 'center';
ctx.fillStyle = '#fff';
ctx.font = '32px "normal"';
ctx.fillText(this.title, canvas.width / 2, canvas.height * 0.4);
ctx.font = '16px "normal"';
if (this.usingTip.length > 0) {
ctx.fillText(
'小贴士:' + this.usingTip,
canvas.width / 2,
canvas.height * 0.75
);
}
}
}
export const FloorChange = wrapInstancedComponent(() => new Change('static'));
@@ -0,0 +1,60 @@
import { logger } from '@motajs/common';
import {
LayerGroupFloorBinder,
ILayerGroupRenderExtends,
LayerGroup
} from '@motajs/render';
import { loading } from '@user/data-base';
const filterMap: [FloorIds[], string][] = [];
function getCanvasFilterByFloorId(floorId: FloorIds = core.status.floorId) {
return filterMap.find(v => v[0].includes(floorId))?.[1] ?? '';
}
loading.once('coreInit', () => {
filterMap.push(
[['MT50', 'MT60', 'MT61'], 'contrast(120%)'], // 童心佬的滤镜(
[
core.floorIds.slice(61, 70).concat(core.floorIds.slice(72, 107)),
'contrast(120%)'
] // 童心佬的滤镜(
);
});
export class LayerGroupFilter implements ILayerGroupRenderExtends {
id: string = 'filter';
group!: LayerGroup;
binder!: LayerGroupFloorBinder;
setFilter(floorId: FloorIds) {
const filter = getCanvasFilterByFloorId(floorId);
this.group.setFilter(filter);
// console.log(filter);
}
private onFloorChange = (floor: FloorIds) => {
this.setFilter(floor);
};
private listen() {
this.binder.on('floorChange', this.onFloorChange);
}
awake(group: LayerGroup): void {
this.group = group;
const ex = group.getExtends('floor-binder');
if (ex instanceof LayerGroupFloorBinder) {
this.binder = ex;
this.listen();
} else {
logger.error(1201);
group.removeExtends('floor-damage');
}
}
onDestroy(group: LayerGroup): void {
this.binder?.off('floorChange', this.onFloorChange);
}
}
@@ -0,0 +1,141 @@
import { logger } from '@motajs/common';
import { MotaOffscreenCanvas2D } from '@motajs/render';
import { mainSetting } from '@motajs/legacy-ui';
import {
LayerGroupFloorBinder,
ILayerGroupRenderExtends,
LayerGroup,
Sprite,
Transform
} from '@motajs/render';
import { gameListener, hook } from '@user/data-base';
export class LayerGroupHalo implements ILayerGroupRenderExtends {
id: string = 'halo';
group!: LayerGroup;
binder!: LayerGroupFloorBinder;
halo!: Halo;
static sprites: Set<Halo> = new Set();
awake(group: LayerGroup): void {
this.group = group;
const ex = group.getExtends('floor-binder');
if (ex instanceof LayerGroupFloorBinder) {
this.binder = ex;
this.halo = new Halo();
this.halo.setHD(true);
this.halo.size(group.width, group.height);
this.halo.setZIndex(75);
this.halo.binder = ex;
group.appendChild(this.halo);
LayerGroupHalo.sprites.add(this.halo);
} else {
logger.error(1401);
group.removeExtends('halo');
}
}
onDestroy(group: LayerGroup): void {
this.halo?.destroy();
LayerGroupHalo.sprites.delete(this.halo);
}
}
const haloColor: Record<number, string[]> = {
21: ['cyan'],
25: ['purple'],
26: ['blue'],
27: ['red'],
31: ['#3CFF49'],
29: ['#51E9FF'],
32: ['#fff966']
};
class Halo extends Sprite {
/** 单元格大小 */
cellSize: number = 32;
/** 当前楼层,用于获取有哪些光环 */
binder!: LayerGroupFloorBinder;
constructor() {
super('static', true);
this.setRenderFn((canvas, transform) => {
this.drawHalo(canvas, transform);
});
}
drawHalo(canvas: MotaOffscreenCanvas2D, transform: Transform) {
if (!mainSetting.getValue('screen.halo', true)) return;
const floorId = this.binder.getFloor();
if (!floorId) return;
const col = core.status.maps[floorId].enemy;
if (!col) return;
const [dx, dy] = col.translation;
const list = col.haloList.concat(
Object.keys(flags[`melt_${floorId}`] ?? {}).map(v => {
const [x, y] = v.split(',').map(v => parseInt(v));
return {
type: 'square',
data: {
x: x + dx,
y: y + dy,
d: 3
},
special: 25
};
})
);
const { ctx } = canvas;
const cell = this.cellSize;
ctx.lineWidth = 1;
for (const halo of list) {
if (halo.type === 'square') {
const { x, y, d } = halo.data;
let [color, border] = haloColor[halo.special];
let alpha = 0.1;
let borderAlpha = 0.6;
const { mouseX, mouseY } = gameListener;
if (mouseX === halo.from?.x && mouseY === halo.from?.y) {
alpha = 0.3;
borderAlpha = 0.8;
color = '#ff0';
border = '#ff0';
}
const r = Math.floor(d / 2);
const left = x - r;
const top = y - r;
ctx.fillStyle = color;
ctx.strokeStyle = border ?? color;
ctx.globalAlpha = alpha;
ctx.fillRect(left * cell, top * cell, d * cell, d * cell);
ctx.globalAlpha = borderAlpha;
ctx.strokeRect(left * cell, top * cell, d * cell, d * cell);
}
}
}
}
function updateHalo(block: Block) {
if (block.event.cls === 'enemys' || block.event.cls === 'enemy48') {
LayerGroupHalo.sprites.forEach(v => {
const floor = v.binder.getFloor();
if (floor === core.status.floorId) {
v.update();
}
});
}
}
hook.on('enemyExtract', col => {
LayerGroupHalo.sprites.forEach(v => {
const floor = v.binder.getFloor();
if (col.floorId === floor) {
v.update();
}
});
});
gameListener.on('hoverBlock', updateHalo);
gameListener.on('leaveBlock', updateHalo);
@@ -0,0 +1,103 @@
import {
wrapInstancedComponent,
RenderItem,
RenderItemPosition,
Transform,
MotaOffscreenCanvas2D
} from '@motajs/render';
import { TimingFn } from 'mutate-animate';
interface PopData {
cx: number;
cy: number;
path: TimingFn<2>;
text: string;
time: number;
start: number;
color: CanvasStyle;
}
function parabola(input: number): [number, number] {
const x = input * 100;
return [x, x ** 2 / 20 - 3 * x];
}
export class Pop extends RenderItem {
private popList: Set<PopData> = new Set();
private delegation: number = 0;
constructor(type: RenderItemPosition) {
super(type, false);
this.delegation = this.delegateTicker(() => {
if (this.popList.size > 0) this.update();
});
}
/**
* 添加一个弹出文字
* @param text 要显示的文字
* @param time 持续时长
* @param cx 中心点,也就是从哪弹出的
* @param cy 中心点,也就是从哪弹出的
* @param path 自定义路径,不填表示默认的抛物线路径
*/
addPop(
text: string,
time: number,
cx: number,
cy: number,
color: CanvasStyle,
path?: TimingFn<2>
) {
this.popList.add({
text,
time,
cx,
cy,
color,
path: path ?? parabola,
start: Date.now()
});
this.update();
}
protected render(
canvas: MotaOffscreenCanvas2D,
_transform: Transform
): void {
const ctx = canvas.ctx;
const toDelete = new Set<PopData>();
const now = Date.now();
ctx.strokeStyle = '#000';
ctx.font = '22px Verdana';
ctx.lineWidth = 3;
this.popList.forEach(v => {
const { cx, cy, path, text, color, time, start } = v;
const dt = now - start;
const progress = dt / time;
if (progress >= 1) {
toDelete.add(v);
return;
}
const [x, y] = path(progress);
const dx = cx + x;
const dy = cy + y;
ctx.globalAlpha = Math.min(1, 2 - progress * 2);
ctx.fillStyle = color;
ctx.strokeText(text, dx, dy);
ctx.fillText(text, dx, dy);
});
toDelete.forEach(v => {
this.popList.delete(v);
});
}
destroy(): void {
super.destroy();
this.removeTicker(this.delegation);
}
}
export const PopText = wrapInstancedComponent(() => new Pop('static'));
@@ -0,0 +1,296 @@
import { logger } from '@motajs/common';
import { MotaOffscreenCanvas2D } from '@motajs/render';
import { mainSetting, MotaSettingItem } from '@motajs/legacy-ui';
import {
LayerGroupFloorBinder,
ILayerGroupRenderExtends,
LayerGroup,
Sprite
} from '@motajs/render';
import { BluePalace } from '@user/data-state';
/** 最大粒子数 */
const MAX_PARTICLES = 10;
/** 粒子持续时长 */
const PARTICLE_LAST = 2000;
/** 粒子产生间隔 */
const PARTICLE_INTERVAL = PARTICLE_LAST / MAX_PARTICLES;
export class LayerGroupPortal implements ILayerGroupRenderExtends {
id: string = 'portal';
group!: LayerGroup;
binder!: LayerGroupFloorBinder;
portal!: Portal;
private onFloorChange = (floor: FloorIds) => {
const data = BluePalace.portals;
this.portal.cellSize = this.group.cellSize;
this.portal.setData(data[floor] ?? []);
};
private listen() {
this.binder.on('floorChange', this.onFloorChange);
}
awake(group: LayerGroup): void {
this.group = group;
const ex = group.getExtends('floor-binder');
if (ex instanceof LayerGroupFloorBinder) {
this.binder = ex;
this.portal = new Portal();
this.portal.setHD(true);
this.portal.size(group.width, group.height);
group.appendChild(this.portal);
this.listen();
} else {
logger.error(1301);
group.removeExtends('portal');
}
}
onDestroy(group: LayerGroup): void {
this.binder.off('floorChange', this.onFloorChange);
}
}
interface DrawingPortal {
color: string;
x: number;
y: number;
particles: Set<PortalParticle>;
/** v表示竖向,h表示横向 */
type: 'v' | 'h';
/** 上一次新增粒子的时间 */
lastParticle: number;
}
interface PortalParticle {
fx: number;
fy: number;
totalTime: number;
time: number;
tx: number;
ty: number;
r: number;
}
export class Portal extends Sprite {
static colors: string[] = ['#0f0', '#ff0', '#0ff', '#fff', '#f0f'];
cellSize: number = 32;
/** 当前的渲染数据 */
private renderData: BluePalace.Portal[] = [];
/** 渲染内容 */
private renderable: Set<DrawingPortal> = new Set();
/** 粒子开关设置 */
private particleSetting: MotaSettingItem;
/** 上一帧时刻 */
private lastTime: number = 0;
private delegation: number;
constructor() {
super('static', false, true);
this.particleSetting = mainSetting.getSetting('fx.portalParticle')!;
this.delegation = this.delegateTicker(() => {
if (this.particleSetting.value && this.renderable.size > 0) {
this.update(this);
}
});
this.setZIndex(35);
this.setRenderFn((canvas, transform) => {
this.renderPortal(canvas);
});
}
/**
* 设置渲染内容
*/
setData(data: BluePalace.Portal[]) {
this.renderData = data;
this.generateRenderable();
this.update(this);
}
private generateRenderable() {
this.renderable.clear();
if (this.renderData.length === 0) return;
const colorLength = Portal.colors.length;
const cell = this.cellSize;
this.renderData.forEach((v, i) => {
const c = Portal.colors[i % colorLength];
const { fx, fy, tx, ty, dir, toDir } = v;
let x1 = fx * cell;
let y1 = fy * cell;
let x2 = tx * cell;
let y2 = ty * cell;
if (dir === 'down') y1 += cell;
else if (dir === 'right') x1 += cell;
if (toDir === 'down') y2 += cell;
else if (toDir === 'right') x2 += cell;
this.renderable.add({
x: x1,
y: y1,
type: dir === 'left' || dir === 'right' ? 'v' : 'h',
color: c,
particles: new Set(),
lastParticle: Date.now()
});
this.renderable.add({
x: x2,
y: y2,
type: toDir === 'left' || toDir === 'right' ? 'v' : 'h',
color: c,
particles: new Set(),
lastParticle: Date.now()
});
});
}
private renderPortal(canvas: MotaOffscreenCanvas2D) {
if (this.renderable.size === 0) return;
const { ctx } = canvas;
const p = this.particleSetting.value;
ctx.lineCap = 'round';
ctx.lineWidth = 3;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
if (p) {
ctx.shadowBlur = 8;
} else {
ctx.shadowBlur = 0;
}
const time = Date.now();
const dt = time - this.lastTime;
this.lastTime = time;
this.renderable.forEach(v => {
const { color, x, y, type, lastParticle, particles } = v;
ctx.strokeStyle = color;
ctx.fillStyle = color;
ctx.globalAlpha = 1;
ctx.shadowColor = color;
if (type === 'v') {
ctx.beginPath();
ctx.moveTo(x, y - 14);
ctx.lineTo(x, y + 30);
ctx.stroke();
} else {
ctx.beginPath();
ctx.moveTo(x + 2, y);
ctx.lineTo(x + 30, y);
ctx.stroke();
}
if (!p) return;
const needDelete = new Set<PortalParticle>();
particles.forEach(v => {
const { fx, fy, tx, ty, time: t, totalTime, r } = v;
const progress = t / totalTime;
const nx = (tx - fx) * progress + fx;
const ny = (ty - fy) * progress + fy;
v.time += dt;
if (progress > 1) {
needDelete.add(v);
return;
} else if (progress > 0.75) {
ctx.globalAlpha = (1 - progress) * 4;
} else if (progress < 0.25) {
ctx.globalAlpha = progress * 4;
} else {
ctx.globalAlpha = 1;
}
ctx.beginPath();
ctx.arc(nx, ny, r, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
});
needDelete.forEach(v => {
particles.delete(v);
});
if (
time - lastParticle >= PARTICLE_INTERVAL &&
particles.size < MAX_PARTICLES
) {
// 添加新粒子
const direction = Math.random();
const k = Math.random() / 2 - 0.3;
const verticle = Math.floor(Math.random() * 8 + 8);
const r = Math.random() * 2;
v.lastParticle = time;
if (direction > 0.5) {
// 左边 | 上边
if (type === 'h') {
const fx = Math.floor(Math.random() * 24 + x + 4);
particles.add({
fx: fx,
fy: y - 1,
tx: verticle * k + fx + 4,
ty: -verticle + y - 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
} else {
const fy = Math.floor(Math.random() * 44 + y - 14);
particles.add({
fy: fy,
fx: x - 1,
ty: verticle * k + fy + 4,
tx: -verticle + x - 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
}
} else {
// 右边 | 下边
if (type === 'h') {
const fx = Math.floor(Math.random() * 24 + x + 4);
particles.add({
fx: fx,
fy: y + 1,
tx: verticle * k + fx + 4,
ty: verticle + y - 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
} else {
const fy = Math.floor(Math.random() * 44 + y - 14);
particles.add({
fy: fy,
fx: x + 1,
ty: verticle * k + fy + 4,
tx: verticle + x + 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
}
}
}
});
}
destroy(): void {
super.destroy();
this.removeTicker(this.delegation);
}
}
@@ -11,13 +11,6 @@ import {
Font
} from '@motajs/render';
import { WeatherController } from '../../weather';
import {
FloorChange,
LayerGroupFilter,
LayerGroupHalo,
LayerGroupPortal,
PopText
} from '@user/legacy-plugin-client';
import { defineComponent, onMounted, reactive, ref } from 'vue';
import { Textbox, Tip } from '../components';
import { GameUI, UIController } from '@motajs/system-ui';
@@ -40,6 +33,11 @@ import { jumpIgnoreFloor } from '@user/legacy-plugin-data';
import { hook } from '@user/data-base';
import { FloorDamageExtends } from '../damage';
import { FloorItemDetail } from '../itemDetail';
import { LayerGroupPortal } from '../legacy/portal';
import { LayerGroupFilter } from '../legacy/gameCanvas';
import { LayerGroupHalo } from '../legacy/halo';
import { FloorChange } from '../legacy/fallback';
import { PopText } from '../legacy/pop';
const MainScene = defineComponent(() => {
const layerGroupExtends: ILayerGroupRenderExtends[] = [
@@ -13,17 +13,14 @@ import {
StepForward,
ViewMapIcon
} from '../components/icons';
import {
generateBinary,
getVitualKeyOnce,
openDanmakuPoster
} from '@motajs/legacy-ui';
import { getVitualKeyOnce, openDanmakuPoster } from '@motajs/legacy-ui';
import { gameKey } from '@motajs/system-action';
import { generateKeyboardEvent } from '@motajs/system-action';
import { transitioned } from '../use';
import { linear } from 'mutate-animate';
import { KeyCode } from '@motajs/client-base';
import { Progress } from '../components/misc';
import { generateBinary } from '@motajs/legacy-common';
interface ToolbarProps extends DefaultProps {
loc?: ElementLocator;