This commit is contained in:
unanmed 2024-02-05 20:51:57 +08:00
parent 6ce86501f1
commit 84620ce779
42 changed files with 233 additions and 3803 deletions

3
components.d.ts vendored
View File

@ -10,15 +10,12 @@ declare module '@vue/runtime-core' {
AButton: typeof import('ant-design-vue/es')['Button']
ADivider: typeof import('ant-design-vue/es')['Divider']
AInput: typeof import('ant-design-vue/es')['Input']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AProgress: typeof import('ant-design-vue/es')['Progress']
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASlider: typeof import('ant-design-vue/es')['Slider']
ASwitch: typeof import('ant-design-vue/es')['Switch']
Box: typeof import('./src/components/box.vue')['default']
BoxAnimate: typeof import('./src/components/boxAnimate.vue')['default']
Changable: typeof import('./src/components/changable.vue')['default']
Colomn: typeof import('./src/components/colomn.vue')['default']
EnemyOne: typeof import('./src/components/enemyOne.vue')['default']
Scroll: typeof import('./src/components/scroll.vue')['default']

View File

@ -12,7 +12,8 @@
"update": "ts-node-esm script/update.ts",
"declare": "ts-node-esm script/declare.ts",
"type": "vue-tsc --noEmit",
"lines": "ts-node-esm script/lines.ts"
"lines": "ts-node-esm script/lines.ts",
"declaration": "vue-tsc -p tsconfig.declaration.json"
},
"dependencies": {
"@ant-design/icons-vue": "^6.1.0",

View File

@ -339,11 +339,15 @@ core.prototype._loadPluginSync = function () {
core.prototype._loadGameProcess = async function () {
// 加载游戏进程代码
if (main.pluginUseCompress) {
if (main.pluginUseCompress && main.replayChecking) {
await main.loadScript(`project/processG.min.js?v=${main.version}`);
} else {
if (main.mode === 'editor') {
await main.loadScript(`src/game/index.esm.ts`, true);
if (main.pluginUseCompress) {
await main.loadScript(`project/processG.min.js`);
} else {
await main.loadScript(`src/game/index.esm.ts`, true);
}
}
}
};

View File

@ -525,6 +525,11 @@ loader.prototype.loadOneMusic = function (name) {
music.loop = 'loop';
core.material.bgms[name] = music;
} else {
if (!main.renderLoaded) {
Mota.require('var', 'hook').once('renderLoaded', () => {
Mota.require('var', 'bgm').add(`bgms.${name}`, music);
});
}
Mota.require('var', 'bgm').add(`bgms.${name}`, music);
}
};
@ -538,8 +543,15 @@ loader.prototype.loadOneSound = function (name) {
if (main.mode === 'editor') {
core.loader._loadOneSound_decodeData(name, data);
} else {
const sound = Mota.require('var', 'sound');
sound.add(`sounds.${name}`, data);
if (!main.renderLoaded) {
Mota.require('var', 'hook').once('renderLoaded', () => {
const sound = Mota.require('var', 'sound');
sound.add(`sounds.${name}`, data);
});
} else {
const sound = Mota.require('var', 'sound');
sound.add(`sounds.${name}`, data);
}
}
},
function (e) {

View File

@ -2588,11 +2588,13 @@ maps.prototype._drawThumbnail_realDrawTempCanvas = function (
blocks,
options
) {
const setting = Mota.require('var', 'mainSetting');
options.ctx.imageSmoothingEnabled = !setting.getValue(
'screen.antiAliasing',
true
);
Mota.r(() => {
const setting = Mota.require('var', 'mainSetting');
options.ctx.imageSmoothingEnabled = !setting.getValue(
'screen.antiAliasing',
true
);
});
// 缩略图:背景
this.drawBg(floorId, options);
// 缩略图:事件

View File

@ -354,8 +354,9 @@ main.prototype.loadAsync = async function (mode, callback) {
main.dom.mainTips.style.display = 'none';
} else {
await new Promise(res => {
const all = main.pluginUseCompress ? '' : '/all/';
main.loadScript(
`/all/__all_floors__.js?v=${
`/${all}__all_floors__.js?v=${
main.version
}&id=${main.floorIds.join(',')}`
).then(

View File

@ -1,85 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>人类:开天辟地 缩略图集</title>
<style>
html {
width: 100%;
height: 100%;
}
body {
background-color: black;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
}
#map-group {
display: flex;
width: 50%;
flex-direction: column;
align-items: center;
}
#map-group span {
font-size: 32px;
}
a {
color: aqua;
text-decoration: none;
transition: color 0.2s linear;
width: 100%;
}
a:hover {
color: aquamarine;
}
img {
width: 90%;
border: 1px solid #ddd4;
border-style: dashed;
cursor: pointer;
}
.map-one {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
}
</style>
</head>
<body>
<div id="map-group">
<span>人类:开天辟地 缩略图集</span>
</div>
<script>
const list = ['草原', '洞穴', '勇气之路', '智慧小径', '冰封雪原', '冰封高原'];
let html = '';
list.forEach(v => {
html += `
<div class="map-one">
<a href="${v}.png" class="map-a" target="_blank">${v}</a>
<img src="${v}.png" class="map-img" onclick="window.open('${v}.png')"></img>
</div>
`
});
const div = document.getElementById('map-group');
div.innerHTML += html;
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

View File

@ -1,8 +1,8 @@
main.floors.empty=
{
"floorId": "empty",
"title": "空地图",
"name": "空地图",
"title": "空地图(不能删)",
"name": "空地图(不能删)",
"width": 15,
"height": 15,
"canFlyTo": false,

View File

@ -187,8 +187,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
core.visitFloor(floorId);
}
}
if (!flags.debug && !main.replayChecking)
Mota.Plugin.require('completion_r').checkVisitedFloor();
},
flyTo: function (toId, callback) {
// 楼层传送器的使用从当前楼层飞往toId

View File

@ -991,7 +991,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
const e = this.col.list.find(v => {
return v.x === x + dx * 2 && v.y === y + dy * 2;
});
if (e) {
if (e && e.info.special.includes(16)) {
const loc = `${x + dx},${y + dy}`;
this.setMapDamage(damage, loc, dam, '夹击');
caledBetween.add(loc);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -13,130 +13,132 @@ import compressing from 'compressing';
const type = process.argv[2];
const map = false;
const resorce = type !== 'dev';
const resorce = false;
const compress = type === 'dist';
(async function () {
const timestamp = Date.now();
// 1. 去除未使用的文件
const data = (() => {
const data = fss.readFileSync('./public/project/data.js', 'utf-8');
const json = JSON.parse(
data
.split(/(\n|\r\n)/)
.slice(1)
.join('\n')
);
return json;
})() as { main: Record<string, string[]> };
const main = data.main;
// const data = (() => {
// const data = fss.readFileSync('./public/project/data.js', 'utf-8');
// const json = JSON.parse(
// data
// .split(/(\n|\r\n)/)
// .slice(1)
// .join('\n')
// );
// return json;
// })() as { main: Record<string, string[]> };
// const main = data.main;
// try {
// const data = [
// ['./dist/project/floors', '.js', 'floorIds'],
// ['./dist/project/bgms', '', 'bgms'],
// ['./dist/project/sounds', '', 'sounds'],
// ['./dist/project/images', '', 'images'],
// ['./dist/project/animates', '.animate', 'animates'],
// ['./dist/project/tilesets', '', 'tilesets'],
// ['./dist/project/fonts', '.ttf', 'fonts']
// ];
// await Promise.all(
// data.map(async v => {
// const all = await fs.readdir(`${v[0]}`);
// const data = main[v[2]].map(vv => vv + v[1]);
// all.forEach(async vv => {
// if (!data.includes(vv)) {
// await fs.rm(`${v[0]}/${vv}`);
// }
// });
// })
// );
// if (!map) await fs.remove('./dist/maps/');
// // 在线查看什么都看不到,这编辑器难道还需要留着吗?
// await fs.remove('./dist/_server');
// await fs.remove('./dist/editor.html');
// await fs.remove('./dist/server.cjs');
// await fs.remove('./dist/project/materials/airwall.png');
// await fs.remove('./dist/project/materials/ground.png');
// await fs.remove('./dist/project/materials/icons_old.png');
// } catch (e) {
// console.log('去除未使用的文件失败!');
// console.log(e);
// }
// // 2. 压缩字体
// try {
// // 获取要压缩的文字列表libs & projects下的所有js文件
// let texts = ``;
// const exclude = `\n \t`;
// const libs = await fs.readdir('./public/libs');
// const project = await fs.readdir('./public/project');
// const floors = await fs.readdir('./public/project/floors');
// const assets = await fs.readdir('./dist/assets/');
// const all = [
// ...libs.map(v => `./public/libs/${v}`),
// ...project.map(v => `./public/project/${v}`),
// ...floors.map(v => `./public/project/floors/${v}`),
// ...assets.map(v => `./dist/assets/${v}`)
// ];
// for await (const dir of all) {
// const stat = await fs.stat(dir);
// if (!stat.isFile()) continue;
// if (dir.endsWith('.ttf')) continue;
// const file = await fs.readFile(dir, 'utf-8');
// for (let i = 0; i < file.length; i++) {
// const char = file[i];
// if (!texts.includes(char) && !exclude.includes(char))
// texts += char;
// }
// }
// // 获取所有字体(直接压缩字体会报错
// const fonts = main.fonts;
// await Promise.all([
// ...fonts.map(v =>
// (async () => {
// const fontmin = new Fontmin();
// fontmin
// .src<string>(`./public/project/fonts/${v}.ttf`)
// .dest('./dist/project/fonts')
// .use(
// Fontmin.glyph({
// text: texts
// })
// );
// await new Promise(res => {
// fontmin.run(err => {
// if (err) throw err;
// res('');
// });
// });
// })()
// )
// ]);
// await Promise.all([
// ...fonts.map(v => {
// return fs.rename(
// `./dist/project/fonts/${v}.ttf`,
// `./dist/project/fonts/${v}-${timestamp}.ttf`
// );
// })
// ]);
// } catch (e) {
// console.log('字体压缩失败');
// console.log(e);
// }
// 3. 压缩游戏进程
try {
const data = [
['./dist/project/floors', '.js', 'floorIds'],
['./dist/project/bgms', '', 'bgms'],
['./dist/project/sounds', '', 'sounds'],
['./dist/project/images', '', 'images'],
['./dist/project/animates', '.animate', 'animates'],
['./dist/project/tilesets', '', 'tilesets'],
['./dist/project/fonts', '.ttf', 'fonts']
];
await Promise.all(
data.map(async v => {
const all = await fs.readdir(`${v[0]}`);
const data = main[v[2]].map(vv => vv + v[1]);
all.forEach(async vv => {
if (!data.includes(vv)) {
await fs.rm(`${v[0]}/${vv}`);
}
});
})
);
if (!map) await fs.remove('./dist/maps/');
// 在线查看什么都看不到,这编辑器难道还需要留着吗?
await fs.remove('./dist/_server');
await fs.remove('./dist/editor.html');
await fs.remove('./dist/server.cjs');
await fs.remove('./dist/project/materials/airwall.png');
await fs.remove('./dist/project/materials/ground.png');
await fs.remove('./dist/project/materials/icons_old.png');
} catch (e) {
console.log('去除未使用的文件失败!');
console.log(e);
}
// 2. 压缩字体
try {
// 获取要压缩的文字列表libs & projects下的所有js文件
let texts = ``;
const exclude = `\n \t`;
const libs = await fs.readdir('./public/libs');
const project = await fs.readdir('./public/project');
const floors = await fs.readdir('./public/project/floors');
const assets = await fs.readdir('./dist/assets/');
const all = [
...libs.map(v => `./public/libs/${v}`),
...project.map(v => `./public/project/${v}`),
...floors.map(v => `./public/project/floors/${v}`),
...assets.map(v => `./dist/assets/${v}`)
];
for await (const dir of all) {
const stat = await fs.stat(dir);
if (!stat.isFile()) continue;
if (dir.endsWith('.ttf')) continue;
const file = await fs.readFile(dir, 'utf-8');
for (let i = 0; i < file.length; i++) {
const char = file[i];
if (!texts.includes(char) && !exclude.includes(char))
texts += char;
}
}
// 获取所有字体(直接压缩字体会报错
const fonts = main.fonts;
await Promise.all([
...fonts.map(v =>
(async () => {
const fontmin = new Fontmin();
fontmin
.src<string>(`./public/project/fonts/${v}.ttf`)
.dest('./dist/project/fonts')
.use(
Fontmin.glyph({
text: texts
})
);
await new Promise(res => {
fontmin.run(err => {
if (err) throw err;
res('');
});
});
})()
)
]);
await Promise.all([
...fonts.map(v => {
return fs.rename(
`./dist/project/fonts/${v}.ttf`,
`./dist/project/fonts/${v}-${timestamp}.ttf`
);
})
]);
} catch (e) {
console.log('字体压缩失败');
console.log(e);
}
// 3. 压缩js插件
try {
await fs.remove('./dist/project/plugin.min.js');
await fs.remove('./dist/project/processG.min.js');
const build = await rollup.rollup({
input: 'src/plugin/game/index.js',
input: 'src/game/index.ts',
plugins: [
typescript({
sourceMap: false
sourceMap: false,
declaration: true,
declarationDir: './dist/types/'
}),
rollupBabel({
// todo: 是否需要添加 polyfill?
@ -151,7 +153,7 @@ const compress = type === 'dist';
await build.write({
format: 'iife',
name: 'CorePlugin',
file: './dist/project/plugin.min.js'
file: './dist/project/processG.min.js'
});
await fs.remove('./dist/project/plugin/');
@ -181,17 +183,17 @@ const compress = type === 'dist';
}
// 5. 杂项
try {
await fs.copy('./LICENSE', './dist/LICENSE');
} catch (e) {
console.log('添加杂项失败');
console.log(e);
}
// try {
// await fs.copy('./LICENSE', './dist/LICENSE');
// } catch (e) {
// console.log('添加杂项失败');
// console.log(e);
// }
// 6. 资源分离
if (resorce) {
await splitResorce(type);
}
// if (resorce) {
// await splitResorce(type);
// }
// 7. 压缩
if (compress) {

View File

@ -0,0 +1,25 @@
import typescript from '@rollup/plugin-typescript';
import fs from 'fs-extra';
import * as rollup from 'rollup';
import resolve from '@rollup/plugin-node-resolve';
export async function buildDeclaration() {
const build = await rollup.rollup({
input: './src/core/index.ts',
plugins: [
typescript({
sourceMap: false,
declaration: true,
emitDeclarationOnly: true,
outDir: './dist/types/',
noEmit: false,
jsx: 'preserve'
}),
resolve()
]
});
build.write({
file: './dist/types/index.d.ts'
});
}
buildDeclaration();

View File

@ -594,7 +594,7 @@ async function ensureConfig() {
// 1. 启动vite服务
const vite = await createServer();
await vite.listen(5173);
console.log(`游戏地址http://localhost:5173/games/${config.name}/`);
console.log(`游戏地址http://localhost:5173`);
// 2. 启动样板http服务
await ensureConfig();

View File

@ -176,6 +176,7 @@ export class SoundController extends ResourceController<
*/
play(sound: SoundIds, end?: () => void): number {
const se = this.get(sound);
if (!se) return -1;
const index = se.playSE();
if (!has(index)) return -1;
this.seIndex[index] = se;

View File

@ -104,3 +104,6 @@ Mota.register('module', 'UITools', {
statusBar: statusBarTools,
toolbox: toolboxTools
});
main.renderLoaded = true;
Mota.require('var', 'hook').emit('renderLoaded');

View File

@ -20,8 +20,6 @@
import * as shadow from '@/plugin/shadow/shadow';
import * as gameShadow from '@/plugin/shadow/gameShadow';
import * as fly from '@/plugin/ui/fly';
import * as chase from '@/plugin/chase/chase';
import * as completion from '@/plugin/completion';
import * as pop from '@/plugin/pop';
import * as frag from '@/plugin/fx/frag';
import * as use from '@/plugin/use';
@ -32,8 +30,6 @@ import * as shader from './fx/shader';
Mota.Plugin.register('shadow_r', shadow, shadow.init);
Mota.Plugin.register('gameShadow_r', gameShadow, gameShadow.init);
Mota.Plugin.register('fly_r', fly);
Mota.Plugin.register('chase_r', chase);
Mota.Plugin.register('completion_r', completion);
Mota.Plugin.register('pop_r', pop, pop.init);
Mota.Plugin.register('frag_r', frag, frag.init);
Mota.Plugin.register('use_r', use);

View File

@ -131,8 +131,8 @@ class GameListener extends EventEmitter<ListenerEvent> {
const getBlockLoc = (px: number, py: number, size: number) => {
return [
Math.floor(((px * 32) / size - core.bigmap.offsetX) / 32),
Math.floor(((py * 32) / size - core.bigmap.offsetY) / 32)
Math.floor(((px * 32) / size + core.bigmap.offsetX) / 32),
Math.floor(((py * 32) / size + core.bigmap.offsetY) / 32)
];
};

View File

@ -144,12 +144,10 @@ interface PluginInterface {
// utils: typeof import('../plugin/utils');
// status: typeof import('../plugin/ui/statusBar');
fly_r: typeof import('../plugin/ui/fly');
chase_r: typeof import('../plugin/chase/chase');
// webglUtils: typeof import('../plugin/webgl/utils');
shadow_r: typeof import('../plugin/shadow/shadow');
gameShadow_r: typeof import('../plugin/shadow/gameShadow');
// achievement: typeof import('../plugin/ui/achievement');
completion_r: typeof import('../plugin/completion');
// path: typeof import('../plugin/fx/path');
gameCanvas_r: typeof import('../plugin/fx/gameCanvas');
// noise: typeof import('../plugin/fx/noise');

View File

@ -1,256 +0,0 @@
import { Animation, circle, hyper, sleep, TimingFn } from 'mutate-animate';
import { completeAchievement } from '../ui/achievement';
import { has } from '../utils';
import { ChaseCameraData, ChasePath, getChaseDataByIndex } from './data';
// todo: 优化可以继承自EventEmitter
export function shake2(power: number, timing: TimingFn): TimingFn {
let r = 0;
return t => {
r += Math.PI / 2;
return Math.sin(r) * power * timing(t);
};
}
export class Chase {
/**
*
*/
ani: Animation = new Animation();
/**
*
*/
path: ChasePath;
/**
*
*/
showPath: boolean = false;
endFn?: (lose: boolean) => void;
/**
*
* @param index
* @param path 线
* @param fn
*/
constructor(
path: ChasePath,
fns: ((chase: Chase) => void)[],
camera: ChaseCameraData[],
showPath: boolean = false
) {
this.path = path;
flags.__lockViewport__ = true;
flags.onChase = true;
flags.chaseTime = {
[core.status.floorId]: Date.now()
};
this.ani
.absolute()
.time(0)
.move(core.bigmap.offsetX / 32, core.bigmap.offsetY / 32);
fns.forEach(v => v(this));
const added: FloorIds[] = [];
const ctx = core.createCanvas('chasePath', 0, 0, 0, 0, 35);
for (const [id, x, y, start, time, mode, path] of camera) {
if (!added.includes(id)) {
this.on(
id,
0,
() => {
flags.__lockViewport__ = false;
core.drawHero();
flags.__lockViewport__ = true;
this.ani
.time(0)
.move(
core.bigmap.offsetX / 32,
core.bigmap.offsetY / 32
);
},
true
);
added.push(id);
}
if (!has(path)) {
this.on(id, start, () => {
this.ani.time(time).mode(mode).move(x, y);
});
} else {
this.on(id, start, () => {
this.ani.time(time).mode(mode).moveAs(path);
});
}
}
this.ani.ticker.add(() => {
if (!flags.floorChanging) {
core.setViewport(this.ani.x * 32, this.ani.y * 32);
core.relocateCanvas(ctx, -this.ani.x * 32, -this.ani.y * 32);
}
});
if (showPath) {
for (const [id, p] of Object.entries(path) as [
FloorIds,
LocArr[]
][]) {
this.on(id, 0, () => {
const floor = core.status.maps[id];
core.resizeCanvas(ctx, floor.width * 32, floor.height * 32);
ctx.beginPath();
ctx.moveTo(p[0][0] * 32 + 16, p[1][1] * 32 + 24);
ctx.lineJoin = 'round';
ctx.lineWidth = 4;
ctx.strokeStyle = 'cyan';
ctx.globalAlpha = 0.3;
p.forEach((v, i, a) => {
if (i === 0) return;
const [x, y] = v;
ctx.lineTo(x * 32 + 16, y * 32 + 24);
});
ctx.stroke();
});
}
}
}
/**
*
* @param floorId id
* @param time
* @param fn
*/
on(
floorId: FloorIds,
time: number,
fn: (chase: Chase) => void,
first: boolean = false
) {
const func = () => {
if (!flags.chaseTime?.[floorId]) return;
if (
Date.now() - (flags.chaseTime?.[floorId] ?? Date.now()) >=
time
) {
fn(this);
this.ani.ticker.remove(func);
}
};
this.ani.ticker.add(func, first);
}
/**
*
* @param x
* @param y
* @param floorId id
* @param fn
* @param mode 0
*/
onHeroLoc(
floorId: FloorIds,
fn: (chase: Chase) => void,
x?: number | number[],
y?: number | number[],
mode: 0 | 1 = 0
) {
if (mode === 1) {
if (typeof x === 'number') x = [x];
if (typeof y === 'number') y = [y];
x!.forEach(v => {
(y as number[]).forEach(vv => {
this.onHeroLoc(floorId, fn, v, vv);
});
});
return;
}
const judge = () => {
if (core.status.floorId !== floorId) return false;
if (has(x)) {
if (typeof x === 'number') {
if (core.status.hero.loc.x !== x) return false;
} else {
if (!x.includes(core.status.hero.loc.x)) return false;
}
}
if (has(y)) {
if (typeof y === 'number') {
if (core.status.hero.loc.y !== y) return false;
} else {
if (!y.includes(core.status.hero.loc.y)) return false;
}
}
return true;
};
const func = () => {
if (judge()) {
fn(this);
try {
this.ani.ticker.remove(func);
} catch {}
}
};
this.ani.ticker.add(func);
}
/**
*
* @param show
*/
setPathShowStatus(show: boolean) {
this.showPath = show;
}
/**
*
* @param fn
*/
onEnd(fn: (lose: boolean) => void) {
this.endFn = fn;
}
/**
*
*/
end(lose: boolean = false) {
this.ani.ticker.destroy();
delete flags.onChase;
delete flags.chase;
delete flags.chaseTime;
delete flags.chaseHard;
delete flags.chaseIndex;
flags.__lockViewport__ = false;
core.deleteCanvas('chasePath');
if (this.endFn) this.endFn(lose);
}
}
export async function startChase(index: number) {
const data = getChaseDataByIndex(index);
flags.chaseIndex = index;
flags.onChase = true;
await sleep(20);
const chase = new Chase(
data.path,
data.fns,
data.camera,
flags.chaseHard === 0
);
flags.chase = chase;
const hard = flags.chaseHard;
// 成就
chase.onEnd(lose => {
if (hard === 1) {
if (index === 1 && !lose) {
completeAchievement('challenge', 0);
}
}
});
}

View File

@ -1,574 +0,0 @@
import { Animation, bezier, hyper, linear, shake, sleep } from 'mutate-animate';
import { Chase, shake2 } from './chase';
import { ChaseCameraData } from './data';
import { completeAchievement } from '../ui/achievement';
const ani = new Animation();
ani.register('rect', 0);
export const path1: Partial<Record<FloorIds, LocArr[]>> = {
MT16: [
[23, 23],
[0, 23]
],
MT15: [
[63, 4],
[61, 4],
[61, 5],
[58, 5],
[58, 8],
[54, 8],
[54, 11],
[51, 11],
[51, 8],
[45, 8],
[45, 4],
[47, 4],
[47, 6],
[51, 6],
[51, 5],
[52, 5],
[52, 3],
[50, 3],
[50, 5],
[48, 5],
[48, 3],
[35, 3],
[35, 5],
[31, 5],
[31, 7],
[34, 7],
[34, 9],
[31, 9],
[31, 11],
[12, 11],
[12, 8],
[1, 8],
[1, 7],
[0, 7]
],
MT14: [
[127, 7],
[126, 7],
[126, 8],
[124, 8],
[124, 7],
[115.2, 7],
[115.2, 9.2],
[110.2, 9.2],
[110.2, 11],
[109.8, 11],
[109.8, 8.8],
[111.8, 8.8],
[111.8, 7],
[104, 7],
[104, 3],
[100, 3],
[100, 4],
[98, 4],
[98, 3],
[96, 3],
[96, 6],
[95, 6],
[95, 7],
[88, 7],
[88, 6],
[85, 6],
[85, 8],
[83, 8],
[83, 9],
[81, 9],
[81, 11],
[72, 11],
[72, 5],
[68, 5],
[68, 8],
[67, 8],
[67, 10],
[65, 10],
[65, 11],
[62, 11],
[62, 9],
[60, 9],
[60, 11],
[57, 11],
[57, 9],
[54, 9]
]
};
export const camera1: ChaseCameraData[] = [
['MT16', 0, 10, 0, 1600, hyper('sin', 'in')],
['MT15', 45, 0, 0, 2324, hyper('sin', 'in')],
['MT15', 40, 0, 2324, 1992, hyper('sin', 'out')],
['MT15', 41, 0, 5312, 498, hyper('sin', 'in-out')],
['MT15', 37, 0, 5810, 1660, hyper('sin', 'in')],
['MT15', 29, 0, 7470, 830, hyper('sin', 'out')],
['MT15', 25, 0, 11454, 996, hyper('sin', 'in')],
['MT15', 12, 0, 12450, 996, linear()],
['MT15', 0, 0, 13446, 1470, hyper('sin', 'out')],
['MT14', 109, 0, 0, 1328, hyper('sin', 'in')],
['MT14', 104, 0, 1328, 332, hyper('sin', 'out')],
['MT14', 92, 0, 5478, 2822, hyper('sin', 'in')],
['MT14', 84, 0, 8300, 1992, linear()],
['MT14', 74, 0, 10292, 2988, linear()],
['MT14', 65, 0, 13280, 2988, linear()],
['MT14', 58, 0, 16268, 1992, linear()],
['MT14', 47, 0, 18260, 3320, linear()],
['MT14', 36, 0, 21580, 3320, linear()],
['MT14', 0, 0, 24900, 9960, linear()]
];
/**
*
*/
export function init1() {
return Mota.Plugin.require('chase_g').chaseInit1();
}
export function chaseShake(chase: Chase) {
chase.ani
.mode(shake2(2 / 32, bezier(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)), true)
.time(50000)
.shake(1, 0);
}
export async function wolfMove(chase: Chase) {
core.moveBlock(23, 17, Array(6).fill('down'), 80);
await sleep(550);
core.setBlock(508, 23, 23);
}
export function judgeFail1(chase: Chase) {
chase.ani.ticker.add(() => {
if (core.status.hero.loc.x > core.bigmap.offsetX / 32 + 17) {
chase.end(true);
ani.time(750).apply('rect', 0);
core.lose('逃跑失败');
}
});
}
export function drawBack(chase: Chase) {
chase.on('MT15', 0, () => {
ani.mode(hyper('sin', 'out')).time(1500).absolute().apply('rect', 64);
const ctx = core.createCanvas('chaseBack', 0, 0, 480, 480, 120);
ctx.fillStyle = '#000';
const fn = () => {
if (!ctx) ani.ticker.remove(fn);
core.clearMap(ctx);
ctx.fillRect(0, 0, 480, ani.value.rect);
ctx.fillRect(0, 480, 480, -ani.value.rect);
};
ani.ticker.add(fn);
});
}
export function para1(chase: Chase) {
chase.on('MT15', 830, () => {
for (let tx = 53; tx < 58; tx++) {
for (let ty = 3; ty < 8; ty++) {
core.setBlock(336, tx, ty);
}
}
core.drawAnimate('explosion3', 55, 5);
core.drawAnimate('stone', 55, 5);
});
chase.on('MT15', 1080, () => {
core.setBlock(336, 58, 9);
core.setBlock(336, 59, 9);
core.drawAnimate('explosion1', 58, 9);
core.drawAnimate('explosion1', 59, 9);
});
chase.on('MT15', 1190, () => {
core.setBlock(336, 53, 8);
core.setBlock(336, 52, 8);
core.drawAnimate('explosion1', 53, 8);
core.drawAnimate('explosion1', 52, 8);
});
chase.on('MT15', 1580, () => {
core.setBlock(336, 51, 7);
core.drawAnimate('explosion1', 51, 7);
});
chase.on('MT15', 1830, () => {
core.setBlock(336, 47, 7);
core.setBlock(336, 49, 9);
core.drawAnimate('explosion1', 49, 9);
core.drawAnimate('explosion1', 47, 7);
});
}
export function para2(chase: Chase) {
chase.onHeroLoc(
'MT15',
() => {
core.setBlock(336, 45, 9);
core.drawAnimate('explosion1', 45, 9);
},
45,
8
);
chase.onHeroLoc(
'MT15',
() => {
core.setBlock(336, 44, 6);
core.drawAnimate('explosion1', 44, 6);
},
45,
6
);
chase.onHeroLoc(
'MT15',
() => {
core.setBlock(336, 44, 4);
core.drawAnimate('explosion1', 44, 4);
core.drawAnimate('explosion1', 48, 6);
core.removeBlock(48, 6);
},
45,
4
);
chase.onHeroLoc(
'MT15',
() => {
core.setBlock(336, 41, 4);
core.setBlock(336, 32, 6);
core.drawAnimate('explosion1', 41, 4);
core.drawAnimate('explosion1', 32, 6);
},
41,
3
);
chase.onHeroLoc(
'MT15',
() => {
core.drawAnimate('explosion3', 37, 7);
core.vibrate('vertical', 1000, 25, 10);
for (let tx = 36; tx < 42; tx++) {
for (let ty = 4; ty < 11; ty++) {
core.setBlock(336, tx, ty);
}
}
},
35,
3
);
chase.onHeroLoc(
'MT15',
() => {
core.vibrate('vertical', 10000, 25, 1);
core.removeBlock(34, 8);
core.removeBlock(33, 8);
core.drawAnimate('explosion1', 34, 8);
core.drawAnimate('explosion1', 33, 8);
},
31,
5
);
chase.onHeroLoc(
'MT15',
() => {
core.setBlock(336, 32, 9);
core.drawAnimate('explosion1', 32, 9);
},
33,
7
);
chase.onHeroLoc(
'MT15',
() => {
core.removeBlock(32, 9);
core.drawAnimate('explosion1', 32, 9);
},
[33, 34, 34],
9
);
for (let x = 19; x < 31; x++) {
const xx = x;
chase.onHeroLoc(
'MT15',
() => {
core.setBlock(336, xx + 1, 11);
core.drawAnimate('explosion1', xx + 1, 11);
},
xx,
11
);
}
}
export function para3(chase: Chase) {
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 126, 6);
core.setBlock(336, 124, 6);
core.setBlock(336, 124, 9);
core.setBlock(336, 126, 9);
core.drawAnimate('explosion1', 126, 6);
core.drawAnimate('explosion1', 124, 6);
core.drawAnimate('explosion1', 124, 9);
core.drawAnimate('explosion1', 126, 9);
},
126,
7
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(508, 127, 7);
core.jumpBlock(127, 7, 112, 7, 500, true);
setTimeout(() => {
core.setBlock(509, 112, 7);
}, 520);
core.drawHeroAnimate('amazed');
core.setBlock(336, 121, 6);
core.setBlock(336, 122, 6);
core.setBlock(336, 120, 8);
core.setBlock(336, 121, 8);
core.setBlock(336, 122, 8);
core.drawAnimate('explosion1', 121, 6);
core.drawAnimate('explosion1', 122, 6);
core.drawAnimate('explosion1', 120, 8);
core.drawAnimate('explosion1', 121, 8);
core.drawAnimate('explosion1', 122, 8);
},
123,
7
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 109, 11);
core.removeBlock(112, 8);
core.drawAnimate('explosion1', 109, 11);
core.drawAnimate('explosion1', 112, 8);
core.insertAction([
{ type: 'moveHero', time: 400, steps: ['backward:1'] }
]);
chase.onHeroLoc(
'MT14',
() => {
core.jumpBlock(112, 7, 110, 4, 500, true);
core.drawHeroAnimate('amazed');
setTimeout(() => {
core.setBlock(506, 110, 4);
}, 540);
},
112,
8
);
},
110,
10
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 117, 6);
core.setBlock(336, 116, 6);
core.setBlock(336, 115, 6);
core.setBlock(336, 114, 6);
core.setBlock(336, 117, 8);
core.setBlock(336, 116, 8);
core.drawAnimate('explosion1', 117, 6);
core.drawAnimate('explosion1', 116, 6);
core.drawAnimate('explosion1', 115, 6);
core.drawAnimate('explosion1', 114, 6);
core.drawAnimate('explosion1', 116, 8);
core.drawAnimate('explosion1', 117, 8);
},
118,
7
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 112, 8);
core.setBlock(336, 113, 7);
core.drawAnimate('explosion1', 112, 8);
core.drawAnimate('explosion1', 113, 7);
},
112,
7
);
chase.onHeroLoc(
'MT14',
() => {
for (let tx = 111; tx <= 115; tx++) {
core.setBlock(336, tx, 10);
core.drawAnimate('explosion1', tx, 10);
}
core.setBlock(336, 112, 8);
core.drawAnimate('explosion1', 112, 8);
},
115,
7
);
chase.onHeroLoc(
'MT14',
() => {
core.jumpBlock(97, 4, 120, -3, 2000);
for (let tx = 109; tx <= 120; tx++) {
for (let ty = 3; ty <= 11; ty++) {
if (ty == 7) continue;
core.setBlock(336, tx, ty);
}
}
core.drawAnimate('explosion2', 119, 7);
core.removeBlock(105, 7);
core.drawAnimate('explosion1', 105, 7);
},
110,
7
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 95, 3);
core.setBlock(336, 93, 6);
core.drawAnimate('explosion1', 95, 3);
core.drawAnimate('explosion1', 93, 6);
},
97,
3
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 87, 4);
core.setBlock(336, 88, 5);
core.drawAnimate('explosion1', 87, 4);
core.drawAnimate('explosion1', 88, 5);
},
88,
6
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 84, 6);
core.setBlock(336, 85, 5);
core.setBlock(336, 86, 8);
core.drawAnimate('explosion1', 84, 6);
core.drawAnimate('explosion1', 85, 5);
core.drawAnimate('explosion1', 86, 8);
},
86,
6
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 81, 8);
core.setBlock(336, 82, 11);
core.drawAnimate('explosion1', 81, 8);
core.drawAnimate('explosion1', 82, 11);
},
81,
9
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 73, 8);
core.setBlock(336, 72, 4);
core.drawAnimate('explosion1', 73, 8);
core.drawAnimate('explosion1', 72, 4);
},
72,
11
);
chase.onHeroLoc(
'MT14',
() => {
for (let tx = 74; tx < 86; tx++) {
for (let ty = 3; ty < 12; ty++) {
core.setBlock(336, tx, ty);
}
}
core.drawAnimate('explosion2', 79, 7);
core.vibrate('vertical', 4000, 25, 15);
},
71,
7
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 68, 4);
core.setBlock(336, 67, 6);
core.drawAnimate('explosion1', 68, 4);
core.drawAnimate('explosion1', 67, 6);
},
68,
5
);
chase.onHeroLoc(
'MT14',
() => {
for (let tx = 65; tx <= 72; tx++) {
for (let ty = 3; ty <= 9; ty++) {
core.setBlock(336, tx, ty);
}
}
core.setBlock(336, 72, 10);
core.setBlock(336, 72, 11);
core.drawAnimate('explosion3', 69, 5);
},
67,
10
);
chase.onHeroLoc(
'MT14',
() => {
core.setBlock(336, 63, 9);
core.setBlock(336, 60, 8);
core.setBlock(336, 56, 11);
core.drawAnimate('explosion1', 63, 9);
core.drawAnimate('explosion1', 60, 8);
core.drawAnimate('explosion1', 56, 11);
},
64,
11
);
chase.onHeroLoc(
'MT14',
() => {
for (let tx = 58; tx <= 64; tx++) {
for (let ty = 3; ty <= 11; ty++) {
core.setBlock(336, tx, ty);
}
}
core.drawAnimate('explosion2', 61, 7);
},
57,
9
);
for (let x = 21; x < 49; x++) {
chase.onHeroLoc(
'MT14',
() => {
for (let ty = 3; ty <= 11; ty++) {
core.setBlock(336, x + 4, ty);
core.drawAnimate('explosion1', x + 4, ty);
}
},
x
);
}
chase.onHeroLoc(
'MT14',
async () => {
flags.finishChase1 = true;
Mota.Plugin.require('replay_g').clip('choices:0');
core.showStatusBar();
ani.time(750).apply('rect', 0);
chase.end();
await sleep(750);
ani.ticker.destroy();
core.deleteCanvas('chaseBack');
},
21
);
}

