refactor: 样板类型声明移入新文件夹,src/core 全部移入 monorepo

This commit is contained in:
unanmed 2025-03-05 22:15:19 +08:00
parent 960dbd429a
commit f7c6434aba
44 changed files with 320 additions and 1652 deletions

View File

@ -1,6 +1,7 @@
{
"name": "@motajs/legacy-client",
"dependencies": {
"@motajs/common": "workspace:*"
"@motajs/legacy-system": "workspace:*",
"@motajs/legacy-ui": "workspace:*"
}
}

View File

@ -0,0 +1,2 @@
export * as System from '@motajs/legacy-system';
export * as UI from '@motajs/legacy-ui';

View File

@ -1,13 +1,14 @@
import { mat4 } from 'gl-matrix';
import { logger } from '@motajs/common';
import { WebGLColorArray, createProgram, isWebGL2Supported } from './webgl';
import {
WebGLColorArray,
createProgram,
isWebGL2Supported
} from './webgl';
import { ILayerRenderExtends, Layer, HeroRenderer, Sprite } from '@motajs/render';
ILayerRenderExtends,
Layer,
HeroRenderer,
Sprite
} from '@motajs/render';
/**
/**
*
* 10
*/
@ -29,7 +30,13 @@ interface ShadowConfig {
blur?: number;
}
function addLightFromBlock(floors: FloorIds[], block: number, config: LightConfig, sc?: ShadowConfig, hero?: LightConfig) {
function addLightFromBlock(
floors: FloorIds[],
block: number,
config: LightConfig,
sc?: ShadowConfig,
hero?: LightConfig
) {
floors.forEach(v => {
const shadow = new Shadow(v);
shadow.background = [0, 0, 0, 0.2];
@ -65,7 +72,7 @@ function addLightFromBlock(floors: FloorIds[], block: number, config: LightConfi
}
});
});
})
});
}
const hook = Mota.require('var', 'hook');
@ -73,7 +80,10 @@ const hook = Mota.require('var', 'hook');
hook.once('reset', () => {
Shadow.init();
addLightFromBlock(
core.floorIds.slice(61, 70).concat(core.floorIds.slice(72, 81)).concat(core.floorIds.slice(85, 107)),
core.floorIds
.slice(61, 70)
.concat(core.floorIds.slice(72, 81))
.concat(core.floorIds.slice(85, 107)),
103,
{ decay: 50, r: 300, color: [0.9333, 0.6, 0.333, 0.3] },
{ background: [0, 0, 0, 0.2] },
@ -82,7 +92,12 @@ hook.once('reset', () => {
addLightFromBlock(
['MT50', 'MT60', 'MT61', 'MT72', 'MT73', 'MT74', 'MT75'],
103,
{ decay: 20, r: 150, color: [0.9333, 0.6, 0.333, 0.3], noShelter: true },
{
decay: 20,
r: 150,
color: [0.9333, 0.6, 0.333, 0.3],
noShelter: true
},
{ background: [0, 0, 0, 0.3] }
);
Mota.rewrite(core.control, 'loadData', 'add', () => {
@ -95,17 +110,17 @@ hook.once('reset', () => {
hook.on('reset', () => {
Shadow.update(true);
LayerShadowExtends.shadowList.forEach(v => v.update());
})
});
hook.on('setBlock', () => {
Shadow.update(true);
LayerShadowExtends.shadowList.forEach(v => v.update());
})
hook.on('changingFloor', floorId => {
});
hook.on('changingFloor', floorId => {
Shadow.clearBuffer();
Shadow.update(true);
// setCanvasFilterByFloorId(floorId);
LayerShadowExtends.shadowList.forEach(v => v.update());
})
});
// 深度测试着色器
@ -259,7 +274,6 @@ void main() {
}
`;
interface ShadowProgram {
depth: WebGLProgram;
color: WebGLProgram;
@ -384,7 +398,8 @@ export class Shadow {
* @param nocache 使
*/
calShadowInfo(nocache: boolean = false) {
if (!nocache && this.cache && Shadow.cached.has(this.floorId)) return this.cache;
if (!nocache && this.cache && Shadow.cached.has(this.floorId))
return this.cache;
Shadow.cached.add(this.floorId);
Shadow.clearBuffer();
@ -402,33 +417,81 @@ export class Shadow {
const t = (core._PY_ - (y + h)) * ratio;
res.push(
// 上边缘
l, t, 0,
l, t, m,
r, t, m,
r, t, 0,
r, t, m,
l, t, 0,
l,
t,
0,
l,
t,
m,
r,
t,
m,
r,
t,
0,
r,
t,
m,
l,
t,
0,
// 右
r, t, 0,
r, t, m,
r, b, m,
r, b, 0,
r, b, m,
r, t, 0,
r,
t,
0,
r,
t,
m,
r,
b,
m,
r,
b,
0,
r,
b,
m,
r,
t,
0,
// 下
r, b, 0,
r, b, m,
l, b, m,
l, b, 0,
l, b, m,
r, b, 0,
r,
b,
0,
r,
b,
m,
l,
b,
m,
l,
b,
0,
l,
b,
m,
r,
b,
0,
// 左
l, b, 0,
l, b, m,
l, t, m,
l, t, 0,
l, t, m,
l, b, 0
l,
b,
0,
l,
b,
m,
l,
t,
m,
l,
t,
0,
l,
t,
m,
l,
b,
0
);
});
});
@ -462,7 +525,7 @@ export class Shadow {
removeLight(id: string) {
const index = this.lights.findIndex(v => v.id === id);
this.lights.splice(index, 1);
delete this.originLightInfo[id]
delete this.originLightInfo[id];
this.followHero.delete(id);
this.requestRefresh();
}
@ -509,7 +572,7 @@ export class Shadow {
* @param nocache 使
* @param resize resize canvas
*/
private refresh(nocache: boolean = false, resize: boolean = false) {
private refresh(nocache: boolean = false, resize: boolean = false) {
this.calShadowInfo(nocache);
if (resize) {
Shadow.resizeCanvas();
@ -527,7 +590,7 @@ export class Shadow {
// depth test
gl.useProgram(Shadow.program.depth);
const lightProjection = Shadow.martix.projection
const lightProjection = Shadow.martix.projection;
// 使用 3D 纹理存储深度信息
const texture = Shadow.texture.depth;
@ -545,11 +608,23 @@ export class Shadow {
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LESS);
this.lights.forEach((light, i) => {
this.depthTest(lightProjection, light, i, texture, length, proj, view);
this.depthTest(
lightProjection,
light,
i,
texture,
length,
proj,
view
);
});
gl.disableVertexAttribArray(position);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(info.length), gl.STATIC_DRAW);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array(info.length),
gl.STATIC_DRAW
);
gl.vertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0);
gl.bindTexture(gl.TEXTURE_2D, null);
@ -593,13 +668,14 @@ export class Shadow {
const v = this.lights[i];
data.push(
// 坐标
v.x * ratio, v.y * ratio, 0, 0 // 填充到 4 个分量以确保对齐
v.x * ratio,
v.y * ratio,
0,
0 // 填充到 4 个分量以确保对齐
);
} else {
// 如果没有光源,添加填充以确保统一缓冲区大小保持一致
data.push(
0, 0, 0, 0
);
data.push(0, 0, 0, 0);
}
}
for (let i = 0; i < MAX_LIGHT_NUM; i++) {
@ -607,13 +683,14 @@ export class Shadow {
const v = this.lights[i];
data.push(
// 颜色
v.color[0], v.color[1], v.color[2], v.color[3] // 4 个分量的颜色
v.color[0],
v.color[1],
v.color[2],
v.color[3] // 4 个分量的颜色
);
} else {
// 如果没有光源,添加填充以确保统一缓冲区大小保持一致
data.push(
0, 0, 0, 0
);
data.push(0, 0, 0, 0);
}
}
for (let i = 0; i < MAX_LIGHT_NUM; i++) {
@ -621,13 +698,14 @@ export class Shadow {
const v = this.lights[i];
data.push(
// 半径、衰减半径、遮挡
v.r * ratio, v.decay * ratio, v.noShelter ? 1 : 0, 0 // 填充到 4 个分量
v.r * ratio,
v.decay * ratio,
v.noShelter ? 1 : 0,
0 // 填充到 4 个分量
);
} else {
// 如果没有光源,添加填充以确保统一缓冲区大小保持一致
data.push(
0, 0, 0, 0
);
data.push(0, 0, 0, 0);
}
}
@ -635,7 +713,11 @@ export class Shadow {
const lightsBuffer = Shadow.buffer.color.lights;
gl.bindBuffer(gl.UNIFORM_BUFFER, lightsBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array(data), gl.DYNAMIC_DRAW);
gl.bufferData(
gl.UNIFORM_BUFFER,
new Float32Array(data),
gl.DYNAMIC_DRAW
);
gl.uniformBlockBinding(Shadow.program.color, blockIndex, 0);
gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, lightsBuffer);
@ -644,7 +726,13 @@ export class Shadow {
const colorFramebuffer = Shadow.buffer.color.framebuffer;
gl.enable(gl.DEPTH_TEST);
gl.bindFramebuffer(gl.FRAMEBUFFER, colorFramebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTexture, 0);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
colorTexture,
0
);
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
gl.viewport(0, 0, canvas.width, canvas.height);
@ -690,7 +778,13 @@ export class Shadow {
const blurFramebuffer = Shadow.buffer.blur1.framebuffer;
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
gl.bindFramebuffer(gl.FRAMEBUFFER, blurFramebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, blurTexture, 0);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
blurTexture,
0
);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
@ -714,7 +808,7 @@ export class Shadow {
gl.vertexAttribPointer(texBlur2, 2, gl.FLOAT, false, 0, 0);
const blur2IndicesBuffer = Shadow.buffer.blur2.indices;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, blur2IndicesBuffer);
// Texture
const blur2TextureLoc = Shadow.locations.blur2.u_texture;
const blur2TextureSizeLoc = Shadow.locations.blur2.u_textureSize;
@ -747,13 +841,24 @@ export class Shadow {
const gl = Shadow.gl;
const ratio = core.domStyle.scale * devicePixelRatio;
const cameraMatrix = mat4.create();
mat4.lookAt(cameraMatrix, [light.x * ratio, light.y * ratio, core._PX_ * 2 * ratio], [light.x * ratio, light.y * ratio, 0], [0, 1, 0]);
mat4.lookAt(
cameraMatrix,
[light.x * ratio, light.y * ratio, core._PX_ * 2 * ratio],
[light.x * ratio, light.y * ratio, 0],
[0, 1, 0]
);
const size = core._PX_ * ratio * 2;
gl.viewport(0, 0, size, size);
const framebuffer = Shadow.buffer.depth.framebuffer[index];
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture, 0, index);
gl.framebufferTextureLayer(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
texture,
0,
index
);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
gl.uniformMatrix4fv(proj, false, lightProjection);
@ -782,12 +887,20 @@ export class Shadow {
gl.UNSIGNED_BYTE,
null
);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(
gl.TEXTURE_2D_ARRAY,
gl.TEXTURE_MIN_FILTER,
gl.NEAREST
);
gl.texParameteri(
gl.TEXTURE_2D_ARRAY,
gl.TEXTURE_MAG_FILTER,
gl.NEAREST
);
return texture!;
}
private static create2DTexture(size: number) {
private static create2DTexture(size: number) {
const gl = Shadow.gl;
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
@ -909,48 +1022,62 @@ export class Shadow {
u_texture: gl.getUniformLocation(blur2, 'u_texture')!,
u_textureSize: gl.getUniformLocation(blur2, 'u_textureSize')!
}
}
};
// Matrix
const lightProjection = mat4.create();
mat4.perspective(lightProjection, FOVY, 1, 1, core._PX_ * ratio);
this.martix = {
projection: lightProjection
}
};
// Buffer
const depthFramebuffers = Array(MAX_LIGHT_NUM).fill(1).map(v => gl.createFramebuffer()!);
const depthFramebuffers = Array(MAX_LIGHT_NUM)
.fill(1)
.map(v => gl.createFramebuffer()!);
this.buffer = {
depth: {
position: gl.createBuffer()!,
framebuffer: depthFramebuffers
},
color: {
position: this.initBuffer(new Float32Array([1, 1, -1, 1, 1, -1, -1, -1])),
texcoord: this.initBuffer(new Float32Array([1, 1, 0, 1, 1, 0, 0, 0])),
position: this.initBuffer(
new Float32Array([1, 1, -1, 1, 1, -1, -1, -1])
),
texcoord: this.initBuffer(
new Float32Array([1, 1, 0, 1, 1, 0, 0, 0])
),
indices: this.initIndicesBuffer(),
framebuffer: gl.createFramebuffer()!,
lights: gl.createBuffer()!
},
blur1: {
position: this.initBuffer(new Float32Array([1, 1, -1, 1, 1, -1, -1, -1])),
texcoord: this.initBuffer(new Float32Array([1, 1, 0, 1, 1, 0, 0, 0])),
position: this.initBuffer(
new Float32Array([1, 1, -1, 1, 1, -1, -1, -1])
),
texcoord: this.initBuffer(
new Float32Array([1, 1, 0, 1, 1, 0, 0, 0])
),
indices: this.initIndicesBuffer(),
framebuffer: gl.createFramebuffer()!
},
blur2: {
position: this.initBuffer(new Float32Array([1, 1, -1, 1, 1, -1, -1, -1])),
texcoord: this.initBuffer(new Float32Array([1, 1, 0, 1, 1, 0, 0, 0])),
position: this.initBuffer(
new Float32Array([1, 1, -1, 1, 1, -1, -1, -1])
),
texcoord: this.initBuffer(
new Float32Array([1, 1, 0, 1, 1, 0, 0, 0])
),
indices: this.initIndicesBuffer()
}
}
};
// Texture
this.texture = {
depth: this.create3DTexture(core._PX_, MAX_LIGHT_NUM),
color: this.create2DTexture(core._PX_),
blur: this.create2DTexture(core._PX_)
}
};
this.resizeCanvas();
}
@ -961,7 +1088,7 @@ export class Shadow {
mat4.perspective(lightProjection, FOVY, 1, 1, core._PX_ * ratio);
this.martix = {
projection: lightProjection
}
};
}
}
@ -1254,8 +1381,8 @@ export class LayerShadowExtends implements ILayerRenderExtends {
static shadowList: Set<LayerShadowExtends> = new Set();
id: string = 'shadow';
layer!: Layer
hero!: HeroRenderer
layer!: Layer;
hero!: HeroRenderer;
sprite!: Sprite;
update() {
@ -1265,19 +1392,19 @@ export class LayerShadowExtends implements ILayerRenderExtends {
private onMoveTick = (x: number, y: number) => {
const now = Shadow.now();
if (!now) return;
if (now.followHero.size === 0) return;
if (now.followHero.size === 0) return;
now.followHero.forEach(v => {
now.modifyLight(v, {
x: x * 32 + 16,
y: y * 32 + 16
});
});
now.requestRefresh();
now.requestRefresh();
this.layer.requestAfterFrame(() => {
this.sprite.update(this.sprite);
});
}
};
private listen() {
this.hero.on('moveTick', this.onMoveTick);
@ -1297,9 +1424,15 @@ export class LayerShadowExtends implements ILayerRenderExtends {
this.sprite = new Sprite('static', false);
this.sprite.setHD(true);
this.sprite.size(layer.width, layer.height);
this.sprite.setRenderFn((canvas, transform) => {
this.sprite.setRenderFn((canvas, transform) => {
if (Shadow.map[core.status.floorId]) {
canvas.ctx.drawImage(Shadow.canvas, 0, 0, layer.width, layer.height);
canvas.ctx.drawImage(
Shadow.canvas,
0,
0,
layer.width,
layer.height
);
}
});

View File

@ -1,57 +0,0 @@
// todo: 这个引入不加会报错,应该是循环引用导致的
import '@/plugin/utils';
import * as LegacyUI from '@motajs/legacy-ui';
import * as LegacySystem from '@motajs/legacy-system';
import './main/init/';
import './main/custom/toolbar';
import { KeyCode } from '@motajs/client-base';
import '@/plugin';
import * as System from '@motajs/system';
import { UI } from '@motajs/legacy-ui';
import Box from '@/components/box.vue';
import BoxAnimate from '@/components/boxAnimate.vue';
import Colomn from '@/components/colomn.vue';
import EnemyOne from '@/components/enemyOne.vue';
import Scroll from '@/components/scroll.vue';
import EnemyCritical from '@/panel/enemyCritical.vue';
import EnemySpecial from '@/panel/enemySpecial.vue';
import EnemyTarget from '@/panel/enemyTarget.vue';
import KeyboardPanel from '@/panel/keyboard.vue';
import { logger } from '@motajs/common';
import * as Shadow from './fx/shadow';
import { Render } from '@motajs/client';
import { HeroKeyMover } from '../module/action/move';
import * as Animation from 'mutate-animate';
import '@/module';
// ----- 变量注册
Mota.register('var', 'KeyCode', KeyCode);
Mota.register('var', 'status', status);
Mota.register('var', 'logger', logger);
// ----- 模块注册
Mota.register('module', 'LegacyUI', LegacyUI);
Mota.register('module', 'System', System);
Mota.register('module', 'LegacySystem', LegacySystem);
Mota.register('module', 'RenderUtils', utils);
Mota.register('module', 'UI', UI);
Mota.register('module', 'UIComponents', {
Box,
BoxAnimate,
Colomn,
EnemyOne,
Scroll,
EnemyCritical,
EnemySpecial,
EnemyTarget,
Keyboard: KeyboardPanel
});
Mota.register('module', 'Shadow', Shadow);
Mota.register('module', 'Effect', {});
Mota.register('module', 'Render', Render);
Mota.register('module', 'Action', {
HeroKeyMover
});
Mota.register('module', 'Animation', Animation);
main.renderLoaded = true;
Mota.require('var', 'hook').emit('renderLoaded');

View File

@ -1,88 +0,0 @@
{
"normal": [
{
"name": "虚惊一场",
"text": [
"打完山洞门口的兽人后只剩一滴血"
],
"point": 30
},
{
"name": "真能刷",
"text": [
"勇气之路的刷血怪刷到 <span style=\"color: gold\">15w</span> 以上的血"
],
"point": 30
}
],
"challenge": [
{
"name": "逃出生天",
"text": [
"通过山路追逐战的困难难度"
],
"point": 20
},
{
"name": "冰与火之舞",
"text": [
"完成第二章音游特殊战的困难难度"
],
"point": 50
}
],
"explore": [
{
"name": "勇气巅峰",
"text": [
"第一章完成度达到100%"
],
"progress": "${Mota.Plugin.require('completion_r').getChapterCompletion(1)} / 100",
"percent": true,
"point": 50
},
{
"name": "你是怎么办到的?!",
"text": [
"与山路上的若干个神秘木牌对话"
],
"progress": "${core.getLocalStorage('mountSign', 0)} / 5",
"hide": "该探索成就需要你自己探索如何达成",
"point": 25
},
{
"name": "智慧之心",
"text": [
"第二章完成度达到100%"
],
"progress": "${Mota.Plugin.require('completion_r').getChapterCompletion(2)} / 100",
"percent": true,
"point": 50
},
{
"name": "源头?",
"text": [
"在冰封雪原第一个山洞的水源处使用跳跃技能,并向前一步触发剧情"
],
"hide": "该探索成就需要你自己探索如何达成",
"point": 30
},
{
"name": "学坏了",
"text": [
"学习电摇嘲讽技能"
],
"hide": "该探索成就需要你自己探索如何达成",
"point": 20
},
{
"name": "满腹经纶",
"text": [
"把第二章中所有能学习的技能都学一遍"
],
"hide": "该探索成就需要你自己探索如何达成",
"progress": "",
"point": 50
}
]
}

View File

@ -1,54 +0,0 @@
{
"title.opus": {
"area": "未完工",
"name": "川井宪次 - 破裂足音",
"from": "《永远的七日之都》",
"img": "/project/images/bg.webp",
"desc": []
},
"cave.opus": {
"area": "山洞",
"name": "Faodail - Wren",
"img": "",
"desc": []
},
"grass.opus": {
"area": "草地",
"name": "大树 & 朱晨阳 - 大树与鹿",
"img": "",
"desc": []
},
"mount.opus": {
"area": "山路",
"name": "Epistra - Dream Of A Dream",
"img": "",
"desc": []
},
"escape.opus": {
"area": "山路追逐",
"name": "Gareth Coker - Escaping a Foul Presence",
"from": "《奥日与精灵意志》",
"img": "",
"desc": []
},
"plot1.opus": {
"area": "勇气之路",
"name": "Mark Petrie & Danny McCarthy - Rags To Rings",
"img": "",
"desc": []
},
"tower.opus": {
"area": "智慧之塔",
"name": "Falcom Sound Team J.D.K. - A Light Illuminating The Depth",
"from": "《英雄传说:零之轨迹》",
"img": "",
"desc": []
},
"beforeBoss.opus": {
"area": "战前独白",
"name": "Evan LE NY - Some Calm",
"from": "《太空化学》",
"img": "",
"desc": []
}
}

View File

@ -1,563 +0,0 @@
{
"tip": {
"text": "注意事项",
"condition": "true",
"desc": [
"这里显示本塔中需要注意的事项。",
"<br>",
"<br>",
"1. <span style=\"color: yellow; font-weight: 700\">",
"本百科全书字数很多,可以选择性地阅读。</span>不过本条目最好可以全部阅读一遍。",
"<br>",
"<br>",
"2. 本百科全书的内容会<span style=\"color: gold\">随着游戏的推进而增加新内容</span>",
"同时每次增加新内容时都会有提示。",
"<br>",
"<br>",
"3. <span style=\"color: gold\">背包中的系统设置同样非常重要,有些问题可以在那里找到原因</span>。",
"例如当你获得技能时可能会发现开启不了技能,",
"就是因为你打开了<span style=\"color: gold\">自动切换技能</span>的功能,在系统设置里面有说。",
"<br>",
"<br>",
"4. <span style=\"color: yellow; font-weight: 700\">重要!!!</span>本塔没有考虑录像的二次播放性,",
"这意味着如果你从头播放一个录像,播放完成后继续游玩,提交成绩后不能保证绿录像,请谨慎考虑。",
"<br>",
"<br>",
"5. 本塔中<span style=\"color: gold\">几乎所有 ui 都可以纵向滚动</span>,如果发现显示不全,",
"可以尝试上下拖动,就像浏览网页一样。电脑端还可以使用滚轮上下滚动。",
"大部分可以纵向滚动的 ui 都会在右方有一个滚动条,也可以拖动它进行滚动,例如本百科全书的条目列表和",
"条目说明都是可以通过上述方式滚动的。",
"<br>",
"<br>",
"6. 本塔主要面向电脑端设计,",
"<span style=\"color: gold\">建议使用电脑游玩以获得更好的游戏体验同时使用约16:9的比例游玩更加合适",
"</span>。但是手机依然可以游玩本塔,",
"但部分操作可能不是很方便ui 也可能不是很美观,不过依然可以完整体验本游戏。",
"<br>",
"<br>",
"7. 对于手机端,可以点击<span style=\"color: gold\">右下角的难度文字</span>来切换工具栏至数字键。",
"这样,你可以更加方便地进行使用技能等操作。",
"<br>",
"<br>",
"8. 本塔中几乎所有 ui 在打开时都会有一个0.6s的动画,如果不想要,可以在开头捡的系统设置里面关闭(默认关闭)。",
"同时,几乎所有 ui 的退出按钮都在左上角。",
"<br>",
"<br>",
"9. 地图上显示的怪物临界有可能不准,当其与折线图有差异时,<span style=\"color: gold\">请以折线图为准</span>。"
]
},
"about": {
"text": "关于游戏",
"condition": "true",
"desc": [
"使用样板Vite 魔塔样板",
"<br>",
"样板版本V2.10.0",
"<br>",
"游戏版本V1.0.0-alpha",
"<br>",
"游戏作者:古祠",
"<br>",
"游戏开源地址:<a href=\"https://github.com/unanmed/HumanBreak\" target=\"_blank\">",
"https://github.com/unanmed/HumanBreak</a>",
"<br>",
"本塔遵循MIT开源协议。<a href=\"LICENSE\" target=\"_blank\">查看开源协议</a>",
"<br>",
"音乐来源:网易云音乐等",
"<br>",
"素材来源:大素材库、爱给网、网站素材库等",
"<br>",
"<span style=\"color: gold\">特别说明:素材与音乐均来自网络,不得用于商业用途,仅用于参考与学习</span>",
"<br>",
"特别鸣谢(排名不分先后):",
"<br>",
"1. 无名甲烷菌(提供部分特殊属性与机制想法)",
"<br>",
"测试(排名不分先后):",
"<br>",
"1. 永葆一颗童心",
"<br>",
"2. 影法师",
"<br>",
"3. 夜战天明889",
"<br>",
"4. 霸道的老鼠"
]
},
"tutorial": {
"text": "新手教程",
"condition": "true",
"desc": [
"本条目是魔塔游戏的新手教程,如果对魔塔有一定的了解,可以直接忽略。",
"<br>",
"<br>",
"魔塔是一种固定数值rpg游戏在打怪的时候遵循<span style=\"color: gold\">我打你一下,你打我一下</span>",
"的原则,造成的伤害是己方攻击减去对方防御,最后怪物的伤害便是你在战斗中失去的生命值。当然,为了游戏体验,",
"战斗过程会被省略。",
"<br>",
"<br>",
"宝石可以增加你的属性,在大部分魔塔中,红宝石增加攻击,蓝宝石增加防御,本塔也不例外。血瓶可以增加你的生命值。",
"一般情况下,拾取宝物的优先级是<span style=\"color: gold\">红宝石 &gt; 蓝宝石 &gt; 血瓶</span>",
"但部分情况可能不是这样,这需要你自己的游玩经验等。",
"<br>",
"<br>",
"本塔还拥有升级机制,升级时能够给你增加大量的属性,因此,一般情况下当你接近升级时,需要尽快打怪升级。",
"<br>",
"<br>",
"然后是门。在魔塔中,很多门都不是必开的门,它们的作用一般是可以躲开怪物拿宝石,或者门里面有血瓶等。",
"当你血量足够时,这些门可以不用开,不然可能会有必开的门无法开启导致卡关。对于钥匙,每种颜色的钥匙开对应颜色的门,",
"价值是<span style=\"color: gold\">红 &gt; 蓝 &gt; 黄</span>。",
"<br>",
"<br>",
"为了更加方便,本塔增加了宝石血瓶显示数据的功能,这样你可以清晰地知道每个宝石增加了多少属性。",
"<br>",
"<br>",
"下面是勇士基础属性的说明:",
"<br>",
"<span style=\"color: lightgreen\">1. 生命值</span>",
"勇士的血量,当它归零时,游戏结束",
"<br>",
"<span style=\"color: lightcoral\">2. 攻击</span>",
"勇士的攻击,攻击越高,每回合对怪物造成的伤害越高",
"<br>",
"<span style=\"color: lightblue\">3. 防御</span>",
"勇士的防御,防御越高,怪物每回合对你造成的伤害越低",
"<br>",
"<span style=\"color: green\">4. 经验</span>",
"勇士的经验,到达一定值后会升级。本塔在状态栏中显示为距离升级剩余的经验",
"<br>",
"<span style=\"color: gold\">5. 金币</span>",
"勇士的金币,可以用于购买物品。本塔中在进入第二章后会有用",
"<br>",
"<span style=\"color: lightgreen\">6. 护盾</span>",
"勇士的护盾,用处是能够在战后减少同等数值的伤害,在本塔中可以使伤害变为负值。本塔中,在点开无上之盾技能后,",
"智慧会充当护盾。更多信息可以查看“勇士属性”条目。"
]
},
"noun": {
"text": "名词解释",
"condition": "true",
"desc": [
"本条目会解释诸如临界等魔塔术语,对魔塔有一定了解的可以直接忽略。",
"<br>",
"<br>",
"<span style=\"color: lightcoral\">1. 临界</span>",
"在魔塔中,临界是一个非常重要的东西。首先,我们很容易可以得到,吃攻击时只有当减少了战斗回合数时怪物的伤害会减少,",
"那么,吃攻击时怪物的减伤是不连续的。而<span style=\"color: gold\">距离下一次减少怪物的伤害需要加的攻击的量</span>",
"便是临界。当我们吃一个攻击恰好使怪物伤害减少时,称为“踩临界”。一般情况下,踩临界的减伤要比吃防御要高,",
"因此,当能踩到临界时,我们应当先踩临界,再吃防御。",
"<br>",
"<br>",
"<span style=\"color: lightblue\">2. 加防</span>",
"加防指的是加防对怪物的减伤。在本塔中会以“n防”的形式显示在怪物手册或其他地方。在本塔中一般你不需要刻意计算",
"临界与加防减伤,你可以在怪物手册中<span style=\"color: gold\">查看减伤折线图</span>",
"更多信息请查看“怪物手册”条目。",
"<br>",
"<br>",
"<span style=\"color: gold\">3. 咸鱼</span>",
"一般来讲,开不必开的门,或者使用不必使用的道具被称为咸鱼,或者是咸门,咸道具。一般情况下,说“咸”便是指咸鱼。",
"一般情况下,门后面有宝石且无法通过其他方式进入的都是必开门,而只有血瓶的都是咸鱼门。"
]
},
"shortcut": {
"text": "快捷键",
"condition": "true",
"desc": [
"这里包含本塔中所有的快捷键。对于手机端,可以点击工具栏的难度的位置切换工具栏至数字键。",
"下面会分为样板快捷键和本塔快捷键两类分别说明。可以ctrl+F进行搜索快捷键的功能。",
"<br>",
"<br>",
"下面是样板中的所有快捷键:",
"<br>",
"<span style=\"color: gold\">X</span>:打开怪物手册",
"<br>",
"<span style=\"color: gold\">S</span>:打开存档界面",
"<br>",
"<span style=\"color: gold\">D</span>:打开读档界面",
"<br>",
"<span style=\"color: gold\">A或5</span>:读取自动存档",
"<br>",
"<span style=\"color: gold\">W或6</span>:撤销读取的自动存档",
"<br>",
"<span style=\"color: gold\">Q</span>:打开装备栏",
"<br>",
"<span style=\"color: gold\">T</span>:打开道具栏",
"<br>",
"<span style=\"color: gold\">G</span>:打开楼层传送器",
"<br>",
"<span style=\"color: gold\">Z或单击勇士</span>:勇士转向",
"<br>",
"<span style=\"color: gold\">空格或双击勇士或7</span>:轻按(拾取勇士周围的宝物但不移动勇士)",
"<br>",
"<span style=\"color: gold\">Esc</span>:打开游戏菜单",
"<br>",
"<span style=\"color: gold\">R</span>:打开录像回放菜单",
"<br>",
"<span style=\"color: gold\">N</span>:询问是否返回游戏主菜单",
"<br>",
"<span style=\"color: gold\">V</span>:打开快捷商店",
"<br>",
"<span style=\"color: gold\">B</span>:打开数据统计界面",
"<br>",
"<span style=\"color: gold\">Alt + 数字键</span>:快速换装",
"<br>",
"<span style=\"color: gold\">PgUp或PgDn</span>:浏览地图",
"<br>",
"<span style=\"color: gold\">P</span>:打开评论区",
"<br>",
"<br>",
"下面是本塔中新增的快捷键(不包括技能,技能快捷键请在查看技能界面中查看):",
"<br>",
"<span style=\"color: gold\">M</span>:快速标记怪物",
"<br>",
"<span style=\"color: gold\">J</span>:打开技能树",
"<br>",
"<span style=\"color: gold\">H</span>:打开百科全书",
"<br>",
"<span style=\"color: gold\">E</span>:查看鼠标位置怪物的特殊属性信息",
"<br>",
"<span style=\"color: gold\">C</span>:查看鼠标位置怪物的详细临界信息"
]
},
"extraAttr": {
"text": "勇士属性",
"condition": "true",
"desc": [
"这里只对本塔中新增的勇士属性进行说明。",
"<br>",
"<br>",
"<span style=\"color: lightblue\">1. 智慧</span>",
"智慧是该塔的核心属性之一。智慧可用于智慧加点,该功能会在进入第一章后开启。使用智慧可以点技能树。",
"除此之外,智慧也有其它功能。例如点开无上之盾技能后智慧还可以充当护盾,第二章点开学习技能后可以使用智慧学习怪物技能等。",
"<br>",
"<br>",
"<span style=\"color: lightgreen\">2. 生命回复</span>",
"生命回复指的是勇士每回合回复的生命值。当与怪物战斗时,勇士每回合都会回复对应量的生命值。因此,当吃攻击时,",
"与怪物战斗的回合数可能会减少,导致生命回复的总回复量减少。不过大部分情况下不需要在意这一点,",
"减少一回合并不会对吸的血造成很大的影响,除了一些特殊情况。",
"该项会显示在状态栏的生命值右方偏下的位置。该项不会超过勇士防御的十分之一,如果真实值溢出,那么多余部分会忽略,",
"当防御提高时,其值会一同改变",
"<br>",
"<br>",
"<span style=\"color: lightcoral\">3. 额外攻击</span>",
"额外攻击指的是勇士每回合的额外造成的伤害。一般情况下,当勇士破了怪物的防御时,该项便会起作用。",
"额外攻击相当于魔攻,无法通过一般方式减免。当勇士攻击怪物时,每回合都会附加对应量的伤害,对坚固怪同样有效。",
"额外攻击会显示在状态栏的攻击右方偏下的位置。"
]
},
"statusBar": {
"text": "状态栏",
"condition": "true",
"desc": [
"在本塔中,状态栏与游戏画面是分开的。<span style=\"color: gold\">你可以自由拖动状态栏,也可以修改其大小</span>。",
"具体方法如下:点击一下状态栏之后,左上角的拖拽图标会放大,此时你可以按住它拖动状态栏。",
"你可以直接将鼠标放到状态栏的边框上,然后直接拖动以改变状态栏的大小。手机端可以先点击一下状态栏使边框",
"变宽,然后拖动。电脑端点击状态栏也可以使边框变宽。如果你想折叠状态栏,完全可以拖动状态栏的下边框,",
"然后直接拖动至上方,这时状态栏便会变成一条线,相当于折叠了状态栏",
"<br>",
"<br>",
"<span style=\"color: gold\">状态栏可以纵向滚动</span>",
"如果你发现状态栏显示不全,可以尝试拉大状态栏,或者纵向拖动状态栏,就像网页上下滚动一样。",
"电脑端还可以使用滚轮上下滚动。",
"<br>",
"<br>",
"如果你觉得状态栏有些碍事,你完全可以将其缩小,或者把它放到不碍事的地方。",
"<br>",
"<br>",
"状态栏上面可能会有按钮,你可以直接点击。",
"<br>",
"<br>",
"对状态栏布局的说明。",
"<br>",
"本塔的状态栏的布局较为灵活。它是横向的布局,在状态栏较宽时可以看到,属性会横向依次显示。按照显示顺序,",
"状态栏显示项依次为:",
"<br>",
"<br>",
"1. <span style=\"color: gold\">楼层名</span>,点击后进入浏览地图界面",
"<br>",
"2. <span style=\"color: gold\">勇士等级</span>",
"<br>",
"3. <span style=\"color: gold\">当前开启的技能</span>",
"<br>",
"4. <span style=\"color: lightgreen\">当前勇士生命值</span>,右方偏下为每回合回复的生命值",
",当点开治愈之泉技能时,右方偏上会显示距离增加生命回复剩余血瓶数",
"<br>",
"5. <span style=\"color: lightcoral\">当前勇士的攻击</span>,右方偏下为勇士的额外攻击",
"<br>",
"6. <span style=\"color: lightblue\">当前勇士的防御</span>,当有魔法防御时,右方偏下为勇士的魔法防御",
"<br>",
"7. <span style=\"color: lightgreen\">当前勇士的智慧</span>,可以用于智慧加点等",
"<br>",
"8. <span style=\"color: gold\">当前勇士的金币</span>",
"<br>",
"9. <span style=\"color: lightgreen\">当前勇士距离升级剩余经验数</span>",
"<br>",
"10. <span style=\"color: gold\">三色钥匙</span>",
"<br>",
"11. <span style=\"color: gold\">打开技能树</span>(进入第一章后开启)",
"<br>",
"12. <span style=\"color: gold\">查看勇士的技能</span>(进入第一章后开启)"
]
},
"markEnemy": {
"text": "标记怪物",
"condition": "true",
"desc": [
"标记怪物可以使你能够更加方便地了解一个怪物的情况。",
"<br>",
"你可以通过以下两种方式标记怪物:",
"<br>",
"1. 打开怪物手册,选中怪物,进入怪物更多信息栏,点击标记怪物。",
"<br>",
"2. 将鼠标移动到你想要标记的怪物上面,<span style=\"color: gold\">",
"按下M键</span>,即可标记怪物,注意浏览地图中不能用该方式标记。",
"手机端暂时没有快速标记怪物的方式。",
"<br>",
"<br>",
"<span style=\"color: gold\">当一个怪物被标记后,怪物会有以下行为</span>",
"<br>",
"1. 当勇士恰好能打败怪物时,会进行提示",
"<br>",
"2. 当怪物的伤害恰好低于勇士生命值的2/3或1/3时会进行提示",
"<br>",
"3. 当勇士恰好踩到怪物的临界时,会进行提示",
"<br>",
"4. 当怪物零伤时,会进行提示",
"<br>",
"5. 被标记的怪物会出现类似于状态栏的盒子,可以随意拖动和改变大小。你也可以选择关闭这个盒子,",
"被关闭后可以通过重新标记来打开。这个盒子会显示标记的怪物的临界与伤害信息等,与状态栏一样,可以纵向滚动。",
"<br>",
"<br>",
"这个功能可以用于标记boss或者较强的挡路怪当这些怪能够攻击时你可以直接收到信息不需要再时刻费心注意怪物的伤害。",
"<br>",
"<br>",
"<span style=\"color: gold\">注意,标记的怪物是不计入存档的,同时标记的怪物只在本次游戏中有效,刷新页面后便会消失。</span>"
]
},
"book": {
"text": "怪物手册",
"condition": "true",
"desc": [
"本塔的怪物手册功能很多,下面一一介绍。",
"<br>",
"<br>",
"首先你可以按X打开怪物手册。除此之外将鼠标移动到怪物上也可以定点查看怪物的粗略信息。",
"将鼠标移动到一个怪物上,按下<span style=\"color: gold\">",
"E键</span>,可以查看该怪物的特殊属性信息。按下<span style=\"color: gold\">",
"C键</span>,可以查看该怪物的详细临界信息。",
"<br>",
"<br>",
"怪物手册打开的时候有一个0.6秒的动画,如果不想要可以在开头捡的系统设置里面关闭(默认关闭)。",
"<br>",
"<br>",
"打开怪物手册后,怪物手册的布局与样板自带的类似。与样板不同的是,这里的怪物手册不再是翻页式结构。",
"<span style=\"color: gold\">这里的怪物手册是滚动式结构</span>",
"你可以像浏览网页一样,用手指或鼠标上下滚动或者拖动右边的滚动条,电脑端还可以使用滚轮。",
"对于电脑端还可以使用键盘操作。上和下可以上下选择怪物左和右可以向上或向下移动5个怪物。这些操作与样板都类似。",
"<br>",
"<br>",
"点击一个怪物或者按下回车空格后,将进入怪物详细信息界面。这个界面分为多个栏,分别是特殊属性栏,详细临界栏,更多信息栏。",
"进入怪物详细信息后默认在特殊属性栏,该栏可以查看怪物的特殊属性。",
"注意特殊属性依然可以纵向滚动。在特殊属性下方,",
"是怪物的临界表,可以粗略地查看怪物的临界信息。在下方,你可以点击详细临界信息进入详细临界栏。",
"<br>",
"<br>",
"在详细临界栏中,怪物的伤害会以<span style=\"color: gold\">可视化折线图</span>的方式显示出来,",
"从而你可以更为清晰地看出怪物减伤趋势。",
"除了查看怪物伤害曲线,你还可以规划宝石。每个折线图下方都有一个滑动条,你可以拖动来模拟吃宝石。",
"注意,拖动时,滑动条左边会显示当前的加攻或加防次数,这个数值指的是在勇士所在地图中需要吃的最弱的宝石数量。",
"例如当前勇士所在地图中最弱的宝石加2点攻击加攻次数为3那么勇士的攻击增加量就为6。",
"勇士增加的攻击数值也会在下方显示。当加攻次数和加防次数改变时,折线图也会变化。",
"当前状态下怪物的伤害以及减伤总量也会在下方显示。<span style=\"color: gold\">",
"注意在此栏中无法通过点击屏幕回到怪物手册界面,更多信息请查看最后一段</span>。",
"<br>",
"<br>",
"在特殊属性栏,点击下方的怪物更多信息可以进入更多信息栏。此栏中,你可以查看怪物描述。但这不是这一栏的核心功能。",
"这一栏的核心功能是标记怪物。被标记的怪物会有一些非常方便的行为,这些行为可以在“",
"<span style=\"color: gold\">标记怪物</span>”条目中查看。",
"<br>",
"<br>",
"注意,在怪物详细信息中,除详细临界栏外均可以通过点击屏幕返回到怪物手册界面。",
"如果你是电脑端,在任意栏目中<span style=\"color: gold\">按下X键</span>会退出怪物手册,返回游戏,",
"<span style=\"color: gold\">按下回车Enter键</span>会回到怪物手册界面。"
]
},
"fly": {
"text": "楼层传送器",
"condition": "true",
"desc": [
"楼传界面打开时会有一个0.6秒的动画,如果不想要可以在开头捡的系统设置里面关闭。(默认关闭)",
"<br>",
"<br>",
"本塔的楼层传送器是一个集<span style=\"color: gold\">分区、小地图、楼层传送、浏览地图</span>于一体的多功能楼传。",
"<a href=\"https://unanmed.github.io/HumanBreak/maps/index.html\" target=\"_blank\">你也可以点击这里</a>查看所有区域的缩略图。",
"下面是楼传的具体说明:",
"<br>",
"<br>",
"首先,对于电脑端,最左侧显示区域信息,手机端则在上方的左侧。",
"<br>",
"<br>",
"然后,区域的右侧是小地图栏,这一栏会显示楼层的平面结构。你可以拖动,也可以使用滚轮或者双指放缩,当放缩到一定大小时,",
"会显示地图的缩略图。直接点击地图也可以选中地图,再次点击会传送至目标地图。",
"<br>",
"<br>",
"对于电脑端,最右侧是当前选中的地图的缩略图,手机则在下方,点击缩略图也可以传送。缩略图的下方是当前选中的地图名,",
"左右各有两个按钮表示后退10层、后退1层、前进1层、前进10层与样板的楼传的按钮功能类似对于小地图无法显示的单层",
"可以使用该功能到达。",
"<br>",
"<br>",
"最下方是设置按钮,可以切换无边框模式,电脑端还可以切换传统按键模式,传统按键模式下按键遵循样板的楼传按键方式。",
"对于非传统模式,<span style=\"color: gold\">上下左右</span>可以移动地图,",
"<span style=\"color: gold\">PageUp和PageDown</span>可以前进1层或后退1层。"
]
},
"tools": {
"text": "道具栏与装备栏",
"condition": "true",
"desc": [
"道具栏与装备栏打开时会有一个0.6秒的动画,如果不想要可以在开头捡的系统设置里面关闭。(默认关闭)",
"<br>",
"<br>",
"本塔的道具栏没有特别之处,这里不需要说明。主要是装备栏。",
"<br>",
"<br>",
"本塔的装备栏手机和电脑端不同,电脑端比手机端多了一个勇士属性的显示。在装备栏的装备列表栏,",
"<span style=\"color: gold\">上方有两个选择框与一个排序方式的选项</span>。",
"这三个可以筛选你拥有的装备并进行排序,从而让你能够更清楚地知道哪个装备更强。",
"第一个选择框可以筛选装备增加的属性,如果装备不增加选择的属性,那么会不显示。第二个选择框可以筛选增加的属性的方式,",
"有数值增加和百分比增加两种。在这个选择框右边有一个图标,这个图标可以改变武器的排序方式,有升序和降序两种,默认为升序。",
"例如你拥有两个装备分别增加10攻击和20攻击三者你分别选择了攻击数值升序那么增加10攻击的装备会排在上面",
"而增加20攻击的装备会排在下面。",
"<br>",
"<br>",
"对于电脑端,如果你想装装备,<span style=\"color: gold\">可以直接拖动装备至装备孔</span>",
"也可以选中装备后再次点击。<span style=\"color: gold\">手机端暂时无法拖动装备</span>。当选中一个装备后,",
"电脑端和手机端均会显示装备增加或减少的属性,注意有的装备可能<span style=\"color: gold\">不增加属性但是有特殊功能</span>。",
"对于电脑端,还会直接在勇士属性栏显示增加或减少的属性。"
]
},
"achievement": {
"text": "成就",
"condition": "true",
"desc": [
"成就系统是本塔的一个独立系统。它不会像勇士属性一样跟随存档变化,而是只要你完成了成就,那么就永远完成了,",
"除非你清理了浏览器。每个成就都有成就点,<span style=\"color: gold\">成就点目前没有实际用途,",
"只是一个收集要素,对游戏进程没有任何影响。</span>",
"<br>",
"<br>",
"成就分为三种,普通成就,挑战成就和探索成就。普通成就完成难度一般较低,挑战成就完成难度较高,",
"而探索成就一般需要你自己探索如何完成。对于完成度类型的探索成就,它的完成度由到达过的地图与本章完成的成就数决定。",
"<br>",
"<br>",
"<span style=\"color: gold\">调试模式下无法完成成就!</span>"
]
},
"score": {
"text": "计分方式",
"condition": "true",
"desc": [
"第一章计分方式:血量 + 黄 * 5000 + 蓝 * 15000",
"<br>",
"第二章计分方式:血量 / 10 + 黄 * 2000 + 蓝 * 5000 + 红 * 10000"
]
},
"skillTree": {
"text": "技能树",
"condition": "flags.chapter > 0",
"desc": [
"打开技能树可以点击状态栏的<span style=\"color: gold\">",
"技能树按钮</span>(如果发现没有显示可以尝试上下滚动状态栏),还可以按",
"<span style=\"color: gold\">快捷键J</span>打开。",
"<br>",
"<br>",
"技能树是本塔的主要玩法之一。它可以让你使用智慧来学习技能,增加属性等。智慧在状态栏显示在防御的下一项,",
"绿宝石可以增加勇士的智慧。",
"<br>",
"<br>",
"打开技能树页面后,你可以在上方看到技能的名称与描述,下方会显示技能树,以及升级要求等。点击一个技能可以选中技能,",
"再次点击可以升级技能。注意,前置技能栏可以上下滚动,因此如果发现显示不全,可以尝试上下滚动前置技能栏",
"<br>",
"<br>",
"注意,技能在点开之后是无法取消的,因此,加点时请慎重加点。注意,部分技能是必点技能,这些技能会在技能说明中明确指出,",
"这些技能一般需要尽早点出。"
]
},
"study": {
"text": "学习",
"condition": "Mota.Plugin.require('skillTree_g').getSkillLevel(11) > 0",
"desc": [
"本条目会详细说明学习的机制与所有可以被学习的技能被学习后的效果。当前已经学习的技能会以与状态栏类似的盒子展示出来。",
"<br>",
"<br>",
"首先学习技能消耗的智慧点会越来越多初始消耗的智慧点为500每学习一次增加250。",
"学习的技能可以持续5场战斗在技能树界面每升级一次增加3场",
"<span style=\"color: gold\">当前为${Mota.Plugin.require('skillTree_g').getSkillLevel(11) * 3 + 2}场</span>。",
"学习后对应属性的值,例如抱团怪增加的属性百分比,会与被学习的怪物相同。学习界面可以使用背包中的道具或点击状态栏打开。",
"<br>",
"<br>",
"下面会详细说明每一种可以被学习的技能被学习后的效果,没有列出的均不可学习。",
"<br>",
"<br>",
"<span style=\"color: #fc3\">1. 致命一击</span>勇士每5回合对怪物造成一次强力攻击。",
"<br>",
"<span style=\"color: #bbb0ff\">2. 恶毒</span>:勇士攻击无视怪物的防御。",
"<br>",
"<span style=\"color: #c0b088\">3. 坚固</span>:勇士防御不低于怪物的攻击-1。",
"<br>",
"<span style=\"color: #fe7\">4. n连击</span>勇士每回合攻击n次",
"<br>",
"<span style=\"color: #b30000\">5. 饥渴</span>:勇士在战前吸取怪物一定量的攻击加载自己身上,",
"同时减少怪物相应量的攻击,优先于怪物。",
"<br>",
"<span style=\"color: #fa4\">6. 抱团</span>:勇士周围每有一个拥有抱团属性的怪物,勇士的属性便增加一定值。",
"相应地,拥有抱团属性的怪物也会受到勇士的加成。",
"<br>",
"<span style=\"color: #b0c0dd\">7. 勇气之刃</span>:勇士第一回合造成一定量的伤害,之后正常。",
"<br>",
"<span style=\"color: #ff00d2\">8. 勇气冲锋</span>勇士首先发动冲锋造成一定量的伤害眩晕怪物5回合。",
"学习该技能后,勇士无条件先手。",
"<br>",
"<span style=\"color: #bbb0ff\">9. 魔攻</span>:勇士攻击无视怪物的防御。",
"<br>",
"<span style=\"color: #b0b666\">10. 先攻</span>:勇士无条件先手。"
]
},
"special1": {
"text": "第一章怪物特技",
"condition": "flags.chapter > 0",
"desc": [
"这里会展示第一章的怪物中需要特别说明的怪物特技。",
"<br>",
"<br>",
"<span style=\"color: #c0b088\">1. 坚固</span>",
"在本塔中,额外攻击可以对坚固怪造成额外伤害。",
"<br>",
"<br>",
"<span style=\"color: #80eed6\">2. 绝对防御</span>",
"该怪物一般可以用于刷血。该怪物可以使你每回合对怪物造成的伤害恰好为1导致战斗回合数很高因此可以刷血。",
"<br>",
"<br>",
"<span style=\"color: #fc3\">3. 致命一击、勇气之刃、勇气冲锋</span>",
"造成的伤害为怪物每回合对勇士的伤害的一定倍数,而非攻击提高一定倍数。"
]
},
"special2": {
"text": "第二章怪物特技",
"condition": "flags.chapter > 1",
"desc": [
"这里会展示第二章的怪物中需要特别说明的怪物特技。",
"<br>",
"<br>",
"<span style=\"color: #f66\">1. 电摇嘲讽</span>",
"该特技会撞碎路上的所有地形和门,不需要消耗钥匙,拾取路上的所有道具,与路上的怪物战斗,最后与该怪物战斗。",
"如果怪物所在位置可以被嘲讽,那么勇士会被继续嘲讽。如果在被嘲讽的路上可以被其他怪物嘲讽,则不会触发。",
"如果一个点可以被多个怪物嘲讽,那么会优先选择最靠左上角的怪物。在地图上会标记出勇士的移动方向。",
"<span style=\"color: gold\">在被嘲讽之前会自动存档。</span>",
"<br>",
"<br>",
"<span style=\"color: #d8a\">2. 永夜</span>、<span style=\"color: #ffd\">极昼</span>",
"战斗后会在本楼层中加减怪物与勇士的攻防每个楼层会单独存储。例如你在1楼层增加了100点攻击2楼层减少了100点攻击",
"那么当你从2楼层到1楼层时攻击会增加200点反之亦然。注意这里没有计算buff。"
]
}
}

View File

@ -1,8 +0,0 @@
{
"redSwordsman": {
"box-shadow": "0px 0px 8px white"
},
"E374": {
"box-shadow": "0px 0px 8px gray"
}
}

View File

@ -1,101 +0,0 @@
{
"error": {
"1": "Unexpected error when posting danmaku. Error info: $1",
"2": "Unexpected loading error in loading resource '$1/$2''. Error info: $3",
"3": "Syntax error in parsing CSS: Unexpected ':'. Col: $1. CSS string: '$2'",
"4": "Syntax error in parsing CSS: Unexpected ';'. Col: $1. CSS string: '$2'",
"5": "Syntax error in parsing CSS: Missing property name after '-'. Col: $1. CSS string: '$2'",
"6": "Syntax error in parsing CSS: Unexpected end of css, expecting ':'. Col: $1. CSS string: '$2'",
"7": "Syntax error in parsing CSS: Unexpected end of css, expecting property value. Col: $1. CSS string: '$2'",
"8": "Post danmaku with not allowed css. Info: $1",
"9": "Cannot initialize shader program. Error info: $1",
"10": "Cannot compile $1 shader. Error info: $2",
"11": "Cache depth cannot larger than 31.",
"12": "Cannot move while status is not 'moving'. Call 'readyMove' first.",
"13": "Cannot compile $1 shader. Error info: $2",
"14": "",
"15": "",
"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.",
"20": "Cannot create render element for tag '$1', since there's no registration for it.",
"21": "Incorrect render prop type is delivered. key: '$1', expected type: '$2', delivered type: '$3'",
"22": "Incorrect props for custom tag. Please ensure you have delivered 'item' prop and other required props.",
"23": "Cannot get reader when fetching '$1'.",
"24": "Cannot decode source type of '$1', since there is no registered decoder for that type.",
"25": "Unknown audio type. Header: '$1'",
"26": "Uncaught error when fetching stream data from '$1'. Error info: $2.",
"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.",
"1401": "Halo extension needs 'floor-binder' extension as dependency."
},
"warn": {
"1": "Resource with type of 'none' is loaded.",
"2": "Repeat load of resource '$1/$2'.",
"3": "Unknown danmaku tag: $1",
"4": "Ignored a mismatched ']' in danmaku.",
"5": "Repeat post danmaku.",
"6": "Registered special danmaku element: $1.",
"7": "Unknown special danmaku element: '$1'.",
"8": "Incomplete render data is put. None will be filled to the lacked data.",
"9": "Data transfered is partially (or totally) out of range. Overflowed data will be ignored.",
"10": "Cannot resolve big image of enemy '$1;.",
"11": "Cannot resolve material $1. Material not exists.",
"12": "Cannot mark buffable with a non-number status. Key: '$1'.",
"13": "Cannot set buff of non-number status. Key: '$1'.",
"14": "Cannot add status of non-number status. Key: '$1'.",
"15": "Cannot get item of a non-item block on loc: $1,$2,$3.",
"16": "Override repeated state key: '$1'.",
"17": "Floor-damage extension needs 'floor-binder' extension as dependency.",
"18": "Uncaught error in posting like info for danmaku. Danmaku id: $1.",
"19": "Repeat light id: '$1'.",
"20": "Cannot apply animation to camera operation that does not belong to it.",
"21": "Cannot apply transition to camera operation that does not belong to it.",
"22": "There is already an active camera for delivered render item. Consider using 'Camera.for' or diable the active camera to avoid some exceptions.",
"23": "Render item with id of '$1' has already exists. Please avoid repeat id since it may cause issues when use 'getElementById'.",
"24": "Uniform block can only be used in glsl version es 300.",
"25": "Cannot activate weather since there's no weather with id of '$1'.",
"26": "Cannot set attribute when only element number specified. Use 'pointer' or 'pointerI' instead.",
"27": "Cannot vertex attribute integer point when specified as float. Use 'set' or 'pointer' instead.",
"28": "Redefinition of shader $1: '$2'",
"29": "Cannot define new texture since texture index is larger than max texture count.",
"30": "Cannot use indices named $1 since no definition for it. Please define it in advance.",
"31": "Cannot use indices since the indices instance is not belong to the program.",
"32": "Sub-image exceeds texture dimensions, auto adjusting size.",
"33": "Cannot modify MotaOffscreenCanvas2D that is freezed.",
"34": "Repeated render tag registration: '$1'.",
"35": "Cannot append child on plain render item, please ensure you have overrided 'appendChild' method in your own element.",
"36": "Cannot remove child on plain render item, please ensure you have overrided 'removeChild' method in your own element.",
"37": "Cannot execute 'requestSort' on plain render item, please ensure you have overrided 'requestSort' method in your own element.",
"38": "Using plain text in jsx is strongly not recommended, since you can hardly modify its attributes. Consider using Text element instead.",
"39": "Plain text is not supported outside Text element.",
"40": "Cannot return canvas that is not provided by this pool.",
"41": "Width of text content components must be positive. receive: $1",
"42": "Repeated Textbox id: '$1'.",
"43": "Cannot set icon of '$1', since it does not exists. Please ensure you have delivered correct icon id or number.",
"44": "Unexpected end when loading stream audio, reason: '$1'",
"45": "Audio route with id of '$1' has already existed. New route will override old route.",
"46": "Cannot pipe new StreamReader object when stream is loading.",
"47": "Audio stream decoder for audio type '$1' has already existed.",
"48": "Sample rate in stream audio must be constant.",
"49": "Repeated patch for '$1', key: '$2'.",
"50": "Unknown audio extension name: '$1'",
"51": "Cannot decode sound '$1', since audio file may not supported by 2.b.",
"52": "Cannot play sound '$1', since there is no added data named it.",
"53": "Cannot $1 audio route '$2', since there is not added route named it.",
"54": "Missing start tag for '$1', index: $2.",
"55": "Unchildable tag '$1' should follow with param.",
"56": "Method '$1' has been deprecated. Consider using '$2' instead.",
"57": "Repeated UI controller on item '$1', new controller will not work.",
"58": "Fail to set ellipse round rect, since length of 'ellipse' property should only be 2, 4, 6 or 8. delivered: $1",
"59": "Unknown icon '$1' in parsing text content.",
"60": "Repeated Tip id: '$1'.",
"61": "Unexpected recursive call of $1.update in render function. Please ensure you have to do this, if you do, ignore this warn.",
"62": "Recursive fallback fonts in '$1'.",
"63": "Uncaught promise error in waiting box component. Error reason: $1",
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
}
}

View File

@ -1,25 +0,0 @@
{
"resource": [
"zip.resource.zip",
"zip.weather.zip",
"zip.materials.zip",
"materials.keyboard.png",
"bgms.tower.opus",
"bgms.cave.opus",
"bgms.mount.opus",
"bgms.towerBoss3.opus",
"bgms.winter.opus",
"bgms.title.opus",
"bgms.road.opus",
"bgms.beforeBoss.opus",
"bgms.winterTown.opus",
"bgms.towerBoss.opus",
"bgms.grass.opus",
"bgms.plot1.opus",
"bgms.escape.opus",
"bgms.towerBoss2.opus"
],
"stereoSE": [
""
]
}

View File

@ -1,62 +0,0 @@
{
"screen": {
"fullscreen": [
"是否全屏进行游戏全屏后按ESC退出全屏开启后将不能通过按ESC开启系统设置菜单",
"请按下方的按钮打开。进入或退出全屏后请存读档一下,以解决一部分绘制问题。"
],
"halo": ["开启后,会在地图上显示范围光环。"],
"itemDetail": ["是否在地图上显示宝石血瓶装备等增加的属性值"],
"transition": [
"是否展示当一个ui界面如怪物手册等的打开与关闭时的动画。当此项开启时",
"所有界面被打开或关闭时都会展示动画,否则会直接展示出来"
],
"fontSize": [
"在各种 ui 界面中显示的文字大小,范围为 2 - 48。注意字体过大可能会引起 ui 布局发生错误"
],
"criticalGem": ["临界是否显示为在当前地图要吃的宝石数"]
},
"action": {
"autoSkill": [
"开启后,打怪物的时候会自动选择伤害最低的技能。同时显伤也会显示此状态下的伤害,",
"临界也会考虑技能在内"
],
"fixed": [
"开启后,当鼠标移动到怪物上时,会以盒子的形式展示该点的怪物信息。手机端此功能无效。",
"<br>",
"<br>",
"注当鼠标移动到怪物上时经过200毫秒才会显示信息防止误操作。"
],
"hotkey": ["设置游戏中会用到的一些快捷键"],
"toolbar": [
"允许你在工具栏上自定义按钮,包括使用道具、开关技能、按下某个按键等。",
"推荐手机进行一些设置"
]
},
"utils": {
"betterLoad": [
"<span style=\"color: yellow; font-weight: 700\">试验性功能</span>",
"<br>",
"开启后游戏将对加载进行优化,缩短进入游戏时的加载时长,而在游戏中对资源进行部分性按需加载,从而对加载进行优化。",
"该设置不会影响你的正常游戏,但如果网络环境较差,可能会导致部分楼层转换时间明显变长。",
"<br>",
"<br>",
"注:修改后刷新页面起效。"
],
"autoScale": [
"开启后,每次进入游戏时会自动缩放游戏画面至合适值。该项只对电脑端有效。",
"<br>",
"<br>",
"缩放原则如下:",
"<br>",
"1. 首先尝试缩放至最大缩放比例",
"<br>",
"2. 如果缩放后游戏画面高度高于页面高度的95%,那么缩小一个缩放比例,否则保持最大比例"
]
},
"fx": {
"paraLight": [
"是否开启野外的平行光阴影,在野外将会显示平行光阴影,模拟太阳光,拥有不错的视觉效果"
],
"frag": ["开启后,在打败怪物后会触发怪物碎裂特效。"]
}
}

View File

@ -1,43 +0,0 @@
{
"none": {
"text": "无",
"opened": "true",
"desc": [
"当前未选择技能"
]
},
"blade": {
"text": "1断灭之刃",
"opened": "true",
"desc": [
"<span style=\"color: gold\">快捷键1</span>,开启后勇士攻击增加${level:2 * 10}%",
"同时防御减少${level:2 * 10}%。",
"<br>",
"<br>",
"当前等级:${level:2}"
]
},
"jump": {
"text": "2跳跃",
"opened": "true",
"desc": [
"<span style=\"color: gold\">快捷键2</span>消耗200点生命值困难消耗400点一个地图只能使用3次",
"如果前方为可通行的地面,则不能使用该技能,如果前方为怪物,则将怪物移至勇士视线上第一个不能通行的方块后",
"如果前方为障碍物,则直接跳到该障碍物的后方。",
"<br>",
"<br>",
"进入第二章后不再消耗生命值。"
]
},
"shield": {
"text": "3铸剑为盾",
"opened": "true",
"desc": [
"<span style=\"color: gold\">快捷键3</span>,开启后勇士防御增加${level:10 * 10}%",
"同时攻击减少${level:10 * 10}%。",
"<br>",
"<br>",
"当前等级:${level:10}"
]
}
}

View File

@ -1,37 +0,0 @@
[
"按下C可以查看鼠标位置怪物临界",
"按下E可以查看鼠标位置怪物的详细属性",
"将鼠标移动到光环怪上以查看其产生的光环",
"字体太大?试试在背包的系统设置里面调整字体大小吧!",
"字体太小?试试在背包的系统设置里面调整字体大小吧!",
"按键不合心意?试试在背包的系统设置里面自定义快捷键",
"拖动状态栏左上角可以移动状态栏哦!",
"拖动状态栏右下角可以缩放状态栏哦!",
"按下M键鼠标位置的怪物的信息就会被你看光啦",
"咱就是说,要不要试一下工具栏的最后一个按钮?",
"要不要试试工具栏倒数第二个按钮呢?",
"想自定义工具栏?去背包的系统设置看看吧!",
"冷知识:临界界面可以拖动滚动条来查看减伤情况",
"可以用滚轮或者双指缩放小地图!",
"楼传的最左侧一栏可以选择区域!",
"冷知识:装备栏左栏最上面可以修改装备排序",
"冷冷冷知识:装备栏左栏最上面右侧可以更改顺序或倒序",
"第一章使用跳跃技能可是要扣血的!要注意!",
"按H查看本游戏的百科全书",
"给别人炫耀一下自己的成就点吧!虽然不能记榜(",
"抱团属性会在怪物右上角显示加成数量!",
"乾坤挪移属性会在怪物左上角显示“乾”字!",
"电脑端可以试试按F11全屏游玩",
"手机端要不试试横屏玩?",
"不在楼梯边也可以使用楼传!",
"技能树的右下角可以切换章节!",
"开启自动切换技能就会自动帮你选择最优技能了!",
"魔塔不仅有撤回还有恢复按W或6就可以了",
"觉得卡顿?可以去试着设置里面关闭一些特性!",
"从第二章开始怪物负伤害量不会超过其生命的1/4",
"生命回复不会超过防御的十分之一",
"不想看小贴士?设置里面可以关掉!",
"不小心进入了追猎范围?读取自动存档撤回到进入前吧!",
"不小心进入了电摇嘲讽范围?读取自动存档撤回到进入前吧!",
"小地图出现卡顿?试试在背包中系统设置里把小地图懒更新打开吧!"
]

View File

@ -1,516 +1,95 @@
import type {
Disposable,
EventEmitter,
IndexedEventEmitter
} from '@motajs/legacy-common';
import type { loading } from './game';
import type { Hotkey } from '@/core/main/custom/hotkey';
import type { Keyboard } from '@/core/main/custom/keyboard';
import type { CustomToolbar } from '@/core/main/custom/toolbar';
import type { Focus, GameUi, UiController } from '@/core/main/custom/ui';
import type { gameListener, hook } from './game';
import type { MotaSetting, SettingDisplayer } from '@/core/main/setting';
import type { GameStorage } from '@/core/main/storage';
import type { DamageEnemy, EnemyCollection } from './enemy/damage';
import type { specials } from './enemy/special';
import type { KeyCode } from '@motajs/client-base';
import type { Ref } from 'vue';
import type * as battle from './enemy/battle';
import type * as hero from './state/hero';
import type * as damage from './enemy/damage';
import type { Logger } from '@motajs/common';
import type { Danmaku } from '@/core/main/custom/danmaku';
import type * as misc from './mechanism/misc';
import type { Render } from '@motajs/client';
import type { ItemState } from './state/item';
import type { HeroKeyMover } from '@/module/action/move';
import type { BlockMover, HeroMover, ObjectMoverBase } from './state/move';
import type * as Animation from 'mutate-animate';
import type { WeatherController } from '@/module/weather/weather';
interface ClassInterface {
// 渲染进程与游戏进程通用
EventEmitter: typeof EventEmitter;
IndexedEventEmitter: typeof IndexedEventEmitter;
Disposable: typeof Disposable;
// 定义于渲染进程
GameStorage: typeof GameStorage;
MotaSetting: typeof MotaSetting;
SettingDisplayer: typeof SettingDisplayer;
Focus: typeof Focus;
GameUi: typeof GameUi;
UiController: typeof UiController;
Hotkey: typeof Hotkey;
Keyboard: typeof Keyboard;
CustomToolbar: typeof CustomToolbar;
Danmaku: typeof Danmaku;
// todo: 放到插件 ShaderEffect: typeof ShaderEffect;
// 定义于游戏进程,渲染进程依然可用
EnemyCollection: typeof EnemyCollection;
DamageEnemy: typeof DamageEnemy;
}
type _IBattle = typeof battle;
type _IHero = typeof hero;
type _IDamage = typeof damage;
interface FunctionInterface extends _IBattle, _IHero, _IDamage {
// 定义于渲染进程录像中会进行polyfill但是不执行任何内容
readyAllResource(): void;
// 定义于游戏进程,渲染进程依然可用
// todo
}
interface VariableInterface {
// 定义于渲染进程录像中会进行polyfill
loading: typeof loading;
hook: typeof hook;
gameListener: typeof gameListener;
mainSetting: MotaSetting;
gameKey: Hotkey;
mainUi: UiController;
fixedUi: UiController;
KeyCode: typeof KeyCode;
// isMobile: boolean;
settingStorage: GameStorage;
status: Ref<boolean>;
// 定义于游戏进程,渲染进程依然可用
haloSpecials: number[];
enemySpecials: typeof specials;
logger: Logger;
}
import type * as Client from '@motajs/client';
import type * as ClientBase from '@motajs/client-base';
import type * as Common from '@motajs/common';
import type * as LegacyClient from '@motajs/legacy-client';
import type * as LegacyCommon from '@motajs/legacy-common';
import type * as LegacySystem from '@motajs/legacy-system';
import type * as LegacyUI from '@motajs/legacy-ui';
import type * as Render from '@motajs/render';
import type * as RenderCore from '@motajs/render-core';
import type * as RenderElements from '@motajs/render-elements';
import type * as RenderStyle from '@motajs/render-style';
import type * as RenderVue from '@motajs/render-vue';
import type * as System from '@motajs/system';
import type * as SystemAction from '@motajs/system-action';
import type * as SystemUI from '@motajs/system-ui';
interface ModuleInterface {
Mechanism: {
BluePalace: typeof misc.BluePalace;
NightSpecial: typeof misc.NightSpecial;
MiscData: typeof misc.MiscData;
HeroSkill: typeof misc.HeroSkill;
};
Effect: {};
Render: typeof Render;
State: {
ItemState: typeof ItemState;
HeroMover: typeof HeroMover;
BlockMover: typeof BlockMover;
ObjectMoverBase: typeof ObjectMoverBase;
heroMoveCollection: {
mover: HeroMover;
keyMover?: HeroKeyMover;
};
};
Action: {
HeroKeyMover: typeof HeroKeyMover;
};
Animation: typeof Animation;
Weather: {
controller: WeatherController;
WeatherController: typeof WeatherController;
};
}
interface SystemInterfaceMap {
class: ClassInterface;
fn: FunctionInterface;
var: VariableInterface;
module: ModuleInterface;
}
type InterfaceType = keyof SystemInterfaceMap;
interface PluginInterface {
// 渲染进程定义的插件
chase_r: typeof import('../plugin/chase');
gameCanvas_r: typeof import('../plugin/fx/gameCanvas');
frag_r: typeof import('../plugin/fx/frag');
boss_r: typeof import('../plugin/boss');
// 游戏进程定义的插件
utils_g: typeof import('../plugin/game/utils');
shop_g: typeof import('../plugin/game/shop');
replay_g: typeof import('../plugin/game/replay');
skillTree_g: typeof import('../plugin/game/skillTree');
removeMap_g: typeof import('../plugin/game/removeMap');
remainEnemy_g: typeof import('../plugin/game/enemy/remainEnemy');
chase_g: typeof import('../plugin/game/chase');
skill_g: typeof import('../plugin/game/skill');
itemDetail_g: typeof import('../plugin/game/fx/itemDetail');
checkBlock_g: typeof import('../plugin/game/enemy/checkblock');
}
interface PackageInterface {
axios: typeof import('axios');
// 'chart.js': typeof import('chart.js');
jszip: typeof import('jszip');
lodash: typeof import('lodash-es');
'lz-string': typeof import('lz-string');
'mutate-animate': typeof import('mutate-animate');
// vue: typeof import('vue');
'@motajs/client': typeof Client;
'@motajs/client-base': typeof ClientBase;
'@motajs/common': typeof Common;
'@motajs/legacy-client': typeof LegacyClient;
'@motajs/legacy-common': typeof LegacyCommon;
'@motajs/legacy-system': typeof LegacySystem;
'@motajs/legacy-ui': typeof LegacyUI;
'@motajs/render': typeof Render;
'@motajs/render-core': typeof RenderCore;
'@motajs/render-elements': typeof RenderElements;
'@motajs/render-style': typeof RenderStyle;
'@motajs/render-vue': typeof RenderVue;
'@motajs/system': typeof System;
'@motajs/system-action': typeof SystemAction;
'@motajs/system-ui': typeof SystemUI;
}
export interface IMota {
rewrite: typeof rewrite;
r: typeof r;
rf: typeof rf;
/** 样板插件接口 */
Plugin: IPlugin;
/**
* 使
* polyfill使
* 使main.replayChecking进行检查使
*/
// Package: IPackage;
/**
*
* @param type
* @param key
*/
require<T extends InterfaceType, K extends keyof SystemInterfaceMap[T]>(
type: T,
key: K
): SystemInterfaceMap[T][K];
require<K extends keyof ModuleInterface>(key: K): ModuleInterface[K];
/**
*
* @param type
* @param key
*/
require(type: InterfaceType, key: string): any;
/**
*
* @param type
*/
requireAll<T extends InterfaceType>(type: T): SystemInterfaceMap[T];
require<T = unknown>(key: string): T;
/**
*
* @param type
* @param key
* @param data
*/
register<T extends InterfaceType, K extends keyof SystemInterfaceMap[T]>(
type: T,
register<K extends keyof ModuleInterface>(
key: K,
data: SystemInterfaceMap[T][K]
data: ModuleInterface[K]
): void;
/**
*
* @param type
* @param key
* @param data
*/
register(type: InterfaceType, key: string, data: any): void;
}
export interface IPlugin {
inited: boolean;
/**
*
*/
init(): void;
/**
*
* @param plugin
*/
require<K extends keyof PluginInterface>(plugin: K): PluginInterface[K];
/**
*
* @param plugin
*/
require(plugin: string): any;
/**
*
*/
requireAll(): PluginInterface & { [x: string]: any };
/**
*
* @param plugin
* @param data
* @param init plugin和data
*/
register<K extends keyof PluginInterface>(
plugin: K,
data: PluginInterface[K],
init?: (plugin: K, data: PluginInterface[K]) => void
): void;
/**
*
* @param plugin
* @param init
*/
register<K extends keyof PluginInterface>(
plugin: K,
init: (plugin: K) => PluginInterface[K]
): void;
/**
*
* @param plugin
* @param data
* @param init plugin和data
*/
register<K extends string, D>(
plugin: K,
data: D,
init?: (plugin: K, data: D) => void
): void;
/**
*
* @param plugin
* @param init
*/
register<K extends string>(plugin: K, init: (plugin: K) => any): void;
}
export interface IPackage {
/**
* 使
* @param name
*/
require<K extends keyof PackageInterface>(name: K): PackageInterface[K];
/**
* 使
*/
requireAll(): PackageInterface;
register<K extends keyof PackageInterface>(
name: K,
data: PackageInterface[K]
): void;
}
interface IPluginData {
/** 插件类型content表示直接注册了内容function表示注册了初始化函数内容从其返回值获取 */
type: 'content' | 'function';
data: any;
init?: (plugin: string, data?: any) => any;
}
class MPlugin {
private static plugins: Record<string, IPluginData> = {};
private static pluginData: Record<string, any> = {};
static inited = false;
constructor() {
throw new Error(`System plugin class cannot be constructed.`);
}
static init() {
for (const [key, data] of Object.entries(this.plugins)) {
if (data.type === 'content') {
data.init?.(key, data.data);
} else {
data.data = data.init!(key);
}
this.pluginData[key] = data.data;
}
this.inited = true;
}
static require(key: string) {
if (!this.inited) {
throw new Error(`Cannot access plugin '${key}' before initialize.`);
}
if (!(key in this.plugins)) {
throw new Error(`Cannot resolve plugin require: key='${key}'`);
}
return this.plugins[key].data;
}
static requireAll(): PluginInterface {
return this.pluginData as PluginInterface;
}
static register(key: string, data: any, init?: any) {
if (typeof data === 'function') {
this.plugins[key] = {
type: 'function',
init: data,
data: void 0
};
} else {
this.plugins[key] = {
type: 'content',
data,
init
};
}
}
}
class MPackage {
// @ts-ignore
private static packages: PackageInterface = {};
constructor() {
throw new Error(`System package class cannot be constructed.`);
}
static require<K extends keyof PackageInterface>(
name: K
): PackageInterface[K] {
return this.packages[name];
}
static requireAll() {
return this.packages;
}
static register<K extends keyof PackageInterface>(
name: K,
data: PackageInterface[K]
) {
this.packages[name] = data;
}
register(key: string, data: unknown): void;
}
/**
* Mota
* Mota
*/
class Mota {
private static classes: Record<string, any> = {};
private static functions: Record<string, any> = {};
private static variables: Record<string, any> = {};
private static modules: Record<string, any> = {};
class Mota implements IMota {
private modules: Record<string, any> = {};
static rewrite = rewrite;
static r = r;
static rf = rf;
static Plugin = MPlugin;
// static Package = MPackage;
r = r;
rf = rf;
constructor() {
throw new Error(`System interface class cannot be constructed.`);
}
static require(type: InterfaceType, key: string): any {
const data = this.getByType(type)[key];
require(key: string): any {
const data = this.modules[key];
if (data) return data;
else {
throw new Error(
`Cannot resolve require: type='${type}',key='${key}'`
);
throw new Error(`Cannot resolve module '${key}'`);
}
}
static requireAll<T extends InterfaceType>(type: T): SystemInterfaceMap[T] {
return this.getByType(type) as SystemInterfaceMap[T];
}
static register(type: InterfaceType, key: string, data: any) {
const obj = this.getByType(type);
if (key in obj) {
console.warn(
`重复的样板接口注册: type='${type}', key='${key}',已将其覆盖`
);
register(key: string, data: any) {
if (key in this.modules) {
console.warn(`模块注册重复: '${key}',已将其覆盖`);
}
obj[key] = data;
}
private static getByType(type: InterfaceType) {
return type === 'class'
? this.classes
: type === 'fn'
? this.functions
: type === 'var'
? this.variables
: this.modules;
}
}
type RewriteType = 'full' | 'front' | 'add';
type _F<F> = F extends (...params: infer P) => infer R ? [P, R] : never;
type _Func = (...params: any) => any;
/**
*
* @param base
* @param key base中叫什么
* @param type full表示全量复写front表示在原函数之前添加内容
* @param re full时表示将原函数完全覆盖front时表示将该函数添加到原函数之前
* @param bind base
* @param rebind base
*/
function rewrite<
O,
K extends SelectKey<O, _Func>,
R extends 'full' | 'front',
T = O
>(
base: O,
key: K,
type: R,
re: (
this: T,
...params: [..._F<O[K]>[0], ...any[]]
) => R extends 'full' ? _F<O[K]>[1] : void,
bind?: any,
rebind?: T
): (this: T, ...params: [..._F<O[K]>[0], ...any[]]) => _F<O[K]>[1];
/**
*
* @param base
* @param key base中叫什么
* @param type add表示在函数后追加
* @param re add时表示在原函数后面追加复写函数
*
* @param bind `base`
* @param rebind `base`
*/
function rewrite<O, K extends SelectKey<O, _Func>, T = O>(
base: O,
key: K,
type: 'add',
re: (
this: T,
...params: [_F<O[K]>[1], ..._F<O[K]>[0], ...any[]]
) => _F<O[K]>[1],
bind?: any,
rebind?: T
): (this: T, ...params: [..._F<O[K]>[0], ...any[]]) => _F<O[K]>[1];
function rewrite<O, K extends SelectKey<O, _Func>, T = O>(
base: O,
key: K,
type: RewriteType,
re: (this: T, ...params: [..._F<O[K]>[0], ...any[]]) => _F<O[K]>[1],
bind?: any,
rebind?: T
): (this: T, ...params: [..._F<O[K]>[0], ...any[]]) => _F<O[K]>[1] {
const func = base[key];
if (typeof func !== 'function') {
throw new Error(
`Cannot rewrite variable with type of '${typeof func}'.`
);
}
if (type === 'full') {
// @ts-ignore
return (base[key] = re.bind(rebind ?? base));
} else if (type === 'add') {
const origin = base[key];
function res(this: T, ...params: [..._F<O[K]>[0], ...any[]]) {
const v = (origin as _Func).call(bind ?? base, ...params);
// @ts-ignore
const ret = re.call(rebind ?? base, v, ...params);
return ret;
}
// @ts-ignore
return (base[key] = res);
} else {
const origin = base[key];
function res(this: T, ...params: [..._F<O[K]>[0], ...any[]]) {
// @ts-ignore
re.call(rebind ?? base, ...params);
const ret = (origin as _Func).call(bind ?? base, ...params);
return ret;
}
// @ts-ignore
return (base[key] = res);
this.modules[key] = data;
}
}
@ -526,6 +105,8 @@ function r<T = undefined>(fn: (this: T) => void, thisArg?: T) {
if (!main.replayChecking && main.mode === 'play') fn.call(thisArg as T);
}
const empty = () => {};
/**
* {@link r}
* `call``bind`
@ -541,8 +122,8 @@ function rf<F extends (...params: any) => any, T>(
fn: F,
thisArg?: T
): (this: T, ...params: Parameters<F>) => ReturnType<F> | undefined {
// @ts-ignore
if (main.replayChecking || main.mode === 'editor') return () => {};
// @ts-expect-error 录像验证的时候不能执行任何操作,因此返回空函数
if (main.replayChecking || main.mode === 'editor') return empty;
else {
return (...params) => {
return fn.call(thisArg, ...params);
@ -556,4 +137,4 @@ declare global {
}
}
window.Mota = Mota;
window.Mota = new Mota();

View File

@ -1,6 +1,5 @@
import { createApp } from 'vue';
import './game/index';
import './core/index';
import App from './App.vue';
import './styles.less';
import 'ant-design-vue/dist/antd.dark.css';

View File

@ -1,18 +1,6 @@
import { soundPlayer } from './audio';
import { patchAll } from './fallback';
import { create } from './render';
import { RainWeather } from './weather/rain';
import { WeatherController } from './weather/weather';
patchAll();
Mota.register('module', 'Weather', {
WeatherController,
RainWeather
});
Mota.register('module', 'Audio', {
soundPlayer
});
Mota.require('var', 'loading').once('coreInit', create);
export * from './weather';
export * from './audio';

View File

@ -5,6 +5,10 @@
"@motajs/client-base": "workspace:*",
"@motajs/common": "workspace:*",
"@motajs/render": "workspace:*",
"@motajs/render-core": "workspace:*",
"@motajs/render-elements": "workspace:*",
"@motajs/render-style": "workspace:*",
"@motajs/render-vue": "workspace:*",
"@motajs/system": "workspace:*",
"@motajs/system-ui": "workspace:*",
"@motajs/system-action": "workspace:*",

7
src/vite-env.d.ts vendored
View File

@ -1,7 +0,0 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

View File

@ -28,7 +28,8 @@
"src/**/*.vue",
"packages/**/*.ts",
"packages/**/*.d.ts",
"packages/**/*.tsx"
"packages/**/*.tsx",
"types/**/*.d.ts"
],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -23,7 +23,7 @@ type Forward<T> = {
type ForwardKeys<T> = keyof Forward<T>;
declare const Mota: import('../game/system').IMota;
declare const Mota: import('../../src/game/system').IMota;
interface Window {
Mota: import('../game/system').IMota;
Mota: import('../../src/game/system').IMota;
}

View File

@ -1,4 +1,3 @@
type FloorIds =
| 'empty'
| 'MT0'
@ -106,7 +105,7 @@ type FloorIds =
| 'MT94'
| 'MT95'
| 'MT96'
| 'MT97'
| 'MT97';
type ImageIds =
| 'IQ.png'
@ -140,7 +139,7 @@ type ImageIds =
| 'tower7.webp'
| 'winskin.png'
| 'winskin2.png'
| 'winskin3.png'
| 'winskin3.png';
type AnimationIds =
| 'amazed'
@ -164,7 +163,7 @@ type AnimationIds =
| 'sweat'
| 'sweat2'
| 'sword'
| 'zone'
| 'zone';
type SoundIds =
| '008-System08.opus'
@ -206,7 +205,7 @@ type SoundIds =
| 'shop.opus'
| 'thunder.opus'
| 'tree.opus'
| 'zone.opus'
| 'zone.opus';
type BgmIds =
| 'beforeBoss.opus'
@ -229,35 +228,33 @@ type BgmIds =
| 'towerBoss2.opus'
| 'towerBoss3.opus'
| 'winter.opus'
| 'winterTown.opus'
| 'winterTown.opus';
type FontIds =
| 'normal'
| 'FiraCode'
type FontIds = 'normal' | 'FiraCode';
interface NameMap {
'确定': 'confirm.opus';
'取消': 'cancel.opus';
'操作失败': 'error.opus';
'光标移动': 'cursor.opus';
'打开界面': 'open_ui.opus';
'读档': 'load.opus';
'存档': 'save.opus';
'获得道具': 'item.opus';
'回血': 'recovery.opus';
'炸弹': 'bomb.opus';
'飞行器': 'centerFly.opus';
'开关门': 'door.opus';
'上下楼': 'floor.opus';
'跳跃': 'jump.opus';
'破墙镐': 'pickaxe.opus';
'破冰镐': 'icePickaxe.opus';
'宝石': 'gem.opus';
'阻激夹域': 'zone.opus';
'穿脱装备': 'equip.opus';
'背景音乐': 'bgm.opus';
'攻击': 'attack.opus';
'背景图': 'bg.jpg';
'商店': 'shop.opus';
'领域': 'zone';
: 'confirm.opus';
: 'cancel.opus';
: 'error.opus';
: 'cursor.opus';
: 'open_ui.opus';
: 'load.opus';
: 'save.opus';
: 'item.opus';
: 'recovery.opus';
: 'bomb.opus';
: 'centerFly.opus';
: 'door.opus';
: 'floor.opus';
: 'jump.opus';
: 'pickaxe.opus';
: 'icePickaxe.opus';
: 'gem.opus';
: 'zone.opus';
穿: 'equip.opus';
: 'bgm.opus';
: 'attack.opus';
: 'bg.jpg';
: 'shop.opus';
: 'zone';
}

7
types/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}