refactor: 完全修改dom结构

This commit is contained in:
AncTe committed 2024-11-19 22:28:48 +08:00
1 parent 4e21f60696
commit b87a2b3e2f
32 files changed
+370 -1902

No files matched your search

+24 -217
View File
@@ -1,7 +1,4 @@
import { parseCss } from '@/plugin/utils';
import { EventEmitter } from 'eventemitter3';
import { CSSObj } from '../interface';
import { isWebGL2Supported } from './webgl';
interface OffscreenCanvasEvent {
/** 当被动触发resize时(例如core.domStyle.scale变化、窗口大小变化)时触发,使用size函数并不会触发 */
@@ -29,10 +26,15 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
/** 更新标识符,如果发生变化则说明画布被动清空 */
symbol: number = 0;
constructor(alpha: boolean = true) {
/**
* 创建一个新的离屏画布
* @param alpha 是否启用透明度通道
* @param canvas 指定画布,不指定时会自动创建一个新画布
*/
constructor(alpha: boolean = true, canvas?: HTMLCanvasElement) {
super();
this.canvas = document.createElement('canvas');
this.canvas = canvas ?? document.createElement('canvas');
this.ctx = this.canvas.getContext('2d', { alpha })!;
this.width = this.canvas.width / devicePixelRatio;
this.height = this.canvas.height / devicePixelRatio;
@@ -45,8 +47,9 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
*/
size(width: number, height: number) {
let ratio = this.highResolution ? devicePixelRatio : 1;
if (this.autoScale && this.highResolution) {
ratio *= core.domStyle.scale;
const scale = core.domStyle.scale;
if (this.autoScale) {
ratio *= scale;
}
this.scale = ratio;
this.canvas.width = width * ratio;
@@ -56,6 +59,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.scale(ratio, ratio);
this.ctx.imageSmoothingEnabled = this.antiAliasing;
if (this.canvas.isConnected) {
this.canvas.style.width = `${width * scale}px`;
this.canvas.style.height = `${height * scale}px`;
}
}
/**
@@ -100,7 +107,7 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
}
/**
* 复制一个离屏Canvas2D对象或Canvas2D对象,一般用于缓存等操作
* 复制一个离屏Canvas2D对象,一般用于缓存等操作
* @param canvas 被复制的MotaOffscreenCanvas2D对象
* @returns 复制结果
*/
@@ -118,220 +125,20 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
);
return newCanvas;
}
}
export class MotaOffscreenCanvasGL2 extends EventEmitter<OffscreenCanvasEvent> {
static support: boolean = isWebGL2Supported();
static list: Set<MotaOffscreenCanvasGL2> = new Set();
canvas: HTMLCanvasElement;
gl: WebGL2RenderingContext;
width: number;
height: number;
/** 是否自动跟随样板的core.domStyle.scale进行缩放 */
autoScale: boolean = false;
/** 是否是高清画布 */
highResolution: boolean = true;
scale: number = 1;
/** 更新标识符,如果发生变化则说明画布被动清空 */
symbol: number = 0;
constructor() {
super();
this.canvas = document.createElement('canvas');
this.gl = this.canvas.getContext('webgl2')!;
this.width = this.canvas.width / devicePixelRatio;
this.height = this.canvas.height / devicePixelRatio;
}
/**
* 设置画布的大小
*/
size(width: number, height: number) {
let ratio = this.highResolution ? devicePixelRatio : 1;
if (this.autoScale && this.highResolution) {
ratio *= core.domStyle.scale;
}
this.scale = ratio;
this.canvas.width = width * ratio;
this.canvas.height = height * ratio;
this.width = width;
this.height = height;
}
/**
* 设置当前画布是否跟随样板的 core.domStyle.scale 一同进行缩放
*/
withGameScale(auto: boolean) {
this.autoScale = auto;
this.size(this.width, this.height);
}
/**
* 设置当前画布是否为高清画布
*/
setHD(hd: boolean) {
this.highResolution = hd;
this.size(this.width, this.height);
}
/**
* 删除这个画布
*/
delete() {
MotaOffscreenCanvasGL2.list.delete(this);
}
}
export class MotaCanvas2D extends MotaOffscreenCanvas2D {
static map: Map<string, MotaCanvas2D> = new Map();
id: string = '';
x: number = 0;
y: number = 0;
private mounted: boolean = false;
private target!: HTMLElement;
/** 是否自动跟随样板的core.domStyle.scale进行缩放 */
autoScale: boolean = false;
/** 是否是高清画布 */
highResolution: boolean = true;
constructor(
id: string = '',
setTarget: boolean = true,
alpha: boolean = true
) {
super();
this.id = id;
if (setTarget) this.target = core.dom.gameDraw;
this.canvas = document.createElement('canvas');
this.canvas.id = id;
this.ctx = this.canvas.getContext('2d', { alpha })!;
this.width = this.canvas.width / devicePixelRatio;
this.height = this.canvas.height / devicePixelRatio;
this.canvas.style.position = 'absolute';
MotaCanvas2D.map.set(this.id, this);
}
/**
* 修改画布的挂载目标,如果已经被挂载,那么会被重新挂载至新的目标元素
* @param target 画布将被挂载的目标
*/
setTarget(target: HTMLElement) {
this.target = target;
if (this.mounted) {
this.unmount();
this.mount();
}
}
/**
* 设置画布的大小
*/
size(width: number, height: number) {
let ratio = this.highResolution ? devicePixelRatio : 1;
if (this.autoScale) {
const scale = core.domStyle.scale;
if (this.highResolution) ratio *= scale;
this.canvas.style.width = `${width * scale}px`;
this.canvas.style.height = `${height * scale}px`;
} else {
this.canvas.style.width = `${width}px`;
this.canvas.style.height = `${height}px`;
}
this.scale = ratio;
this.canvas.width = width * ratio;
this.canvas.height = height * ratio;
this.width = width;
this.height = height;
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.scale(ratio, ratio);
}
/**
* 设置画布的位置
*/
pos(x: number, y: number) {
this.canvas.style.left = `${x}px`;
this.canvas.style.top = `${y}px`;
this.x = x;
this.y = y;
}
/**
* 设置画布的css
* @param css 要设置成的css
*/
css(css: string | CSSObj) {
const s = typeof css === 'string' ? parseCss(css) : css;
for (const [key, value] of Object.entries(s)) {
this.canvas.style[key as CanParseCss] = value;
}
}
/**
* 删除这个画布
*/
delete() {
super.delete();
this.unmount();
MotaCanvas2D.map.delete(this.id);
}
/**
* 将这个画布添加至游戏画布
*/
mount() {
if (!this.mounted) {
this.mounted = true;
this.target.appendChild(this.canvas);
}
}
/**
* 将这个画布从页面上移除
*/
unmount() {
if (this.mounted) {
this.mounted = false;
this.canvas.remove();
}
}
/**
* 类似于 Symbol.for
*/
static for(id: string, setTarget?: boolean) {
const canvas = this.map.get(id);
return canvas ?? new MotaCanvas2D(id, setTarget);
static refreshAll() {
this.list.forEach(v => {
if (v.autoScale) {
v.size(v.width, v.height);
v.symbol++;
v.emit('resize');
}
});
}
}
window.addEventListener('resize', () => {
requestAnimationFrame(() => {
MotaOffscreenCanvas2D.list.forEach(v => {
if (v.autoScale) {
v.size(v.width, v.height);
v.symbol++;
v.emit('resize');
}
});
MotaOffscreenCanvasGL2.list.forEach(v => {
if (v.autoScale) {
v.size(v.width, v.height);
v.symbol++;
v.emit('resize');
}
});
MotaOffscreenCanvas2D.refreshAll();
});
});
-241
View File
@@ -1,241 +0,0 @@
import { Ticker } from 'mutate-animate';
import { MotaCanvas2D } from '@/core/fx/canvas2d';
import { MotaSettingItem, mainSetting } from '@/core/main/setting';
// 苍蓝殿左上角区域的传送门机制的绘制部分,传送部分看 src/game/machanism/misc.ts
interface DrawingPortal {
color: string;
x: number;
y: number;
particles: PortalParticle[];
/** v表示竖向,h表示横向 */
type: 'v' | 'h';
/** 上一次新增粒子的时间 */
lastParticle: number;
}
interface PortalParticle {
fx: number;
fy: number;
totalTime: number;
time: number;
tx: number;
ty: number;
r: number;
}
const MAX_PARTICLES = 10;
const PARTICLE_LAST = 2000;
const PARTICLE_INTERVAL = PARTICLE_LAST / MAX_PARTICLES;
const color: string[] = ['#0f0', '#ff0', '#0ff', '#fff', '#f0f'];
const drawing: DrawingPortal[] = [];
const ticker = new Ticker();
let canvas: MotaCanvas2D;
let ctx: CanvasRenderingContext2D;
let particleSetting: MotaSettingItem;
let lastTime = 0;
// Mota.require('var', 'loading').once('coreInit', () => {
// canvas = MotaCanvas2D.for('@portal');
// ctx = canvas.ctx;
// canvas.mount();
// canvas.css(`z-index: 51`);
// canvas.withGameScale(true);
// canvas.pos(0, 0);
// canvas.size(480, 480);
// canvas.on('resize', () => {
// canvas.css(`z-index: 51`);
// });
// particleSetting = mainSetting.getSetting('fx.portalParticle')!;
// ticker.add(tickPortal);
// });
// Mota.require('var', 'hook').on('changingFloor', id => {
// drawPortals(id);
// });
let needDraw = false;
function tickPortal(time: number) {
const last = lastTime;
lastTime = time;
const p = particleSetting.value;
if (!core.isPlaying() || drawing.length === 0) return;
if (!p && !needDraw) return;
needDraw = false;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.lineCap = 'round';
ctx.lineWidth = 3;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
if (p) {
ctx.shadowBlur = 8;
} else {
ctx.shadowBlur = 0;
}
drawing.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) {
// 绘制粒子效果
let needDelete = false;
const dt = time - last;
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 = true;
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();
});
if (needDelete) {
particles.shift();
}
if (
time - lastParticle >= PARTICLE_INTERVAL &&
particles.length < 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.push({
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.push({
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.push({
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.push({
fy: fy,
fx: x + 1,
ty: verticle * k + fy + 4,
tx: verticle + x + 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
}
}
}
}
});
}
/**
* 绘制传送门
* @param floorId 要绘制传送门的楼层
*/
export function drawPortals(floorId: FloorIds) {
drawing.splice(0);
const p = Mota.require('module', 'Mechanism').BluePalace.portals[floorId];
if (!p) return;
p.forEach((v, i) => {
const c = color[i % color.length];
const { fx, fy, tx, ty, dir, toDir } = v;
let x1 = fx * 32;
let y1 = fy * 32;
let x2 = tx * 32;
let y2 = ty * 32;
if (dir === 'down') y1 += 32;
else if (dir === 'right') x1 += 32;
if (toDir === 'down') y2 += 32;
else if (toDir === 'right') x2 += 32;
drawing.push({
x: x1,
y: y1,
type: dir === 'left' || dir === 'right' ? 'v' : 'h',
color: c,
particles: [],
lastParticle: lastTime
});
drawing.push({
x: x2,
y: y2,
type: toDir === 'left' || toDir === 'right' ? 'v' : 'h',
color: c,
particles: [],
lastParticle: lastTime
});
});
needDraw = true;
}
+1 -1
View File
@@ -990,7 +990,7 @@ export class Shadow {
}
static mount() {
core.dom.gameDraw.appendChild(this.canvas);
// core.dom.gameDraw.appendChild(this.canvas);
}
/**
+3 -6
View File
@@ -60,8 +60,6 @@ import { ResourceController } from './loader/controller';
import { logger } from './common/logger';
import { Danmaku } from './main/custom/danmaku';
import * as Shadow from './fx/shadow';
import { MotaCanvas2D } from './fx/canvas2d';
import * as portal from './fx/portal';
import { MotaRenderer } from './render/render';
import { Container } from './render/container';
import { Sprite } from './render/sprite';
@@ -77,6 +75,7 @@ import * as Animation from 'mutate-animate';
import './render/index';
import * as RenderUtils from './render/utils';
import '@/module';
import { MotaOffscreenCanvas2D } from './fx/canvas2d';
// ----- 类注册
Mota.register('class', 'AudioPlayer', AudioPlayer);
@@ -95,7 +94,6 @@ Mota.register('class', 'UiController', UiController);
Mota.register('class', 'MComponent', MComponent);
Mota.register('class', 'ResourceController', ResourceController);
Mota.register('class', 'Danmaku', Danmaku);
Mota.register('class', 'MotaCanvas2D', MotaCanvas2D);
// ----- 函数注册
Mota.register('fn', 'm', m);
Mota.register('fn', 'unwrapBinary', unwarpBinary);
@@ -151,9 +149,7 @@ Mota.register('module', 'UIComponents', {
});
Mota.register('module', 'MCGenerator', MCGenerator);
Mota.register('module', 'Shadow', Shadow);
Mota.register('module', 'Effect', {
Portal: portal
});
Mota.register('module', 'Effect', {});
Mota.register('module', 'Render', {
texture,
MotaRenderer,
@@ -166,6 +162,7 @@ Mota.register('module', 'Render', {
Layer,
LayerGroupFloorBinder,
Camera,
MotaOffscreenCanvas2D,
Utils: RenderUtils
});
Mota.register('module', 'Action', {
-25
View File
@@ -428,31 +428,6 @@ document.addEventListener('keyup', e => {
}
} else {
// polyfill样板
if (
main.dom.startPanel.style.display == 'block' &&
(main.dom.startButtons.style.display == 'block' ||
main.dom.levelChooseButtons.style.display == 'block')
) {
if (e.keyCode == 38 || e.keyCode == 33)
// up/pgup
main.selectButton((main.selectedButton || 0) - 1);
else if (e.keyCode == 40 || e.keyCode == 34)
// down/pgdn
main.selectButton((main.selectedButton || 0) + 1);
else if (e.keyCode == 67 || e.keyCode == 13 || e.keyCode == 32)
// C/Enter/Space
main.selectButton(main.selectedButton);
else if (
e.keyCode == 27 &&
main.dom.levelChooseButtons.style.display == 'block'
) {
// ESC
core.showStartAnimate(true);
e.preventDefault();
}
e.stopPropagation();
return;
}
if (main.dom.inputDiv.style.display == 'block') {
if (e.keyCode == 13) {
setTimeout(function () {
+23 -34
View File
@@ -389,11 +389,13 @@ Mota.require('var', 'hook').on('reset', () => {
Mota.require('var', 'hook').once('reset', () => {
const mainStorage = GameStorage.for(GameStorage.fromGame('main'));
mainStorage.read();
if (!mainStorage.getValue('played', false)) {
if (mainStorage.getValue('played', false)) {
mainStorage.setValue('played', true);
const defaultsTool =
CustomToolbar.list.find(v => v.id === '@defaults') ??
new CustomToolbar('@defaults', true);
let defaultsTool = CustomToolbar.list.find(v => v.id === '@defaults');
const hasDefaults = !!defaultsTool;
if (!defaultsTool) {
defaultsTool = new CustomToolbar('@defaults', true);
}
defaultsTool.closeAll();
defaultsTool.items = reactive([]);
defaultsTool.add({
@@ -417,38 +419,25 @@ Mota.require('var', 'hook').once('reset', () => {
'minimap'
]
});
// 计算位置
if (isMobile) {
// 手机端显示在最下方
defaultsTool.setPos(25, window.innerHeight - 100);
defaultsTool.setSize(window.innerWidth - 50, 100);
} else {
// 电脑显示在屏幕右方
const x = window.innerWidth / 2 + core.domStyle.scale * 240 + 75;
defaultsTool.setPos(x, window.innerHeight / 2 + 100);
defaultsTool.setSize(window.innerWidth - x - 75, 200);
}
// 计算位置,显示在游戏画面下方
// if (!hasDefaults) {
// const gameGroup = core.dom.gameGroup;
// const bottom = gameGroup.offsetTop + gameGroup.offsetHeight - 3;
// const left = gameGroup.offsetLeft + 3;
// const width = gameGroup.offsetWidth - 6;
// if (isMobile) {
// // 手机端显示在最下方
// defaultsTool.setPos(16, window.innerHeight - 32);
// defaultsTool.setSize(bottom, 35);
// } else {
// // 电脑显示在屏幕右方
// defaultsTool.setPos(left, bottom);
// defaultsTool.setSize(width, 70);
// }
// }
defaultsTool.show();
CustomToolbar.save();
}
});
window.addEventListener('resize', () => {
requestAnimationFrame(() => {
const defaultsTool = CustomToolbar.list.find(v => v.id === '@defaults');
if (!defaultsTool) return;
// 计算位置
if (isMobile) {
// 手机端显示在最下方
defaultsTool.setPos(25, window.innerHeight - 100);
defaultsTool.setSize(window.innerWidth - 50, 100);
} else {
// 电脑显示在屏幕右方
const x = window.innerWidth / 2 + core.domStyle.scale * 240 + 75;
defaultsTool.setPos(x, window.innerHeight / 2 + 100);
defaultsTool.setSize(window.innerWidth - x - 75, 200);
}
defaultsTool.refresh(true);
});
});
+1 -13
View File
@@ -355,16 +355,6 @@ function handleScreenSetting<T extends number | boolean>(
} else if (key === 'heroDetail') {
// 勇士显伤
core.drawHero();
} else if (key === 'antiAlias') {
// 抗锯齿
for (const canvas of core.dom.gameCanvas) {
if (core.domStyle.hdCanvas.includes(canvas.id)) continue;
if (n) {
canvas.classList.remove('no-anti-aliasing');
} else {
canvas.classList.add('no-anti-aliasing');
}
}
} else if (key === 'fontSize') {
// 字体大小
root.style.fontSize = `${n}px`;
@@ -434,9 +424,8 @@ mainSetting
.register('itemDetail', '宝石血瓶显伤', true, COM.Boolean)
.register('heroDetail', '勇士显伤', false, COM.Boolean)
.register('transition', '界面动画', false, COM.Boolean)
.register('antiAlias', '抗锯齿', false, COM.Boolean)
.register('fontSize', '字体大小', 16, COM.Number, [2, 48, 1])
.register('fontSizeStatus', '状态栏字体', 16, COM.Number, [2, 48, 1])
.register('fontSizeStatus', '状态栏字体', 16, COM.Number, [10, 300, 10])
.register('smoothView', '平滑镜头', true, COM.Boolean)
.register('criticalGem', '临界显示方式', false, COM.Boolean)
.setDisplayFunc('criticalGem', value => (value ? '宝石数' : '攻击'))
@@ -501,7 +490,6 @@ loading.once('coreInit', () => {
'screen.itemDetail': !!storage.getValue('screen.itemDetail', true),
'screen.heroDetail': !!storage.getValue('screen.heroDetail', false),
'screen.transition': !!storage.getValue('screen.transition', false),
'screen.antiAlias': !!storage.getValue('screen.antiAlias', false),
'screen.fontSize': storage.getValue('screen.fontSize', isMobile ? 9 : 16),
'screen.smoothView': !!storage.getValue('screen.smoothView', true),
'screen.criticalGem': !!storage.getValue('screen.criticalGem', false),
+43 -2
View File
@@ -12,27 +12,33 @@ import { LayerGroupHalo } from '@/plugin/fx/halo';
import { FloorViewport } from './preset/viewport';
import { Container } from './container';
import { PopText } from '@/plugin/fx/pop';
import { FloorChange } from '@/plugin/fallback';
let main: MotaRenderer;
Mota.require('var', 'loading').once('coreInit', () => {
const render = new MotaRenderer();
main = render;
render.mount();
render.hide();
const mapDraw = new Container();
const layer = new LayerGroup();
const pop = new PopText('static');
const floorChange = new FloorChange('static');
mapDraw.id = 'map-draw';
layer.id = 'layer-main';
pop.id = 'pop-main';
floorChange.id = 'floor-change';
mapDraw.setHD(true);
mapDraw.setAntiAliasing(false);
mapDraw.size(core._PX_, core._PY_);
floorChange.size(480, 480);
floorChange.setHD(true);
floorChange.setZIndex(10);
floorChange.setTips(tips);
pop.setZIndex(80);
['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
layer.addLayer(v as FloorLayer);
});
@@ -61,6 +67,7 @@ Mota.require('var', 'loading').once('coreInit', () => {
render.appendChild(mapDraw);
mapDraw.appendChild(layer);
layer.appendChild(pop);
mapDraw.appendChild(floorChange);
console.log(render);
});
@@ -71,3 +78,37 @@ Mota.require('var', 'hook').on('reset', () => {
Mota.require('var', 'hook').on('restart', () => {
main.hide();
});
const tips = [
'按下C可以查看鼠标位置怪物临界',
'按下E可以查看鼠标位置怪物属性',
'将鼠标移动到光环怪上以查看其产生的光环',
'字体太大?试试在背包的系统设置里面调整字体大小吧!',
'字体太小?试试在背包的系统设置里面调整字体大小吧!',
'按键不合心意?试试在背包的系统设置里面自定义快捷键',
'拖动状态栏左上角可以移动状态栏哦!',
'拖动状态栏右下角可以缩放状态栏哦!',
'按下M键,鼠标位置的怪物的信息就会被你看光啦!',
'咱就是说,要不要试一下工具栏的最后一个按钮?',
'要不要试试工具栏倒数第二个按钮呢?',
'想自定义工具栏?去背包的系统设置看看吧!',
'冷知识:临界界面可以拖动滚动条来查看减伤情况',
'可以用滚轮或者双指缩放小地图!',
'楼传的最左侧一栏可以选择区域!',
'冷知识:装备栏左栏最上面可以修改装备排序',
'冷冷冷知识:装备栏左栏最上面右侧可以更改顺序或倒序',
'第一章使用跳跃技能可是要扣血的!要注意!',
'按H查看本游戏的百科全书',
'给别人炫耀一下自己的成就点吧!虽然不能记榜(',
'抱团属性会在怪物右上角显示加成数量!',
'乾坤挪移属性绘制怪物左上角显示“乾”字!',
'电脑端可以试试按F11全屏游玩!',
'手机端要不试试横屏玩?',
'不在楼梯边也可以使用楼传!',
'技能树的右下角可以切换章节!',
'开启自动切换技能就会自动帮你选择最优技能了!',
'魔塔不仅有撤回,还有恢复,按W或6就可以了!',
'觉得卡顿?可以去试着设置里面关闭一些特性!',
'从第二章开始,怪物负伤量不会超过其生命的1/4',
'生命回复不会超过防御的十分之一'
];
+2
View File
@@ -123,6 +123,8 @@ export interface ERenderItemEvent {
beforeRender: [transform: Transform];
afterRender: [transform: Transform];
destroy: [];
/** 当这个元素被点击时触发 */
clickCapture: [x: number, y: number, type: number, ev: MouseEvent];
}
interface TickerDelegation {
+9 -11
View File
@@ -1,4 +1,5 @@
import { MotaCanvas2D } from '../fx/canvas2d';
import { logger } from '../common/logger';
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
import { Container } from './container';
import { RenderItem } from './item';
import { Transform } from './transform';
@@ -6,7 +7,7 @@ import { Transform } from './transform';
export class MotaRenderer extends Container {
static list: Map<string, MotaRenderer> = new Map();
target: MotaCanvas2D;
target!: MotaOffscreenCanvas2D;
protected needUpdate: boolean = false;
readonly isRoot: boolean = true;
@@ -14,11 +15,15 @@ export class MotaRenderer extends Container {
constructor(id: string = 'render-main') {
super('static', false);
this.target = new MotaCanvas2D(id);
const canvas = document.getElementById(id) as HTMLCanvasElement;
if (!canvas) {
logger.error(19);
return;
}
this.target = new MotaOffscreenCanvas2D(true, canvas);
this.size(core._PX_, core._PY_);
this.target.withGameScale(true);
this.target.size(core._PX_, core._PY_);
this.target.css(`z-index: 100`);
this.target.setAntiAliasing(false);
this.setAnchor(0.5, 0.5);
@@ -72,13 +77,6 @@ export class MotaRenderer extends Container {
return null;
}
/**
* 添加至游戏画面
*/
mount() {
this.target.mount();
}
destroy() {
MotaRenderer.list.delete(this.id);
}
+1
View File
@@ -18,6 +18,7 @@
"16": "Cannot find log message for $1 code $2.",
"17": "Cannot use shader program for shader element that does not belong to it.",
"18": "Cannot delete shader program for shader element that does not belong to it.",
"19": "Cannot create MotaRenderer instance for nonexistent canvas.",
"1101": "Shadow extension needs 'floor-hero' extension as dependency.",
"1201": "Floor-damage extension needs 'floor-binder' extension as dependency.",
"1301": "Portal extension need 'floor-binder' extension as dependency.",
-3
View File
@@ -10,9 +10,6 @@
"是否展示当一个ui界面,如怪物手册等的打开与关闭时的动画。当此项开启时,",
"所有界面被打开或关闭时都会展示动画,否则会直接展示出来"
],
"antiAlias": [
"是否开启抗锯齿。开启后,画面会变得不那么锐利,观感更加舒适;关闭后,可以更好地展现出像素感,同时部分像素错误也不会出现。"
],
"fontSize": [
"在各种 ui 界面中显示的文字大小,范围为 2 - 48。注意,字体过大可能会引起 ui 布局发生错误"
],
+3 -6
View File
@@ -24,8 +24,6 @@ import type * as damage from './enemy/damage';
import type { Logger } from '@/core/common/logger';
import type { Danmaku } from '@/core/main/custom/danmaku';
import type * as misc from './mechanism/misc';
import type { MotaCanvas2D } from '@/core/fx/canvas2d';
import type * as portal from '@/core/fx/portal';
import type { texture } from '@/core/render/cache';
import type { MotaRenderer } from '@/core/render/render';
import type { Container } from '@/core/render/container';
@@ -42,6 +40,7 @@ import type { Camera } from '@/core/render/camera';
import type * as Animation from 'mutate-animate';
import type * as RenderUtils from '@/core/render/utils';
import type { WeatherController } from '@/module/weather/weather';
import type { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
interface ClassInterface {
// 渲染进程与游戏进程通用
@@ -62,7 +61,6 @@ interface ClassInterface {
SoundEffect: typeof SoundEffect;
SoundController: typeof SoundController;
BgmController: typeof BgmController;
MotaCanvas2D: typeof MotaCanvas2D;
Danmaku: typeof Danmaku;
// todo: 放到插件 ShaderEffect: typeof ShaderEffect;
// 定义于游戏进程,渲染进程依然可用
@@ -110,9 +108,7 @@ interface ModuleInterface {
MiscData: typeof misc.MiscData;
HeroSkill: typeof misc.HeroSkill;
};
Effect: {
Portal: typeof portal;
};
Effect: {};
Render: {
texture: typeof texture;
MotaRenderer: typeof MotaRenderer;
@@ -125,6 +121,7 @@ interface ModuleInterface {
Layer: typeof Layer;
LayerGroupFloorBinder: typeof LayerGroupFloorBinder;
Camera: typeof Camera;
MotaOffscreenCanvas2D: typeof MotaOffscreenCanvas2D;
Utils: typeof RenderUtils;
};
State: {
View File
Whitespace-only changes.
+18
View File
@@ -0,0 +1,18 @@
import { Shader, ShaderProgram } from '@/core/render/shader';
import { IWeather, WeatherController } from './weather';
export class SunWeather implements IWeather {
static id: string = 'sun';
activate(): void {}
frame(): void {}
deactivate(): void {}
}
WeatherController.register(SunWeather);
class SunShader extends Shader {
protected override postDraw(gl: WebGL2RenderingContext): void {}
}
+108
View File
@@ -0,0 +1,108 @@
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { RenderItem, RenderItemPosition } from '@/core/render/item';
import { Transform } from '@/core/render/transform';
// 渲染端的向后兼容用,会充当两个版本间过渡的作用
export class FloorChange extends RenderItem {
private tips: string[] = [];
/** 当前小贴士 */
private usingTip: string = '';
/** 透明度 */
private alpha: 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.alpha = 1;
this.removeTicker(id);
} else {
this.alpha = 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.alpha = 0;
} else {
this.alpha = 1 - progress;
}
this.update();
},
10000,
res
);
});
}
protected render(
canvas: MotaOffscreenCanvas2D,
transform: Transform
): void {
if (this.alpha === 0) return;
const ctx = canvas.ctx;
ctx.globalAlpha = this.alpha;
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
);
}
}
}
+12 -11
View File
@@ -17,22 +17,23 @@ function createCanvas(name, zIndex) {
}
export function init() {
var bg2Canvas = createCanvas('bg2', 20);
var fg2Canvas = createCanvas('fg2', 63);
// 大地图适配
core.bigmap.canvas = [
'bg2',
'fg2',
'bg',
'event',
'event2',
'fg',
'damage'
];
core.initStatus.bg2maps = {};
core.initStatus.fg2maps = {};
if (main.mode == 'editor') {
var bg2Canvas = createCanvas('bg2', 20);
var fg2Canvas = createCanvas('fg2', 63);
core.bigmap.canvas = [
'bg2',
'fg2',
'bg',
'event',
'event2',
'fg',
'damage'
];
/*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/
// 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层)
// 背景层2(bg2) 插入事件层(event)之前(即bg与event之间)
+8 -8
View File
@@ -35,30 +35,30 @@ export function init() {
// todo: 多个状态栏分离与控制
control.prototype.showStatusBar = function () {
if (main.mode == 'editor') return;
const CustomToolbar = Mota.require('class', 'CustomToolbar');
const defaultsTool = CustomToolbar.get('@defaults');
core.removeFlag('hideStatusBar');
if (!fixedUi.hasName('statusBar')) {
fixedUi.open('statusBar');
}
defaultsTool?.show();
};
control.prototype.hideStatusBar = function (showToolbox) {
if (main.mode == 'editor') return;
const CustomToolbar = Mota.require('class', 'CustomToolbar');
const defaultsTool = CustomToolbar.get('@defaults');
// 如果原本就是隐藏的,则先显示
if (!core.domStyle.showStatusBar) this.showStatusBar();
if (core.isReplaying()) showToolbox = true;
fixedUi.closeByName('statusBar');
if (!showToolbox) {
defaultsTool?.closeAll();
}
var toolItems = core.dom.tools;
core.setFlag('hideStatusBar', true);
core.setFlag('showToolbox', showToolbox || null);
if (
(!core.domStyle.isVertical && !core.flags.extendToolbar) ||
!showToolbox
) {
for (var i = 0; i < toolItems.length; ++i)
toolItems[i].style.display = 'none';
}
};
}
-2
View File
@@ -2,7 +2,6 @@ import * as fly from './ui/fly';
import * as chase from './chase';
import * as completion from './completion';
import * as pop from './pop';
import * as frag from './fx/frag';
import * as use from './use';
import * as gameCanvas from './fx/gameCanvas';
import * as animateController from './animateController';
@@ -14,7 +13,6 @@ Mota.Plugin.register('fly_r', fly);
Mota.Plugin.register('chase_r', chase);
Mota.Plugin.register('completion_r', completion, completion.init);
Mota.Plugin.register('pop_r', pop, pop.init);
Mota.Plugin.register('frag_r', frag, frag.init);
Mota.Plugin.register('use_r', use);
Mota.Plugin.register('gameCanvas_r', gameCanvas);
Mota.Plugin.register(
+7
View File
@@ -939,21 +939,25 @@ interface Core extends Pick<Main, CoreDataFromMain> {
readonly material: Material;
/**
* @deprecated
* 计时器(样板的神秘操作
*/
readonly timeout: Timeout;
/**
* @deprecated
* 定时器
*/
readonly interval: Interval;
/**
* @deprecated
* 全局动画信息
*/
readonly animateFrame: AnimateFrame;
/**
* @deprecated
* 音乐状态
*/
readonly musicStatus: Readonly<MusicStatus>;
@@ -964,11 +968,13 @@ interface Core extends Pick<Main, CoreDataFromMain> {
readonly platform: Readonly<CorePlatform>;
/**
* @deprecated
* dom样式
*/
readonly domStyle: Readonly<DomStyle>;
/**
* @deprecated
* 大地图信息
*/
readonly bigmap: CoreBigmap;
@@ -989,6 +995,7 @@ interface Core extends Pick<Main, CoreDataFromMain> {
readonly initStatus: DeepReadonly<InitGameStatus>;
/**
* @deprecated
* 所有的自定义画布
*/
readonly dymCanvas: Record<string, CanvasRenderingContext2D>;
-1
View File
@@ -155,7 +155,6 @@ async function clickStartButton(id: string) {
core.startGame(id === 'easy' ? 'easy' : 'hard');
}
if (id === 'load-game') {
core.dom.gameGroup.style.display = 'block';
start.style.top = '200vh';
core.load();
}