View File

@ -1,52 +0,0 @@
import { PathFn, TimingFn } from 'mutate-animate';
import { Chase } from './chase';
import {
camera1,
para1,
para2,
para3,
path1,
chaseShake,
wolfMove,
init1,
judgeFail1,
drawBack
} from './chase1';
export type ChaseCameraData = [
floorId: FloorIds, // 楼层
x: number, // 目标横坐标
y: number, // 目标纵坐标
start: number, // 开始时间
time: number, // 持续时间
mode: TimingFn, // 渐变函数
path?: PathFn // 路径函数
];
export type ChasePath = Partial<Record<FloorIds, LocArr[]>>;
interface ChaseData {
camera: ChaseCameraData[];
fns: ((chase: Chase) => void)[];
path: ChasePath;
}
export function getChaseDataByIndex(index: number): ChaseData {
if (index === 1) {
init1();
return {
camera: camera1,
fns: [
para1,
para2,
para3,
chaseShake,
wolfMove,
drawBack,
judgeFail1
],
path: path1
};
}
throw new ReferenceError(`Deliver wrong chase index.`);
}

View File

@ -1,115 +0,0 @@
import {
AchievementType,
completeAchievement,
hasCompletedAchievement
} from './ui/achievement';
import { changeLocalStorage } from './utils';
import list from '../data/achievement.json';
export const floors: Record<number, FloorIds[]> = {
1: ['MT0', 'tower7']
};
const achis: Record<number, Record<AchievementType, number[]>> = {
1: {
normal: [0, 1],
challenge: [0],
explore: [1]
}
};
export const achiDict: Record<number, number> = {
1: 0
};
const loading = Mota.require('var', 'loading');
loading.once('coreInit', () => {
Object.values(floors).forEach((v, i) => {
const from = core.floorIds.indexOf(v[0]);
const to = core.floorIds.indexOf(v[1]);
const all = core.floorIds.slice(from, to + 1);
floors[i + 1] = all;
});
});
/**
*
*/
export function checkVisitedFloor() {
changeLocalStorage<Partial<Record<FloorIds, boolean>>>(
'visitedFloor',
data => {
let needUpdate = false;
core.floorIds.forEach(v => {
if (core.hasVisitedFloor(v)) {
data[v] = true;
needUpdate = true;
}
});
if (needUpdate) {
checkCompletionAchievement();
}
return data;
},
{}
);
}
/**
*
* @param num
*/
export function getChapterCompletion(num: number) {
if (!achis[num]) return 0;
let res = 0;
const all = floors[num];
const achiNum = Object.values(achis[num]).reduce(
(pre, cur) => pre + cur.length,
0
);
// 计算到达过的楼层
let visitedFloor = 0;
const visited = core.getLocalStorage<Partial<Record<FloorIds, boolean>>>(
'visitedFloor',
{}
);
all.forEach(v => {
if (visited[v]) visitedFloor++;
});
const floorRatio = all.length / (all.length + achiNum);
const floorPoint = (floorRatio * visitedFloor) / all.length;
let completedPoint = 0;
let totalPoint = 0;
// 计算成就,占比按成就点走
for (const [type, achi] of Object.entries(achis[num]) as [
AchievementType,
number[]
][]) {
achi.forEach(v => {
totalPoint += list[type][v].point;
if (hasCompletedAchievement(type, v)) {
completedPoint += list[type][v].point;
}
});
}
const achiPoint = (completedPoint / totalPoint) * (1 - floorRatio);
res = floorPoint + achiPoint;
return Math.floor(res * 100);
}
/**
*
*/
export function checkCompletionAchievement() {
[1].forEach(v => {
if (getChapterCompletion(v) >= 100) {
completeAchievement('explore', achiDict[v]);
}
});
}

View File

@ -1,63 +0,0 @@
export {};
const potionItems: AllIdsOf<'items'>[] = [
'redPotion',
'bluePotion',
'yellowPotion',
'greenPotion',
'I482',
'I484',
'I487',
'I491'
];
const hook = Mota.require('var', 'hook');
hook.on('afterGetItem', (itemId, x, y, isGentleClick) => {
// 获得一个道具后触发的事件
// itemId获得的道具IDx和y是该道具所在的坐标
// isGentleClick是否是轻按触发的
if (potionItems.includes(itemId)) core.playSound('回血');
else core.playSound('获得道具');
const todo: any[] = [];
// 检查该点的获得道具后事件。
if (core.status.floorId == null) return;
const event = core.floors[core.status.floorId].afterGetItem[`${x},${y}`];
if (
event &&
(event instanceof Array ||
!isGentleClick ||
!event.disableOnGentleClick)
) {
core.unshift(todo, event as any[]);
}
if (core.hasFlag('spring')) {
if (!core.hasFlag('springCount')) core.setFlag('springCount', 0);
if (potionItems.includes(itemId)) {
core.addFlag('springCount', 1);
}
if (core.getFlag<number>('springCount', 0) === 50) {
core.setFlag('springCount', 0);
core.status.hero.hpmax += core.getNakedStatus('hpmax') * 0.1;
}
core.updateStatusBar();
}
if (todo.length > 0) core.insertAction(todo, x, y);
});
hook.on('afterOpenDoor', (doorId, x, y) => {
// 开一个门后触发的事件
const todo: any[] = [];
// 检查该点的获得开门后事件。
if (core.status.floorId == null) return;
const event = core.floors[core.status.floorId].afterOpenDoor[`${x},${y}`];
if (event) core.unshift(todo, event as any[]);
if (todo.length > 0) core.insertAction(todo, x, y);
if (core.status.event.id == null) core.continueAutomaticRoute();
else core.clearContinueAutomaticRoute();
});

View File

@ -1,256 +0,0 @@
import { slide } from './utils';
const list = ['tower6'];
/**
*
* @param offset
*/
function setLoopMap(offset: number, floorId: FloorIds) {
const floor = core.status.maps[floorId];
if (offset < 9) {
moveMap(floor.width - 17, floorId);
}
if (offset > floor.width - 9) {
moveMap(17 - floor.width, floorId);
}
}
/**
*
*/
function autoSetLoopMap(floorId: FloorIds) {
setLoopMap(core.status.hero.loc.x, floorId);
}
export function checkLoopMap() {
if (isLoopMap(core.status.floorId)) {
autoSetLoopMap(core.status.floorId);
}
}
/**
*
*/
function moveMap(delta: number, floorId: FloorIds) {
core.extractBlocks(floorId);
const floor = core.status.maps[floorId];
core.setHeroLoc('x', core.status.hero.loc.x + delta);
flags[`loop_${floorId}`] += delta;
flags[`loop_${floorId}`] %= floor.width;
const origin = floor.blocks.slice();
for (let i = 0; i < origin.length; i++) {
core.removeBlockByIndex(0, floorId);
core.removeGlobalAnimate(origin[i].x, origin[i].y);
}
origin.forEach(v => {
let to = v.x + delta;
if (to >= floor.width) to -= floor.width;
if (to < 0) to += floor.width;
core.setBlock(v.id, to, v.y, floorId, true);
core.setMapBlockDisabled(floorId, to, v.y, false);
});
core.drawMap();
core.drawHero();
}
function isLoopMap(floorId: FloorIds) {
return list.includes(floorId);
}
export function init() {
events.prototype._sys_changeFloor = function (
data: any,
callback: () => void
) {
data = data.event.data;
let heroLoc: Partial<DiredLoc> = {};
if (isLoopMap(data.floorId)) {
const floor = core.status.maps[data.floorId as FloorIds] as Floor;
flags[`loop_${data.floorId}`] ??= 0;
let tx = data.loc[0] + flags[`loop_${data.floorId}`];
tx %= floor.width;
if (tx < 0) tx += floor.width;
heroLoc = {
x: tx,
y: data.loc[1]
};
} else if (data.loc) heroLoc = { x: data.loc[0], y: data.loc[1] };
if (data.direction) heroLoc.direction = data.direction;
// @ts-ignore
if (core.status.event.id != 'action') core.status.event.id = null;
core.changeFloor(
data.floorId,
data.stair,
heroLoc,
data.time,
function () {
core.replay();
if (callback) callback();
}
);
};
events.prototype.trigger = function (
x: number,
y: number,
callback: () => void
) {
var _executeCallback = function () {
// 因为trigger之后还有可能触发其他同步脚本比如阻激夹域检测
// 所以这里强制callback被异步触发
if (callback) {
setTimeout(callback, 1); // +1是为了录像检测系统
}
return;
};
if (core.status.gameOver) return _executeCallback();
if (core.status.event.id == 'action') {
core.insertAction(
{
type: 'function',
function:
'function () { core.events._trigger_inAction(' +
x +
',' +
y +
'); }',
async: true
},
void 0,
void 0,
void 0,
true
);
return _executeCallback();
}
if (core.status.event.id) return _executeCallback();
let block = core.getBlock(x, y);
const id = core.status.floorId;
const loop = isLoopMap(id);
if (loop && flags[`loop_${id}`] !== 0) {
if (block && block.event.trigger === 'changeFloor') {
delete block.event.trigger;
// @ts-ignore
core.maps._addInfo(block);
} else {
const floor = core.status.maps[id];
let tx = x - flags[`loop_${id}`];
tx %= floor.width;
if (tx < 0) tx += floor.width;
const c = core.floors[id].changeFloor[`${tx},${y}`];
if (c) {
const b: DeepPartial<Block> = { event: {}, x: tx, y };
b.event!.data = c;
b.event!.trigger = 'changeFloor';
block = b as Block;
}
}
}
if (block == null) return _executeCallback();
// 执行该点的脚本
if (block.event.script) {
core.clearRouteFolding();
try {
eval(block.event.script);
} catch (ee) {
console.error(ee);
}
}
// 碰触事件
if (block.event.event) {
core.clearRouteFolding();
core.insertAction(block.event.event, block.x, block.y);
// 不再执行该点的系统事件
return _executeCallback();
}
if (block.event.trigger && block.event.trigger !== 'null') {
var noPass = block.event.noPass,
trigger = block.event.trigger;
if (noPass) core.clearAutomaticRouteNode(x, y);
// 转换楼层能否穿透
if (
trigger == 'changeFloor' &&
!noPass &&
this._trigger_ignoreChangeFloor(block) &&
!loop
)
return _executeCallback();
// @ts-ignore
core.status.automaticRoute.moveDirectly = false;
this.doSystemEvent(trigger, block);
}
return _executeCallback();
};
maps.prototype._getBgFgMapArray = function (
name: string,
floorId: FloorIds,
noCache: boolean
) {
floorId = floorId || core.status.floorId;
if (!floorId) return [];
var width = core.floors[floorId].width;
var height = core.floors[floorId].height;
// @ts-ignore
if (!noCache && core.status[name + 'maps'][floorId])
// @ts-ignore
return core.status[name + 'maps'][floorId];
var arr: number[][] =
main.mode == 'editor' &&
// @ts-ignore
!(window.editor && editor.uievent && editor.uievent.isOpen)
? // @ts-ignore
core.cloneArray(editor[name + 'map'])
: null;
if (arr == null)
// @ts-ignore
arr = core.cloneArray(core.floors[floorId][name + 'map'] || []);
if (isLoopMap(floorId) && window.flags) {
flags[`loop_${floorId}`] ??= 0;
arr.forEach(v => {
slide(v, flags[`loop_${floorId}`] % width);
});
}
for (var y = 0; y < height; ++y) {
if (arr[y] == null) arr[y] = Array(width).fill(0);
}
// @ts-ignore
(core.getFlag('__' + name + 'v__', {})[floorId] || []).forEach(
// @ts-ignore
function (one) {
arr[one[1]][one[0]] = one[2] || 0;
}
);
// @ts-ignore
(core.getFlag('__' + name + 'd__', {})[floorId] || []).forEach(
// @ts-ignore
function (one) {
arr[one[1]][one[0]] = 0;
}
);
if (main.mode == 'editor') {
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
// @ts-ignore
arr[y][x] = arr[y][x].idnum || arr[y][x] || 0;
}
}
}
// @ts-ignore
if (core.status[name + 'maps'])
// @ts-ignore
core.status[name + 'maps'][floorId] = arr;
return arr;
};
}

View File

@ -1,179 +0,0 @@
// @ts-nocheck
// 所有的主动技能效果
var ignoreInJump = {
event: ['X20007', 'X20001', 'X20006', 'X20014', 'X20010', 'X20007'],
bg: [
'X20037',
'X20038',
'X20039',
'X20045',
'X20047',
'X20053',
'X20054',
'X20055',
'X20067',
'X20068',
'X20075',
'X20076'
]
};
export const jumpIgnoreFloor: FloorIds[] = [
'MT31',
'snowTown',
'MT36',
'MT37',
'MT38',
'MT39',
'MT40',
'MT42',
'MT43',
'MT44',
'MT45',
'MT46',
'MT47',
'MT48',
'MT49',
'MT50'
];
// 跳跃
export function jumpSkill() {
if (core.status.floorId.startsWith('tower'))
return core.drawTip('当无法使用该技能');
if (jumpIgnoreFloor.includes(core.status.floorId) || flags.onChase) {
return core.drawTip('当前楼层无法使用该技能');
}
if (!flags.skill2) return;
if (!flags['jump_' + core.status.floorId])
flags['jump_' + core.status.floorId] = 0;
if (core.status.floorId == 'MT14') {
const loc = core.status.hero.loc;
if (loc.x === 77 && loc.y === 5) {
flags.MT14Jump = true;
}
if (flags.jump_MT14 === 2 && !flags.MT14Jump) {
return core.drawTip('该地图还有一个必跳的地方,你还没有跳');
}
}
if (flags['jump_' + core.status.floorId] >= 3)
return core.drawTip('当前地图使用次数已用完');
var direction = core.status.hero.loc.direction;
var loc = core.status.hero.loc;
var checkLoc = {};
switch (direction) {
case 'up':
checkLoc.x = loc.x;
checkLoc.y = loc.y - 1;
break;
case 'right':
checkLoc.x = loc.x + 1;
checkLoc.y = loc.y;
break;
case 'down':
checkLoc.x = loc.x;
checkLoc.y = loc.y + 1;
break;
case 'left':
checkLoc.x = loc.x - 1;
checkLoc.y = loc.y;
break;
}
// 前方是否可通行 或 是怪物
var cls = core.getBlockCls(checkLoc.x, checkLoc.y);
var noPass = core.noPass(checkLoc.x, checkLoc.y);
var id = core.getBlockId(checkLoc.x, checkLoc.y) || '';
var bgId =
core.getBlockByNumber(core.getBgNumber(checkLoc.x, checkLoc.y)).event
.id || '';
// 可以通行
if (
!noPass ||
cls == 'items' ||
(id.startsWith('X') && !ignoreInJump.event.includes(id)) ||
(bgId.startsWith('X') && !ignoreInJump.bg.includes(bgId))
)
return core.drawTip('当前无法使用技能');
// 不是怪物且不可以通行
if (noPass && !(cls == 'enemys' || cls == 'enemy48')) {
var toLoc = checkNoPass(direction, checkLoc.x, checkLoc.y, true);
if (!toLoc) return;
core.autosave();
if (flags.chapter <= 1) core.status.hero.hp -= 200 * flags.hard;
core.updateStatusBar();
flags['jump_' + core.status.floorId]++;
if (core.status.hero.hp <= 0) {
core.status.hero.hp = 0;
core.updateStatusBar();
core.events.lose('你跳死了');
}
core.playSound('015-Jump01.ogg');
core.insertAction([
{ type: 'jumpHero', loc: [toLoc.x, toLoc.y], time: 500 }
]);
}
// 是怪物
if (cls == 'enemys' || cls == 'enemy48') {
var firstNoPass = checkNoPass(direction, checkLoc.x, checkLoc.y, false);
if (!firstNoPass) return;
core.autosave();
if (flags.chapter <= 1) core.status.hero.hp -= 200 * flags.hard;
core.updateStatusBar();
flags['jump_' + core.status.floorId]++;
if (core.status.hero.hp <= 0) {
core.status.hero.hp = 0;
core.updateStatusBar();
core.events.lose('你跳死了');
}
core.playSound('015-Jump01.ogg');
core.insertAction([
{
type: 'jump',
from: [checkLoc.x, checkLoc.y],
to: [firstNoPass.x, firstNoPass.y],
time: 500,
keep: true
}
]);
}
// 检查一条线上的不可通过
function checkNoPass(direction, x, y, startNo) {
if (!startNo) startNo = false;
switch (direction) {
case 'up':
y--;
break;
case 'right':
x++;
break;
case 'down':
y++;
break;
case 'left':
x--;
break;
}
if (
x > core.status.thisMap.width - 1 ||
y > core.status.thisMap.height - 1 ||
x < 0 ||
y < 0
)
return core.drawTip('当前无法使用技能');
var id = core.getBlockId(x, y) || '';
if (core.getBgNumber(x, y))
var bgId =
core.getBlockByNumber(core.getBgNumber(x, y)).event.id || '';
else var bgId = '';
if (
core.noPass(x, y) ||
core.getBlockCls(x, y) == 'items' ||
(id.startsWith('X') && !ignoreInJump.event.includes(id)) ||
(bgId.startsWith('X') && !ignoreInJump.bg.includes(bgId)) ||
core.getBlockCls(x, y) == 'animates'
)
return checkNoPass(direction, x, y, true);
if (!startNo) return checkNoPass(direction, x, y, false);
return { x: x, y: y };
}
}

View File

@ -1,304 +0,0 @@
let levels: number[] = [];
/**
* @type {Record<Chapter, Skill[]>}
*/
export const skills: Record<Chapter, Skill[]> = {
chapter1: [
{
index: 0,
title: '力量',
desc: ['力量就是根本可以通过智慧增加力量每级增加2点攻击。'],
consume: '10 * level + 10',
front: [],
loc: [1, 2],
max: 10,
effect: ['攻击 + ${level * 2}']
},
{
index: 1,
title: '致命一击',
desc: ['爆发出全部力量攻击敌人每级增加5点额外攻击。'],
consume: '30 * level + 30',
front: [[0, 5]],
loc: [2, 1],
max: 10,
effect: ['额外攻击 + ${level * 5}']
},
{
index: 2,
title: '断灭之刃',
desc: [
'<span style="color: gold">主动技能快捷键1</span>',
'开启后会在战斗时会额外增加一定量的攻击,但同时减少一定量的防御。'
],
consume: '200 * level + 400',
front: [[1, 5]],
loc: [4, 1],
max: 5,
effect: ['增加${level * 10}%攻击,减少${level * 10}%防御']
},
{
index: 3,
title: '坚韧',
desc: ['由智慧转化出坚韧每级增加2点防御'],
consume: '10 * level + 10',
front: [],
loc: [1, 4],
max: 10,
effect: ['防御 + ${level * 2}']
},
{
index: 4,
title: '回春',
desc: ['让智慧化为治愈之泉水每级增加1点生命回复'],
consume: '20 * level + 20',
front: [[3, 5]],
loc: [2, 5],
max: 25,
effect: ['生命回复 + ${level}']
},
{
index: 5,
title: '治愈之泉',
desc: [
'让生命变得更多一些吧每吃50瓶血瓶就增加当前生命回复10%的生命回复'
],
consume: '1500',
front: [[4, 25]],
loc: [4, 5],
max: 1,
effect: ['50瓶血10%生命回复']
},
{
index: 6,
title: '坚固之盾',
desc: ['让护甲更加坚硬一些吧每级增加10点防御'],
consume: '50 + level * 50',
front: [[3, 5]],
loc: [2, 3],
max: 10,
effect: ['防御 + ${level * 10}']
},
{
index: 7,
title: '无上之盾',
desc: [
'<span style="color: #dd4">第一章终极技能</span>,战斗时智慧的 1/10 会充当等量护盾。'
],
consume: '2500',
front: [
[6, 10],
[5, 1],
[2, 2]
],
loc: [5, 3],
max: 1,
effect: ['战斗时智慧会充当护盾']
}
],
chapter2: [
{
index: 8,
title: '锋利',
desc: ['让剑变得更加锋利每级使攻击增加1%buff式增加'],
consume: 'level > 5 ? 50 * level ** 2 : 250 * level + 250',
front: [],
loc: [1, 2],
max: 15,
effect: ['攻击增加${level}%']
},
{
index: 9,
title: '坚硬',
desc: ['让盾牌变得更加坚固每级使防御增加1%buff式增加'],
consume: 'level > 5 ? 50 * level ** 2 : 250 * level + 250',
front: [],
loc: [1, 4],
max: 15,
effect: ['防御增加${level}%']
},
{
index: 10,
title: '铸剑为盾',
desc: [
'<span style="color: gold">主动技能快捷键3</span>',
'减少一定的攻击,增加一定的防御'
],
consume: '1000 * level ** 2 + 1000',
front: [[9, 5]],
loc: [2, 5],
max: 5,
effect: ['增加${level * 10}%的防御,减少${level * 10}%的攻击']
},
{
index: 11,
title: '学习',
desc: [
'<span style="color: gold">主动技能</span>可以消耗500智慧学习一个怪物的技能',
'持续5场战斗每学习一次消耗的智慧点增加250每次升级使持续的战斗次数增加3次。更多信息可在学习后在百科全书查看。'
],
consume: '2500 * 2 ** level + 5000',
front: [
[8, 10],
[12, 5]
],
loc: [4, 1],
max: 6,
effect: ['学习怪物技能,持续${level * 3 + 2}场战斗']
},
{
index: 12,
title: '聪慧',
desc: ['使主角变得更加聪明每级使绿宝石增加的智慧点上升5%'],
consume: 'level > 5 ? 100 * level ** 2 : 250 * level + 1250',
front: [
[8, 10],
[9, 10]
],
loc: [3, 3],
max: 20,
effect: ['增加${level * 5}%绿宝石效果']
},
{
index: 13,
title: '治愈',
desc: ['使主角能够更好地回复生命每级使血瓶的加血量增加2%'],
consume: 'level > 5 ? 100 * level ** 2 : 250 * level + 1250',
front: [[10, 3]],
loc: [4, 5],
max: 20,
effect: ['增加${level * 2}%的血瓶回血量']
},
{
index: 14,
title: '胜利之号',
desc: [
'<span style="color: #dd4">第二章终极技能</span>',
'每打一个怪物勇士在本楼层对怪物造成的伤害便增加1%'
],
consume: '15000',
front: [
[13, 10],
[12, 10],
[11, 3]
],
loc: [5, 3],
max: 1,
effect: ['每打一个怪勇士造成的伤害增加1%']
}
]
};
export function resetSkillLevel() {
levels = [];
}
export function getSkillFromIndex(index: number) {
for (const [, skill] of Object.entries(skills)) {
const s = skill.find(v => v.index === index);
if (s) return s;
}
}
/**
*
* @param {number} skill
*/
export function getSkillLevel(skill: number) {
return (levels[skill] ??= 0);
}
export function getSkillConsume(skill: number) {
return eval(
getSkillFromIndex(skill)?.consume.replace(
/level(:\d+)?/g,
(str, $1) => {
if ($1)
return `Mota.Plugin.require('skillTree_g').getSkillLevel(${$1})`;
else
return `Mota.Plugin.require('skillTree_g').getSkillLevel(${skill})`;
}
) ?? ''
);
}
export function openTree() {
if (main.replayChecking) return;
Mota.require('var', 'mainUi').open('skillTree');
}
/**
*
*/
export function canUpgrade(skill: number) {
const consume = getSkillConsume(skill);
if (consume > core.status.hero.mdef) return false;
const level = getSkillLevel(skill);
const s = getSkillFromIndex(skill);
if (level === s?.max) return false;
const front = s?.front ?? [];
for (const [skill, level] of front) {
if (getSkillLevel(skill) < level) return false;
}
return true;
}
/**
*
* @param {number} skill
*/
export function upgradeSkill(skill: number) {
if (!canUpgrade(skill)) return false;
switch (skill) {
case 0: // 力量 +2攻击
core.status.hero.atk += 2;
break;
case 1: // 致命一击 +5额外攻击
core.status.hero.mana += 5;
break;
case 2: // 断灭之刃
core.setFlag('bladeOn', true);
break;
case 3: // 坚韧 +2防御
core.status.hero.def += 2;
break;
case 4: // 回春 +1回复
core.status.hero.hpmax += 1;
break;
case 5: // 治愈之泉
core.setFlag('spring', true);
break;
case 6: // 坚固之盾 +10防御
core.status.hero.def += 10;
break;
case 7: // 无上之盾
core.setFlag('superSheild', true);
break;
case 8: // 锋利 +1%攻击
core.addBuff('atk', 0.01);
break;
case 9: // 锋利 +1%防御
core.addBuff('def', 0.01);
break;
case 10: // 铸剑为盾
core.setFlag('shieldOn', true);
break;
case 11: // 学习
core.setItem('I565', 1);
break;
}
const consume = getSkillConsume(skill);
core.status.hero.mdef -= consume;
levels[skill]++;
core.updateStatusBar();
return true;
}
export function saveSkillTree() {
return levels.slice();
}
export function loadSkillTree(data: number[]) {
levels = data ?? [];
}

View File

@ -1,53 +0,0 @@
// 负责勇士技能:学习
const values: Record<number, string[]> = {
1: ['crit'],
6: ['n'],
7: ['hungry'],
8: ['together'],
10: ['courage'],
11: ['charge']
};
const cannotStudy = [9, 12, 14, 15, 24];
export function canStudySkill(number: number) {
const s = (core.status.hero.special ??= { num: [], last: [] });
if (Mota.Plugin.require('skillTree_g').getSkillLevel(11) === 0)
return false;
if (s.num.length >= 1) return false;
if (s.num.includes(number)) return false;
if (cannotStudy.includes(number)) return false;
return true;
}
export function studySkill(enemy: any, number: number) {
core.status.hero.special ??= { num: [], last: [] };
const s = core.status.hero.special;
}
export function forgetStudiedSkill(num: number, i: number) {
const s = core.status.hero.special;
const index = i !== void 0 && i !== null ? i : s.num.indexOf(num);
if (index === -1) return;
s.num.splice(index, 1);
s.last.splice(index, 1);
const value = values[num] ?? [];
for (const key of value) {
delete s[key];
}
}
export function declineStudiedSkill() {
const s = (core.status.hero.special ??= { num: [], last: [] });
s.last = s.last.map(v => v - 1);
}
export function checkStudiedSkill() {
const s = core.status.hero.special;
for (let i = 0; i < s.last.length; i++) {
if (s.last[i] <= 0) {
forgetStudiedSkill(1, i);
i--;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
// @ts-nocheck

View File

@ -1,120 +0,0 @@
import { ref } from 'vue';
import list from '../../data/achievement.json';
import { achiDict, checkCompletionAchievement } from '../completion';
import { changeLocalStorage, has } from '../utils';
type AchievementList = typeof list;
export type AchievementType = keyof AchievementList;
type AchievementData = Record<AchievementType, boolean[]>;
export interface Achievement {
name: string;
text: string[];
point: number;
hide?: string;
progress?: string;
percent?: boolean;
}
export default function init() {
return { completeAchievement, hasCompletedAchievement, addMountSign };
}
export const totalPoint = Object.values(list)
.map((v: Achievement[]) =>
v.reduce((prev, curr) => {
return curr.point + prev;
}, 0)
)
.reduce((prev, curr) => prev + curr);
/**
*
* @param type
* @param index
*/
export function completeAchievement(type: AchievementType, index: number) {
if (flags.debug || hasCompletedAchievement(type, index)) return;
changeLocalStorage<AchievementData>(
'achievement',
data => {
data[type][index] = true;
return data;
},
{
normal: [],
challenge: [],
explore: []
}
);
if (type === 'explore' && !Object.values(achiDict).includes(index)) {
checkCompletionAchievement();
}
Mota.require('var', 'fixedUi').open('completeAchi', {
complete: `${type},${index}`
});
}
/**
*
* @param type
* @param index
*/
export function hasCompletedAchievement(type: AchievementType, index: number) {
let data = core.getLocalStorage<AchievementData>('achievement');
if (!has(data)) {
const d = {
normal: [],
challenge: [],
explore: []
};
data = d;
core.setLocalStorage('achievement', d);
}
return data[type][index] ?? false;
}
/**
*
*/
export function getNowPoint() {
let res = 0;
for (const [type, achi] of Object.entries(list)) {
achi.forEach((v, i) => {
if (hasCompletedAchievement(type as AchievementType, i)) {
res += v.point;
}
});
}
return res;
}
// ----- 各个成就相关的函数
/**
*
* @param id id
*/
export function addMountSign(id: number) {
if (flags.debug) return;
if (
!core.getLocalStorage(`mountSign_${id}`, false) &&
!hasCompletedAchievement('explore', 1)
) {
changeLocalStorage(
'mountSign',
n => {
if (n + 1 >= 5) {
completeAchievement('explore', 1);
for (const i of [1, 2, 3, 4, 5]) {
core.removeLocalStorage(`mountSign_${i}`);
}
}
return n + 1;
},
0
);
core.setLocalStorage(`mountSign_${id}`, true);
}
}

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

@ -1145,6 +1145,11 @@ interface Main extends MainData {
*/
readonly replayChecking: boolean;
/**
*
*/
renderLoaded: boolean;
/**
* @deprecated
* coremain.core吧

View File

@ -54,7 +54,7 @@
<div class="info-editor" v-if="!!selectedItem">
<div class="editor-custom">
<component
:is="selectedItem.controller"
:is="(selectedItem.controller as any)"
:item="selectedItem"
:displayer="displayer"
:setting="setting"

34
tsconfig.declaration.json Normal file
View File

@ -0,0 +1,34 @@
// packages/vlib-ui/tsconfig.declaration.json
{
"compilerOptions": {
"experimentalDecorators": true,
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@ui/*": ["src/ui/*"]
},
"outDir": "dist/types/",
"declaration": true,
"emitDeclarationOnly": true
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"mota.config.ts"
],
"references": [{ "path": "./tsconfig.node.json" }],
"exclude": ["node_modules", "**/__tests__/**", "**/__demos__/**", "**/*.md"]
}

View File

@ -21,7 +21,7 @@ export default defineConfig({
}),
components({ resolvers: [AntDesignVueResolver()] })
],
base: `/games/${motaConfig.name}/`,
base: `./`,
resolve: {
alias: {
'@': resolve(__dirname, './src'),