mirror of
https://github.com/unanmed/HumanBreak.git
synced 2026-11-01 07:24:30 +08:00
插件完全ts化
This commit is contained in:
1 parent
b2cb89a7f6
commit
2e7e64b399
32 files changed
+147
-112
No files matched your search
@@ -0,0 +1,23 @@
|
||||
export function chaseInit1() {
|
||||
const ids = ['MT13', 'MT14', 'MT15'];
|
||||
const toRemove = [];
|
||||
ids.forEach(v => {
|
||||
core.status.maps[v].cannotMoveDirectly = true;
|
||||
core.extractBlocks(v);
|
||||
core.status.maps[v].blocks.forEach(vv => {
|
||||
if (
|
||||
['animates', 'items'].includes(vv.event.cls) &&
|
||||
!vv.event.id.endsWith('Portal')
|
||||
) {
|
||||
toRemove.push([vv.x, vv.y, v]);
|
||||
}
|
||||
});
|
||||
});
|
||||
toRemove.forEach(v => {
|
||||
core.removeBlock(...v);
|
||||
});
|
||||
}
|
||||
|
||||
core.plugin.chase = {
|
||||
chaseInit1
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
export class EnemyCollection {}
|
||||
|
||||
export class Enemy {}
|
||||
|
||||
core.plugin.damage = {
|
||||
Enemy
|
||||
};
|
||||
@@ -0,0 +1,198 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
export {};
|
||||
|
||||
// 创建新图层
|
||||
function createCanvas(name, zIndex) {
|
||||
if (!name) return;
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.id = name;
|
||||
canvas.className = 'gameCanvas no-anti-aliasing';
|
||||
// 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高
|
||||
if (main.mode != 'editor') canvas.style.zIndex = zIndex || 0;
|
||||
// 将图层插入进游戏内容
|
||||
document.getElementById('gameDraw').appendChild(canvas);
|
||||
var ctx = canvas.getContext('2d');
|
||||
core.canvas[name] = ctx;
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
var bg2Canvas = createCanvas('bg2', 20);
|
||||
var fg2Canvas = createCanvas('fg2', 63);
|
||||
// 大地图适配
|
||||
core.bigmap.canvas = ['bg2', 'fg2', 'bg', 'event', 'event2', 'fg', 'damage'];
|
||||
core.initStatus.bg2maps = {};
|
||||
core.initStatus.fg2maps = {};
|
||||
|
||||
if (main.mode == 'editor') {
|
||||
/*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/
|
||||
// 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层)
|
||||
// 背景层2(bg2) 插入事件层(event)之前(即bg与event之间)
|
||||
document
|
||||
.getElementById('mapEdit')
|
||||
.insertBefore(bg2Canvas, document.getElementById('event'));
|
||||
// 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后)
|
||||
document
|
||||
.getElementById('mapEdit')
|
||||
.insertBefore(fg2Canvas, document.getElementById('ebm'));
|
||||
// 原本有三个图层 从4开始添加
|
||||
var num = 4;
|
||||
// 新增图层存入editor.dom中
|
||||
editor.dom.bg2c = core.canvas.bg2.canvas;
|
||||
editor.dom.bg2Ctx = core.canvas.bg2;
|
||||
editor.dom.fg2c = core.canvas.fg2.canvas;
|
||||
editor.dom.fg2Ctx = core.canvas.fg2;
|
||||
editor.dom.maps.push('bg2map', 'fg2map');
|
||||
editor.dom.canvas.push('bg2', 'fg2');
|
||||
|
||||
// 创建编辑器上的按钮
|
||||
var createCanvasBtn = name => {
|
||||
// 电脑端创建按钮
|
||||
var input = document.createElement('input');
|
||||
// layerMod4/layerMod5
|
||||
var id = 'layerMod' + num++;
|
||||
// bg2map/fg2map
|
||||
var value = name + 'map';
|
||||
input.type = 'radio';
|
||||
input.name = 'layerMod';
|
||||
input.id = id;
|
||||
input.value = value;
|
||||
editor.dom[id] = input;
|
||||
input.onchange = () => {
|
||||
editor.uifunctions.setLayerMod(value);
|
||||
};
|
||||
return input;
|
||||
};
|
||||
|
||||
var createCanvasBtn_mobile = name => {
|
||||
// 手机端往选择列表中添加子选项
|
||||
var input = document.createElement('option');
|
||||
var id = 'layerMod' + num++;
|
||||
var value = name + 'map';
|
||||
input.name = 'layerMod';
|
||||
input.value = value;
|
||||
editor.dom[id] = input;
|
||||
return input;
|
||||
};
|
||||
if (!editor.isMobile) {
|
||||
var input = createCanvasBtn('bg2');
|
||||
var input2 = createCanvasBtn('fg2');
|
||||
// 获取事件层及其父节点
|
||||
var child = document.getElementById('layerMod'),
|
||||
parent = child.parentNode;
|
||||
// 背景层2插入事件层前
|
||||
parent.insertBefore(input, child);
|
||||
// 不能直接更改背景层2的innerText 所以创建文本节点
|
||||
var txt = document.createTextNode('背2');
|
||||
// 插入事件层前(即新插入的背景层2前)
|
||||
parent.insertBefore(txt, child);
|
||||
// 向最后插入前景层2(即插入前景层后)
|
||||
parent.appendChild(input2);
|
||||
var txt2 = document.createTextNode('前2');
|
||||
parent.appendChild(txt2);
|
||||
} else {
|
||||
var input = createCanvasBtn_mobile('bg2');
|
||||
var input2 = createCanvasBtn_mobile('fg2');
|
||||
// 手机端因为是选项 所以可以直接改innerText
|
||||
input.innerText = '背景2';
|
||||
input2.innerText = '前景2';
|
||||
var parent = document.getElementById('layerMod');
|
||||
parent.insertBefore(input, parent.children[1]);
|
||||
parent.appendChild(input2);
|
||||
}
|
||||
}
|
||||
core.maps._loadFloor_doNotCopy = function () {
|
||||
return [
|
||||
'firstArrive',
|
||||
'eachArrive',
|
||||
'blocks',
|
||||
'parallelDo',
|
||||
'map',
|
||||
'bgmap',
|
||||
'fgmap',
|
||||
'bg2map',
|
||||
'fg2map',
|
||||
'events',
|
||||
'changeFloor',
|
||||
'afterBattle',
|
||||
'afterGetItem',
|
||||
'afterOpenDoor',
|
||||
'cannotMove'
|
||||
];
|
||||
};
|
||||
////// 绘制背景和前景层 //////
|
||||
core.maps._drawBg_draw = function (floorId, toDrawCtx, cacheCtx, config) {
|
||||
config.ctx = cacheCtx;
|
||||
core.maps._drawBg_drawBackground(floorId, config);
|
||||
// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。
|
||||
core.maps._drawFloorImages(
|
||||
floorId,
|
||||
config.ctx,
|
||||
'bg',
|
||||
null,
|
||||
null,
|
||||
config.onMap
|
||||
);
|
||||
core.maps._drawBgFgMap(floorId, 'bg', config);
|
||||
if (config.onMap) {
|
||||
core.drawImage(
|
||||
toDrawCtx,
|
||||
cacheCtx.canvas,
|
||||
core.bigmap.v2 ? -32 : 0,
|
||||
core.bigmap.v2 ? -32 : 0
|
||||
);
|
||||
core.clearMap('bg2');
|
||||
core.clearMap(cacheCtx);
|
||||
}
|
||||
core.maps._drawBgFgMap(floorId, 'bg2', config);
|
||||
if (config.onMap)
|
||||
core.drawImage(
|
||||
'bg2',
|
||||
cacheCtx.canvas,
|
||||
core.bigmap.v2 ? -32 : 0,
|
||||
core.bigmap.v2 ? -32 : 0
|
||||
);
|
||||
config.ctx = toDrawCtx;
|
||||
};
|
||||
core.maps._drawFg_draw = function (floorId, toDrawCtx, cacheCtx, config) {
|
||||
config.ctx = cacheCtx;
|
||||
// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。
|
||||
core.maps._drawFloorImages(
|
||||
floorId,
|
||||
config.ctx,
|
||||
'fg',
|
||||
null,
|
||||
null,
|
||||
config.onMap
|
||||
);
|
||||
core.maps._drawBgFgMap(floorId, 'fg', config);
|
||||
if (config.onMap) {
|
||||
core.drawImage(
|
||||
toDrawCtx,
|
||||
cacheCtx.canvas,
|
||||
core.bigmap.v2 ? -32 : 0,
|
||||
core.bigmap.v2 ? -32 : 0
|
||||
);
|
||||
core.clearMap('fg2');
|
||||
core.clearMap(cacheCtx);
|
||||
}
|
||||
core.maps._drawBgFgMap(floorId, 'fg2', config);
|
||||
if (config.onMap)
|
||||
core.drawImage(
|
||||
'fg2',
|
||||
cacheCtx.canvas,
|
||||
core.bigmap.v2 ? -32 : 0,
|
||||
core.bigmap.v2 ? -32 : 0
|
||||
);
|
||||
config.ctx = toDrawCtx;
|
||||
};
|
||||
////// 移动判定 //////
|
||||
core.maps._generateMovableArray_arrays = function (floorId) {
|
||||
return {
|
||||
bgArray: this.getBgMapArray(floorId),
|
||||
fgArray: this.getFgMapArray(floorId),
|
||||
eventArray: this.getMapArray(floorId),
|
||||
bg2Array: this._getBgFgMapArray('bg2', floorId),
|
||||
fg2Array: this._getBgFgMapArray('fg2', floorId)
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
/**
|
||||
* 绘制光环范围
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {boolean} onMap
|
||||
*/
|
||||
export function drawHalo(ctx, onMap) {
|
||||
if (main.replayChecking) return;
|
||||
if (!core.getLocalStorage('showHalo', true)) return;
|
||||
const halo = core.status.checkBlock.halo;
|
||||
ctx.save();
|
||||
for (const [loc, range] of Object.entries(halo)) {
|
||||
const [x, y] = loc.split(',').map(v => parseInt(v));
|
||||
for (const r of range) {
|
||||
const [type, value, color, border] = r.split(':');
|
||||
if (type === 'square') {
|
||||
// 正方形光环
|
||||
const n = parseInt(value);
|
||||
const r = Math.floor(n / 2);
|
||||
let left = x - r,
|
||||
right = x + r,
|
||||
top = y - r,
|
||||
bottom = y + r;
|
||||
if (onMap && core.bigmap.v2) {
|
||||
left -= core.bigmap.posX;
|
||||
top -= core.bigmap.posY;
|
||||
right -= core.bigmap.posX;
|
||||
bottom -= core.bigmap.posY;
|
||||
if (
|
||||
right < -1 ||
|
||||
left > core._PX_ / 32 + 1 ||
|
||||
top < -1 ||
|
||||
bottom > core._PY_ / 32 + 1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ctx.fillStyle = color;
|
||||
ctx.strokeStyle = border ?? color;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.globalAlpha = 0.1;
|
||||
ctx.fillRect(left * 32, top * 32, n * 32, n * 32);
|
||||
ctx.globalAlpha = 0.6;
|
||||
ctx.strokeRect(left * 32, top * 32, n * 32, n * 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
core.plugin.halo = {
|
||||
drawHalo
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
/**
|
||||
* 获取勇士在某一点的属性
|
||||
* @param {keyof HeroStatus | 'all'} name
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {FloorIds} floorId
|
||||
*/
|
||||
export function getHeroStatusOn(name, x, y, floorId) {
|
||||
return getHeroStatusOf(core.status.hero, name, x, y, floorId);
|
||||
}
|
||||
|
||||
export function getHeroStatusOf(status, name, x, y, floorId) {
|
||||
return getRealStatus(status, name, x, y, floorId);
|
||||
}
|
||||
|
||||
function getRealStatus(status, name, x, y, floorId) {
|
||||
if (name instanceof Array) {
|
||||
return Object.fromEntries(
|
||||
name.map(v => [
|
||||
v,
|
||||
v !== 'all' && getRealStatus(status, v, x, y, floorId)
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'all') {
|
||||
return Object.fromEntries(
|
||||
Object.keys(core.status.hero).map(v => [
|
||||
v,
|
||||
v !== 'all' && getRealStatus(status, v, x, y, floorId)
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
let s = status?.[name] ?? core.status.hero[name];
|
||||
if (s === null || s === void 0) {
|
||||
throw new ReferenceError(
|
||||
`Wrong hero status property name is delivered: ${name}`
|
||||
);
|
||||
}
|
||||
|
||||
x ??= core.status.hero.loc.x;
|
||||
y ??= core.status.hero.loc.y;
|
||||
floorId ??= core.status.floorId;
|
||||
|
||||
// 永夜、极昼
|
||||
if (name === 'atk' || name === 'def') {
|
||||
s += window.flags?.[`night_${floorId}`] ?? 0;
|
||||
}
|
||||
|
||||
// 技能
|
||||
if (flags.bladeOn && flags.blade) {
|
||||
const level = core.plugin.skillTree.getSkillLevel(2);
|
||||
if (name === 'atk') {
|
||||
s *= 1 + 0.1 * level;
|
||||
}
|
||||
if (name === 'def') {
|
||||
s *= 1 - 0.1 * level;
|
||||
}
|
||||
}
|
||||
if (flags.shield && flags.shieldOn) {
|
||||
const level = core.plugin.skillTree.getSkillLevel(10);
|
||||
if (name === 'atk') {
|
||||
s *= 1 - 0.1 * level;
|
||||
}
|
||||
if (name === 'def') {
|
||||
s *= 1 + 0.1 * level;
|
||||
}
|
||||
}
|
||||
|
||||
// buff
|
||||
if (typeof s === 'number') s *= core.getBuff(name);
|
||||
|
||||
// 取整
|
||||
if (typeof s === 'number') s = Math.floor(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
core.plugin.hero = {
|
||||
getHeroStatusOf,
|
||||
getHeroStatusOn
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
export {};
|
||||
|
||||
['up', 'down', 'left', 'right'].forEach(one => {
|
||||
// 指定中间帧动画
|
||||
core.material.icons.hero[one].midFoot = 2;
|
||||
});
|
||||
|
||||
var heroMoving = timestamp => {
|
||||
if (core.status.heroMoving <= 0) return;
|
||||
if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) {
|
||||
core.animateFrame.leftLeg++;
|
||||
core.animateFrame.moveTime = timestamp;
|
||||
}
|
||||
core.drawHero(
|
||||
['stop', 'leftFoot', 'midFoot', 'rightFoot'][
|
||||
core.animateFrame.leftLeg % 4
|
||||
],
|
||||
4 * core.status.heroMoving
|
||||
);
|
||||
};
|
||||
core.registerAnimationFrame('heroMoving', true, heroMoving);
|
||||
|
||||
core.events._eventMoveHero_moving = function (step, moveSteps) {
|
||||
var curr = moveSteps[0];
|
||||
var direction = curr[0],
|
||||
x = core.getHeroLoc('x'),
|
||||
y = core.getHeroLoc('y');
|
||||
// ------ 前进/后退
|
||||
var o = direction == 'backward' ? -1 : 1;
|
||||
if (direction == 'forward' || direction == 'backward')
|
||||
direction = core.getHeroLoc('direction');
|
||||
var faceDirection = direction;
|
||||
if (direction == 'leftup' || direction == 'leftdown')
|
||||
faceDirection = 'left';
|
||||
if (direction == 'rightup' || direction == 'rightdown')
|
||||
faceDirection = 'right';
|
||||
core.setHeroLoc('direction', direction);
|
||||
if (curr[1] <= 0) {
|
||||
core.setHeroLoc('direction', faceDirection);
|
||||
moveSteps.shift();
|
||||
return true;
|
||||
}
|
||||
if (step <= 4) core.drawHero('stop', 4 * o * step);
|
||||
else if (step <= 8) core.drawHero('leftFoot', 4 * o * step);
|
||||
else if (step <= 12) core.drawHero('midFoot', 4 * o * (step - 8));
|
||||
else if (step <= 16) core.drawHero('rightFoot', 4 * o * (step - 8)); // if (step == 8) {
|
||||
if (step == 8 || step == 16) {
|
||||
core.setHeroLoc('x', x + o * core.utils.scan2[direction].x, true);
|
||||
core.setHeroLoc('y', y + o * core.utils.scan2[direction].y, true);
|
||||
core.updateFollowers();
|
||||
curr[1]--;
|
||||
if (curr[1] <= 0) moveSteps.shift();
|
||||
core.setHeroLoc('direction', faceDirection);
|
||||
return step == 16;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -0,0 +1,226 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
export {};
|
||||
|
||||
/* @__PURE__ */ (function () {
|
||||
if (main.mode !== 'play' || main.replayChecking) return;
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
* @param {string} url
|
||||
* @param {string} type
|
||||
* @param {string} data
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function post(url, type, data) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open(type, url);
|
||||
xhr.send(data);
|
||||
const res = await new Promise(res => {
|
||||
xhr.onload = () => {
|
||||
if (xhr.status !== 200) {
|
||||
console.error(`hot reload: http ${xhr.status}`);
|
||||
res('@error');
|
||||
} else res('success');
|
||||
};
|
||||
xhr.onerror = () => {
|
||||
res('@error');
|
||||
console.error(`hot reload: error on connection`);
|
||||
};
|
||||
});
|
||||
if (res === 'success') return xhr.response;
|
||||
else return '@error';
|
||||
}
|
||||
|
||||
/**
|
||||
* 热重载css
|
||||
* @param {string} data
|
||||
*/
|
||||
function reloadCss(data) {
|
||||
const css = document.getElementById('mota-css');
|
||||
css.remove();
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = data;
|
||||
link.id = 'mota-css';
|
||||
document.head.appendChild(link);
|
||||
console.log(`css hot reload: ${data}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 热重载楼层
|
||||
* @param {string} data
|
||||
*/
|
||||
async function reloadFloor(data) {
|
||||
// 如果被砍层了直接忽略
|
||||
if (
|
||||
core.status.maps[data].deleted ||
|
||||
core.status.maps[data].forceDelete
|
||||
)
|
||||
return;
|
||||
// 首先重新加载main.floors对应的楼层
|
||||
await import(
|
||||
/* @vite-ignore */ `./project/floors/${data}.js?v=${Date.now()}`
|
||||
);
|
||||
// 然后写入core.floors并解析
|
||||
core.floors[data] = main.floors[data];
|
||||
const floor = core.loadFloor(data);
|
||||
if (core.isPlaying()) {
|
||||
core.status.maps[data] = floor;
|
||||
delete core.status.mapBlockObjs[data];
|
||||
core.extractBlocks(data);
|
||||
if (data === core.status.floorId) {
|
||||
core.drawMap(data);
|
||||
let weather = core.getFlag('__weather__', null);
|
||||
if (!weather && core.status.thisMap.weather)
|
||||
weather = core.status.thisMap.weather;
|
||||
if (weather) core.setWeather(weather[0], weather[1]);
|
||||
else core.setWeather();
|
||||
}
|
||||
core.updateStatusBar(true, true);
|
||||
}
|
||||
console.log(`floor hot reload: ${data}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 热重载脚本编辑
|
||||
* @param {string} data
|
||||
*/
|
||||
async function reloadScript() {
|
||||
// 脚本编辑略微麻烦点
|
||||
const before = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a;
|
||||
// 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了
|
||||
const script = document.createElement('script');
|
||||
script.src = `/project/functions.js?v=${Date.now()}`;
|
||||
document.body.appendChild(script);
|
||||
await new Promise(res => {
|
||||
script.onload = () => res('success');
|
||||
});
|
||||
const after = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a;
|
||||
// 找到差异的函数
|
||||
for (const mod in before) {
|
||||
const fns = before[mod];
|
||||
for (const id in fns) {
|
||||
const fn = fns[id];
|
||||
if (typeof fn !== 'function' || id === 'hasSpecial') continue;
|
||||
const now = after[mod][id];
|
||||
if (fn.toString() !== now.toString()) {
|
||||
try {
|
||||
if (mod === 'events') {
|
||||
core.events.eventdata[id] = now;
|
||||
} else if (mod === 'enemys') {
|
||||
core.enemys.enemydata[id] = now;
|
||||
} else if (mod === 'actions') {
|
||||
core.actions.actionsdata[id] = now;
|
||||
} else if (mod === 'control') {
|
||||
core.control.controldata[id] = now;
|
||||
} else if (mod === 'ui') {
|
||||
core.ui.uidata[id] = now;
|
||||
}
|
||||
core.updateStatusBar(true, true);
|
||||
console.log(`function hot reload: ${mod}.${id}`);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadPlugin(data) {
|
||||
// 直接import就完事了
|
||||
await import(/* @vite-ignore */ `./src/plugin/game/${data}.js`);
|
||||
console.log(`plugin hot reload: ${data}.js`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 属性热重载,包括全塔属性等
|
||||
* @param {string} data
|
||||
*/
|
||||
async function reloadData(data) {
|
||||
const script = document.createElement('script');
|
||||
script.src = `/project/${data}.js?v=${Date.now()}`;
|
||||
document.body.appendChild(script);
|
||||
await new Promise(res => {
|
||||
script.onload = () => res('success');
|
||||
});
|
||||
|
||||
let after;
|
||||
if (data === 'data') after = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
if (data === 'enemys')
|
||||
after = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80;
|
||||
if (data === 'icons')
|
||||
after = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1;
|
||||
if (data === 'items')
|
||||
after = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a;
|
||||
if (data === 'maps') after = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
|
||||
if (data === 'events')
|
||||
after = events_c12a15a8_c380_4b28_8144_256cba95f760;
|
||||
|
||||
if (data === 'enemys') {
|
||||
core.enemys.enemys = after;
|
||||
for (var enemyId in after) {
|
||||
core.enemys.enemys[enemyId].id = enemyId;
|
||||
}
|
||||
core.material.enemys = core.getEnemys();
|
||||
} else if (data === 'icons') {
|
||||
core.icons.icons = after;
|
||||
core.material.icons = core.getIcons();
|
||||
} else if (data === 'items') {
|
||||
core.items.items = after;
|
||||
for (var itemId in after) {
|
||||
core.items.items[itemId].id = itemId;
|
||||
}
|
||||
core.material.items = core.getItems();
|
||||
} else if (data === 'maps') {
|
||||
core.maps.blocksInfo = after;
|
||||
core.status.mapBlockObjs = {};
|
||||
core.status.number2block = {};
|
||||
Object.values(core.status.maps).forEach(v => delete v.blocks);
|
||||
core.extractBlocks();
|
||||
core.setWeather(
|
||||
core.animateFrame.weather.type,
|
||||
core.animateFrame.weather.level
|
||||
);
|
||||
core.drawMap();
|
||||
} else if (data === 'events') {
|
||||
core.events.commonEvent = after.commonEvent;
|
||||
} else if (data === 'data') {
|
||||
location.reload();
|
||||
}
|
||||
core.updateStatusBar(true, true);
|
||||
console.log(`data hot reload: ${data}`);
|
||||
}
|
||||
|
||||
// 初始化
|
||||
(async function () {
|
||||
const data = await post('/reload', 'POST', 'test');
|
||||
if (data === '@error') {
|
||||
console.log(`未检测到node服务,热重载插件将无法使用`);
|
||||
} else {
|
||||
console.log(`热重载插件加载成功`);
|
||||
// reload
|
||||
setInterval(async () => {
|
||||
const res = await post('/reload', 'POST');
|
||||
if (res === '@error') return;
|
||||
if (res === 'true') location.reload();
|
||||
else return;
|
||||
}, 1000);
|
||||
|
||||
// hot reload
|
||||
setInterval(async () => {
|
||||
const res = await post('/hotReload', 'POST');
|
||||
const data = res.split('@@');
|
||||
data.forEach(v => {
|
||||
if (v === '') return;
|
||||
const [type, file] = v.split(':');
|
||||
if (type === 'css') reloadCss(file);
|
||||
if (type === 'data') reloadData(file);
|
||||
if (type === 'floor') reloadFloor(file);
|
||||
if (type === 'script') reloadScript(file);
|
||||
if (type === 'plugin') reloadPlugin(file);
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
})();
|
||||
})();
|
||||
@@ -0,0 +1,36 @@
|
||||
import './fiveLayer.js';
|
||||
import './heroFourFrames.js';
|
||||
import './hotReload.js';
|
||||
import './itemDetail.js';
|
||||
import './popup.js';
|
||||
import './replay.js';
|
||||
import './ui.js';
|
||||
import * as halo from './halo.js';
|
||||
import * as hero from './hero.js';
|
||||
import * as loopMap from './loopMap.js';
|
||||
import * as remainEnemy from './remainEnemy.js';
|
||||
import * as removeMap from './removeMap.js';
|
||||
import * as shop from './shop.js';
|
||||
import * as skill from './skills.js';
|
||||
import * as skillTree from './skillTree.js';
|
||||
import * as study from './study.js';
|
||||
import * as towerBoss from './towerBoss.js';
|
||||
import * as utils from './utils.js';
|
||||
import * as chase from './chase.js';
|
||||
import * as damage from './damage';
|
||||
|
||||
export {
|
||||
halo,
|
||||
hero,
|
||||
loopMap,
|
||||
remainEnemy,
|
||||
removeMap,
|
||||
shop,
|
||||
skill,
|
||||
skillTree,
|
||||
study,
|
||||
towerBoss,
|
||||
utils,
|
||||
chase,
|
||||
damage
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
export {};
|
||||
|
||||
core.control.updateDamage = function (floorId, ctx) {
|
||||
floorId = floorId || core.status.floorId;
|
||||
if (!floorId || core.status.gameOver || main.mode != 'play') return;
|
||||
const onMap = ctx == null;
|
||||
|
||||
// 没有怪物手册
|
||||
if (!core.hasItem('book')) return;
|
||||
core.status.damage.posX = core.bigmap.posX;
|
||||
core.status.damage.posY = core.bigmap.posY;
|
||||
if (!onMap) {
|
||||
const width = core.floors[floorId].width,
|
||||
height = core.floors[floorId].height;
|
||||
// 地图过大的缩略图不绘制显伤
|
||||
if (width * height > core.bigmap.threshold) return;
|
||||
}
|
||||
this._updateDamage_damage(floorId, onMap);
|
||||
this._updateDamage_extraDamage(floorId, onMap);
|
||||
getItemDetail(floorId, onMap); // 宝石血瓶详细信息
|
||||
this.drawDamage(ctx);
|
||||
};
|
||||
|
||||
// 获取宝石信息 并绘制
|
||||
function getItemDetail(floorId, onMap) {
|
||||
if (!core.getFlag('itemDetail')) return;
|
||||
floorId ??= core.status.thisMap.floorId;
|
||||
let diff = {};
|
||||
const before = core.status.hero;
|
||||
const hero = core.clone(core.status.hero);
|
||||
const handler = {
|
||||
set(target, key, v) {
|
||||
diff[key] = v - (target[key] || 0);
|
||||
if (!diff[key]) diff[key] = void 0;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
core.status.hero = new Proxy(hero, handler);
|
||||
|
||||
core.status.maps[floorId].blocks.forEach(function (block) {
|
||||
if (block.event.cls !== 'items' || block.disable) return;
|
||||
const x = block.x,
|
||||
y = block.y;
|
||||
// v2优化,只绘制范围内的部分
|
||||
if (onMap && core.bigmap.v2) {
|
||||
if (
|
||||
x < core.bigmap.posX - core.bigmap.extend ||
|
||||
x > core.bigmap.posX + core._PX_ + core.bigmap.extend ||
|
||||
y < core.bigmap.posY - core.bigmap.extend ||
|
||||
y > core.bigmap.posY + core._PY_ + core.bigmap.extend
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
diff = {};
|
||||
const id = block.event.id;
|
||||
const item = core.material.items[id];
|
||||
if (item.cls === 'equips') {
|
||||
// 装备也显示
|
||||
const diff = core.clone(item.equip.value ?? {});
|
||||
const per = item.equip.percentage ?? {};
|
||||
for (const name in per) {
|
||||
diff[name + 'per'] = per[name].toString() + '%';
|
||||
}
|
||||
drawItemDetail(diff, x, y);
|
||||
return;
|
||||
}
|
||||
// 跟数据统计原理一样 执行效果 前后比较
|
||||
core.setFlag('__statistics__', true);
|
||||
try {
|
||||
eval(item.itemEffect);
|
||||
} catch (error) {}
|
||||
drawItemDetail(diff, x, y);
|
||||
});
|
||||
core.status.hero = before;
|
||||
window.hero = before;
|
||||
window.flags = before.flags;
|
||||
}
|
||||
|
||||
// 绘制
|
||||
function drawItemDetail(diff, x, y) {
|
||||
const px = 32 * x + 2,
|
||||
py = 32 * y + 31;
|
||||
let content = '';
|
||||
// 获得数据和颜色
|
||||
let i = 0;
|
||||
for (const name in diff) {
|
||||
if (!diff[name]) continue;
|
||||
let color = '#fff';
|
||||
|
||||
if (typeof diff[name] === 'number')
|
||||
content = core.formatBigNumber(diff[name], true);
|
||||
else content = diff[name];
|
||||
switch (name) {
|
||||
case 'atk':
|
||||
case 'atkper':
|
||||
color = '#FF7A7A';
|
||||
break;
|
||||
case 'def':
|
||||
case 'defper':
|
||||
color = '#00E6F1';
|
||||
break;
|
||||
case 'mdef':
|
||||
case 'mdefper':
|
||||
color = '#6EFF83';
|
||||
break;
|
||||
case 'hp':
|
||||
color = '#A4FF00';
|
||||
break;
|
||||
case 'hpmax':
|
||||
case 'hpmaxper':
|
||||
color = '#F9FF00';
|
||||
break;
|
||||
case 'mana':
|
||||
color = '#c66';
|
||||
break;
|
||||
}
|
||||
// 绘制
|
||||
core.status.damage.data.push({
|
||||
text: content,
|
||||
px: px,
|
||||
py: py - 10 * i,
|
||||
color: color
|
||||
});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
import { slide } from './utils.js';
|
||||
|
||||
const list = ['tower6'];
|
||||
|
||||
/**
|
||||
* 设置循环地图的偏移量
|
||||
* @param {number} offset 横向偏移量
|
||||
* @param {FloorIds} floorId
|
||||
*/
|
||||
function setLoopMap(offset, floorId) {
|
||||
const floor = core.status.maps[floorId];
|
||||
if (offset < 9) {
|
||||
moveMap(floor.width - 17, floorId);
|
||||
}
|
||||
if (offset > floor.width - 9) {
|
||||
moveMap(17 - floor.width, floorId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当勇士移动时自动设置循环地图
|
||||
* @param {FloorIds} floorId
|
||||
*/
|
||||
function autoSetLoopMap(floorId) {
|
||||
setLoopMap(core.status.hero.loc.x, floorId);
|
||||
}
|
||||
|
||||
export function checkLoopMap() {
|
||||
if (isLoopMap(core.status.floorId)) {
|
||||
autoSetLoopMap(core.status.floorId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动地图
|
||||
* @param {number} delta
|
||||
* @param {FloorIds} floorId
|
||||
*/
|
||||
function moveMap(delta, floorId) {
|
||||
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) {
|
||||
return list.includes(floorId);
|
||||
}
|
||||
|
||||
events.prototype._sys_changeFloor = function (data, callback) {
|
||||
data = data.event.data;
|
||||
let heroLoc = {};
|
||||
if (isLoopMap(data.floorId)) {
|
||||
const floor = core.status.maps[data.floorId];
|
||||
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;
|
||||
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, y, callback) {
|
||||
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
|
||||
},
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
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;
|
||||
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 = { event: {}, x: tx, y };
|
||||
b.event.data = c;
|
||||
b.event.trigger = 'changeFloor';
|
||||
block = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
core.status.automaticRoute.moveDirectly = false;
|
||||
this.doSystemEvent(trigger, block);
|
||||
}
|
||||
return _executeCallback();
|
||||
};
|
||||
|
||||
maps.prototype._getBgFgMapArray = function (name, floorId, noCache) {
|
||||
floorId = floorId || core.status.floorId;
|
||||
if (!floorId) return [];
|
||||
var width = core.floors[floorId].width;
|
||||
var height = core.floors[floorId].height;
|
||||
|
||||
if (!noCache && core.status[name + 'maps'][floorId])
|
||||
return core.status[name + 'maps'][floorId];
|
||||
|
||||
var arr =
|
||||
main.mode == 'editor' &&
|
||||
!(window.editor && editor.uievent && editor.uievent.isOpen)
|
||||
? core.cloneArray(editor[name + 'map'])
|
||||
: null;
|
||||
if (arr == null)
|
||||
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);
|
||||
}
|
||||
(core.getFlag('__' + name + 'v__', {})[floorId] || []).forEach(function (
|
||||
one
|
||||
) {
|
||||
arr[one[1]][one[0]] = one[2] || 0;
|
||||
});
|
||||
(core.getFlag('__' + name + 'd__', {})[floorId] || []).forEach(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++) {
|
||||
arr[y][x] = arr[y][x].idnum || arr[y][x] || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (core.status[name + 'maps']) core.status[name + 'maps'][floorId] = arr;
|
||||
return arr;
|
||||
};
|
||||
|
||||
core.plugin.loopMap = {
|
||||
checkLoopMap
|
||||
};
|
||||
@@ -0,0 +1,121 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
export {};
|
||||
|
||||
// 伤害弹出
|
||||
// 复写阻激夹域检测
|
||||
control.prototype.checkBlock = function (forceMockery) {
|
||||
var x = core.getHeroLoc('x'),
|
||||
y = core.getHeroLoc('y'),
|
||||
loc = x + ',' + y;
|
||||
var damage = core.status.checkBlock.damage[loc];
|
||||
if (damage) {
|
||||
if (!main.replayChecking)
|
||||
core.addPop(
|
||||
(x - core.bigmap.offsetX / 32) * 32 + 12,
|
||||
(y - core.bigmap.offsetY / 32) * 32 + 20,
|
||||
-damage.toString()
|
||||
);
|
||||
core.status.hero.hp -= damage;
|
||||
var text =
|
||||
Object.keys(core.status.checkBlock.type[loc] || {}).join(',') ||
|
||||
'伤害';
|
||||
core.drawTip('受到' + text + damage + '点');
|
||||
core.drawHeroAnimate('zone');
|
||||
this._checkBlock_disableQuickShop();
|
||||
core.status.hero.statistics.extraDamage += damage;
|
||||
if (core.status.hero.hp <= 0) {
|
||||
core.status.hero.hp = 0;
|
||||
core.updateStatusBar();
|
||||
core.events.lose();
|
||||
return;
|
||||
} else {
|
||||
core.updateStatusBar();
|
||||
}
|
||||
}
|
||||
this._checkBlock_repulse(core.status.checkBlock.repulse[loc]);
|
||||
checkMockery(loc, forceMockery);
|
||||
};
|
||||
|
||||
control.prototype.moveHero = function (direction, callback) {
|
||||
// 如果正在移动,直接return
|
||||
if (core.status.heroMoving != 0) return;
|
||||
if (core.isset(direction)) core.setHeroLoc('direction', direction);
|
||||
|
||||
const nx = core.nextX();
|
||||
const ny = core.nextY();
|
||||
if (core.status.checkBlock.mockery[`${nx},${ny}`]) {
|
||||
core.autosave();
|
||||
}
|
||||
|
||||
if (callback) return this.moveAction(callback);
|
||||
this._moveHero_moving();
|
||||
};
|
||||
|
||||
/**
|
||||
* 电摇嘲讽
|
||||
* @param {LocString} loc
|
||||
* @param {boolean} force
|
||||
*/
|
||||
function checkMockery(loc, force) {
|
||||
if (core.status.lockControl && !force) return;
|
||||
const mockery = core.status.checkBlock.mockery[loc];
|
||||
if (mockery) {
|
||||
mockery.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]));
|
||||
const action = [];
|
||||
const [tx, ty] = mockery[0];
|
||||
let { x, y } = core.status.hero.loc;
|
||||
const dir = x > tx ? 'left' : x < tx ? 'right' : y > ty ? 'up' : 'down';
|
||||
const { x: dx, y: dy } = core.utils.scan[dir];
|
||||
|
||||
action.push({ type: 'changePos', direction: dir });
|
||||
const blocks = core.getMapBlocksObj();
|
||||
while (1) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
const block = blocks[`${x},${y}`];
|
||||
if (block) {
|
||||
block.event.cls === '';
|
||||
if (
|
||||
[
|
||||
'animates',
|
||||
'autotile',
|
||||
'tileset',
|
||||
'npcs',
|
||||
'npc48',
|
||||
'terrains'
|
||||
].includes(block.event.cls)
|
||||
) {
|
||||
action.push(
|
||||
{
|
||||
type: 'hide',
|
||||
loc: [[x, y]],
|
||||
remove: true,
|
||||
time: 0
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: `function() { core.removeGlobalAnimate(${x}, ${y}) }`
|
||||
},
|
||||
{
|
||||
type: 'animate',
|
||||
name: 'hand',
|
||||
loc: [x, y],
|
||||
async: true
|
||||
}
|
||||
);
|
||||
}
|
||||
if (block.event.cls.startsWith('enemy')) {
|
||||
action.push({ type: 'moveAction' });
|
||||
}
|
||||
}
|
||||
action.push({ type: 'moveAction' });
|
||||
if (x === tx && y === ty) break;
|
||||
}
|
||||
action.push({
|
||||
type: 'function',
|
||||
function: `function() { core.checkBlock(true); }`
|
||||
});
|
||||
action.push({ type: 'stopAsync' });
|
||||
core.insertAction(action);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
/**
|
||||
* 检查漏怪
|
||||
* @param {FloorIds[]} floorIds
|
||||
*/
|
||||
export function checkRemainEnemy(floorIds) {
|
||||
/**
|
||||
* @type {Record<FloorIds, {loc: LocArr, id: EnemyIds}[]>}
|
||||
*/
|
||||
const enemy = {};
|
||||
floorIds.forEach(v => {
|
||||
core.extractBlocks(v);
|
||||
const blocks = core.status.maps[v].blocks;
|
||||
blocks.forEach(block => {
|
||||
if (!block.event.cls.startsWith('enemy') || block.disable) return;
|
||||
/**
|
||||
* @type {EnemyIds}
|
||||
*/
|
||||
const id = block.event.id;
|
||||
enemy[v] ??= [];
|
||||
const info = enemy[v];
|
||||
info.push({ loc: [block.x, block.y], id });
|
||||
});
|
||||
});
|
||||
return enemy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剩余怪物字符串
|
||||
* @param {FloorIds[]} floorIds
|
||||
*/
|
||||
export function getRemainEnemyString(floorIds) {
|
||||
const enemy = checkRemainEnemy(floorIds);
|
||||
const str = [];
|
||||
let now = [];
|
||||
for (const floor in enemy) {
|
||||
/**
|
||||
* @type {{loc: LocArr, id: EnemyIds}[]}
|
||||
*/
|
||||
const all = enemy[floor];
|
||||
/**
|
||||
* @type {Record<EnemyIds, number>}
|
||||
*/
|
||||
const remain = {};
|
||||
all.forEach(v => {
|
||||
const id = v.id;
|
||||
remain[id] ??= 0;
|
||||
remain[id]++;
|
||||
});
|
||||
const title = core.status.maps[floor].title;
|
||||
for (const id in remain) {
|
||||
const name = core.material.enemys[id].name;
|
||||
now.push(`${title}(${floor}): ${name} * ${remain[id]}`);
|
||||
if (now.length === 10) {
|
||||
str.push(now.join('\n'));
|
||||
now = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (now.length > 0) {
|
||||
str.push(now.join('\n'));
|
||||
str[0] = `当前剩余怪物:\n${str[0]}`;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
core.plugin.remainEnemy = {
|
||||
checkRemainEnemy,
|
||||
getRemainEnemyString
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
export function removeMaps(fromId, toId, force) {
|
||||
toId = toId || fromId;
|
||||
var fromIndex = core.floorIds.indexOf(fromId),
|
||||
toIndex = core.floorIds.indexOf(toId);
|
||||
if (toIndex < 0) toIndex = core.floorIds.length - 1;
|
||||
flags.__visited__ = flags.__visited__ || {};
|
||||
flags.__removed__ = flags.__removed__ || [];
|
||||
flags.__disabled__ = flags.__disabled__ || {};
|
||||
flags.__leaveLoc__ = flags.__leaveLoc__ || {};
|
||||
flags.__forceDelete__ ??= {};
|
||||
let deleted = false;
|
||||
for (var i = fromIndex; i <= toIndex; ++i) {
|
||||
var floorId = core.floorIds[i];
|
||||
if (core.status.maps[floorId].deleted) continue;
|
||||
delete flags.__visited__[floorId];
|
||||
flags.__removed__.push(floorId);
|
||||
delete flags.__disabled__[floorId];
|
||||
delete flags.__leaveLoc__[floorId];
|
||||
(core.status.autoEvents || []).forEach(event => {
|
||||
if (event.floorId == floorId && event.currentFloor) {
|
||||
core.autoEventExecuting(event.symbol, false);
|
||||
core.autoEventExecuted(event.symbol, false);
|
||||
}
|
||||
});
|
||||
core.status.maps[floorId].deleted = true;
|
||||
core.status.maps[floorId].canFlyTo = false;
|
||||
core.status.maps[floorId].canFlyFrom = false;
|
||||
core.status.maps[floorId].cannotViewMap = true;
|
||||
if (force) {
|
||||
core.status.maps[floorId].forceDelete = true;
|
||||
flags.__forceDelete__[floorId] = true;
|
||||
}
|
||||
deleteFlags(floorId);
|
||||
deleted = true;
|
||||
}
|
||||
if (deleted && !main.replayChecking) {
|
||||
core.splitArea();
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteFlags(floorId) {
|
||||
delete flags[`jump_${floorId}`];
|
||||
delete flags[`inte_${floorId}`];
|
||||
delete flags[`loop_${floorId}`];
|
||||
delete flags[`melt_${floorId}`];
|
||||
delete flags[`night_${floorId}`];
|
||||
}
|
||||
|
||||
// 恢复楼层
|
||||
// core.plugin.removeMap.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层
|
||||
// core.plugin.removeMap.resumeMaps("MT10") 只恢复MT10层
|
||||
export function resumeMaps(fromId, toId) {
|
||||
toId = toId || fromId;
|
||||
var fromIndex = core.floorIds.indexOf(fromId),
|
||||
toIndex = core.floorIds.indexOf(toId);
|
||||
if (toIndex < 0) toIndex = core.floorIds.length - 1;
|
||||
flags.__removed__ = flags.__removed__ || [];
|
||||
for (var i = fromIndex; i <= toIndex; ++i) {
|
||||
var floorId = core.floorIds[i];
|
||||
if (!core.status.maps[floorId].deleted) continue;
|
||||
if (
|
||||
core.status.maps[floorId].forceDelete ||
|
||||
flags.__forceDelete__[floorId]
|
||||
)
|
||||
continue;
|
||||
flags.__removed__ = flags.__removed__.filter(f => {
|
||||
return f != floorId;
|
||||
});
|
||||
core.status.maps[floorId] = core.loadFloor(floorId);
|
||||
}
|
||||
}
|
||||
|
||||
// 分区砍层相关
|
||||
var inAnyPartition = floorId => {
|
||||
var inPartition = false;
|
||||
(core.floorPartitions || []).forEach(floor => {
|
||||
var fromIndex = core.floorIds.indexOf(floor[0]);
|
||||
var toIndex = core.floorIds.indexOf(floor[1]);
|
||||
var index = core.floorIds.indexOf(floorId);
|
||||
if (fromIndex < 0 || index < 0) return;
|
||||
if (toIndex < 0) toIndex = core.floorIds.length - 1;
|
||||
if (index >= fromIndex && index <= toIndex) inPartition = true;
|
||||
});
|
||||
return inPartition;
|
||||
};
|
||||
|
||||
// 分区砍层
|
||||
export function autoRemoveMaps(floorId) {
|
||||
if (main.mode != 'play' || !inAnyPartition(floorId)) return;
|
||||
// 根据分区信息自动砍层与恢复
|
||||
(core.floorPartitions || []).forEach(floor => {
|
||||
var fromIndex = core.floorIds.indexOf(floor[0]);
|
||||
var toIndex = core.floorIds.indexOf(floor[1]);
|
||||
var index = core.floorIds.indexOf(floorId);
|
||||
if (fromIndex < 0 || index < 0) return;
|
||||
if (toIndex < 0) toIndex = core.floorIds.length - 1;
|
||||
if (index >= fromIndex && index <= toIndex) {
|
||||
core.plugin.removeMap.resumeMaps(
|
||||
core.floorIds[fromIndex],
|
||||
core.floorIds[toIndex]
|
||||
);
|
||||
} else {
|
||||
removeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
core.plugin.removeMap = {
|
||||
removeMaps,
|
||||
deleteFlags,
|
||||
resumeMaps,
|
||||
autoRemoveMaps
|
||||
};
|
||||
@@ -0,0 +1,121 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
import { studySkill, canStudySkill } from './study.js';
|
||||
|
||||
const replayableSettings = ['autoSkill'];
|
||||
|
||||
let cliping = false;
|
||||
let startIndex = 0;
|
||||
|
||||
export function ready() {}
|
||||
|
||||
export function readyClip() {
|
||||
cliping = true;
|
||||
return (startIndex = core.status.route.length - 1);
|
||||
}
|
||||
|
||||
export function clip(...replace) {
|
||||
if (!cliping) return;
|
||||
cliping = false;
|
||||
|
||||
core.status.route.splice(startIndex);
|
||||
core.status.route.push(...replace);
|
||||
}
|
||||
|
||||
// 注册修改设置的录像操作
|
||||
core.registerReplayAction('settings', name => {
|
||||
if (!name.startsWith('set:')) return false;
|
||||
const [, setting, value] = name.split(':');
|
||||
const v = eval(value);
|
||||
if (typeof v !== 'boolean') return false;
|
||||
if (!replayableSettings.includes(setting)) return false;
|
||||
flags[setting] = v;
|
||||
core.status.route.push(name);
|
||||
core.replay();
|
||||
return true;
|
||||
});
|
||||
|
||||
core.registerReplayAction('upgradeSkill', name => {
|
||||
if (!name.startsWith('skill:')) return false;
|
||||
const skill = parseInt(name.slice(6));
|
||||
core.plugin.skillTree.upgradeSkill(skill);
|
||||
core.status.route.push(name);
|
||||
core.replay();
|
||||
return true;
|
||||
});
|
||||
|
||||
core.registerReplayAction('study', name => {
|
||||
if (!name.startsWith('study:')) return false;
|
||||
const [num, x, y] = name
|
||||
.slice(6)
|
||||
.split(',')
|
||||
.map(v => parseInt(v));
|
||||
if (!canStudySkill(num)) return false;
|
||||
const id = core.getBlockId(x, y);
|
||||
const enemy = core.getEnemyInfo(id, void 0, x, y);
|
||||
if (!enemy.special.includes(num)) return false;
|
||||
studySkill(enemy, num);
|
||||
core.status.route.push(name);
|
||||
core.replay();
|
||||
return true;
|
||||
});
|
||||
|
||||
// 商店
|
||||
let shopOpened = false;
|
||||
let openedShopId = '';
|
||||
core.registerReplayAction('openShop', name => {
|
||||
if (!name.startsWith('openShop:')) return false;
|
||||
if (shopOpened) return false;
|
||||
openedShopId = name.slice(9);
|
||||
shopOpened = true;
|
||||
core.status.route.push(name);
|
||||
core.replay();
|
||||
return true;
|
||||
});
|
||||
|
||||
core.registerReplayAction('buy', name => {
|
||||
if (!name.startsWith('buy:') && !name.startsWith('sell:')) return false;
|
||||
if (!shopOpened) return false;
|
||||
if (!openedShopId) return false;
|
||||
const [type, id, num] = name
|
||||
.split(':')
|
||||
.map(v => (/^\d+$/.test(v) ? parseInt(v) : v));
|
||||
const shop = core.status.shops[openedShopId];
|
||||
const item = shop.choices.find(v => v.id === id);
|
||||
if (!item) return false;
|
||||
flags.itemShop ??= {};
|
||||
flags.itemShop[openedShopId] ??= {};
|
||||
flags.itemShop[openedShopId][id] ??= 0;
|
||||
if (num > item.number - flags.itemShop[openedShopId][id]) {
|
||||
return false;
|
||||
}
|
||||
let cost = 0;
|
||||
if (type === 'buy') {
|
||||
cost = item.money * num;
|
||||
} else {
|
||||
cost = -item.sell * num;
|
||||
}
|
||||
if (cost > core.status.hero.money) return false;
|
||||
core.status.hero.money -= cost;
|
||||
flags.itemShop[openedShopId][id] += type === 'buy' ? num : -num;
|
||||
core.addItem(id, type === 'buy' ? num : -num);
|
||||
core.status.route.push(name);
|
||||
core.replay();
|
||||
return true;
|
||||
});
|
||||
|
||||
core.registerReplayAction('closeShop', name => {
|
||||
if (name !== 'closeShop') return false;
|
||||
if (!shopOpened) return false;
|
||||
shopOpened = false;
|
||||
openedShopId = '';
|
||||
core.status.route.push(name);
|
||||
core.replay();
|
||||
return true;
|
||||
});
|
||||
|
||||
core.plugin.replay = {
|
||||
ready,
|
||||
readyClip,
|
||||
clip
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
const { openItemShop } = core.plugin.gameUi;
|
||||
|
||||
export function openShop(shopId, noRoute) {
|
||||
var shop = core.status.shops[shopId];
|
||||
// Step 1: 检查能否打开此商店
|
||||
if (!this.canOpenShop(shopId)) {
|
||||
core.drawTip('该商店尚未开启');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3: 检查道具商店 or 公共事件
|
||||
if (shop.item) {
|
||||
if (openItemShop) openItemShop(shopId);
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 是否访问过某个快捷商店
|
||||
export function isShopVisited(id) {
|
||||
flags.__shops__ ??= {};
|
||||
var shops = core.getFlag('__shops__');
|
||||
if (!shops[id]) shops[id] = {};
|
||||
return shops[id].visited;
|
||||
}
|
||||
|
||||
/// 当前应当显示的快捷商店列表
|
||||
export function listShopIds() {
|
||||
return Object.keys(core.status.shops).filter(id => {
|
||||
return (
|
||||
core.plugin.shop.isShopVisited(id) ||
|
||||
!core.status.shops[id].mustEnable
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 是否能够打开某个商店
|
||||
export function canOpenShop(id) {
|
||||
if (this.isShopVisited(id)) return true;
|
||||
var shop = core.status.shops[id];
|
||||
if (shop.item || shop.commonEvent || shop.mustEnable) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 启用或禁用某个快捷商店
|
||||
export function setShopVisited(id, visited) {
|
||||
if (!core.hasFlag('__shops__')) core.setFlag('__shops__', {});
|
||||
var shops = core.getFlag('__shops__');
|
||||
if (!shops[id]) shops[id] = {};
|
||||
if (visited) shops[id].visited = true;
|
||||
else delete shops[id].visited;
|
||||
}
|
||||
|
||||
/// 能否使用快捷商店
|
||||
export function canUseQuickShop() {
|
||||
// 如果返回一个字符串,表示不能,字符串为不能使用的提示
|
||||
// 返回null代表可以使用
|
||||
|
||||
// 检查当前楼层的canUseQuickShop选项是否为false
|
||||
if (core.status.thisMap.canUseQuickShop === false)
|
||||
return '当前楼层不能使用快捷商店。';
|
||||
return null;
|
||||
}
|
||||
|
||||
core.plugin.shop = {
|
||||
openShop,
|
||||
isShopVisited,
|
||||
listShopIds,
|
||||
canOpenShop,
|
||||
setShopVisited,
|
||||
canUseQuickShop
|
||||
};
|
||||
@@ -0,0 +1,318 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
/**
|
||||
* @type {number[]}
|
||||
*/
|
||||
let levels = [];
|
||||
|
||||
/**
|
||||
* @type {Record<Chapter, Skill[]>}
|
||||
*/
|
||||
const skills = {
|
||||
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%']
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
core.plugin.skills = skills;
|
||||
|
||||
export function resetSkillLevel() {
|
||||
levels = [];
|
||||
}
|
||||
|
||||
export function getSkillFromIndex(index) {
|
||||
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) {
|
||||
return (levels[skill] ??= 0);
|
||||
}
|
||||
|
||||
export function getSkillConsume(skill) {
|
||||
return eval(
|
||||
getSkillFromIndex(skill).consume.replace(/level(:\d+)?/g, (str, $1) => {
|
||||
if ($1) return `core.plugin.skillTree.getSkillLevel(${$1})`;
|
||||
else return `core.plugin.skillTree.getSkillLevel(${skill})`;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function openTree() {
|
||||
if (main.replayChecking) return;
|
||||
core.plugin.skillTreeOpened.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 能否升级某个技能
|
||||
* @param {number} skill
|
||||
*/
|
||||
export function canUpgrade(skill) {
|
||||
const consume = core.plugin.skillTree.getSkillConsume(skill);
|
||||
if (consume > core.status.hero.mdef) return false;
|
||||
const level = core.plugin.skillTree.getSkillLevel(skill);
|
||||
const s = getSkillFromIndex(skill);
|
||||
if (level === s.max) return false;
|
||||
const front = s.front;
|
||||
for (const [skill, level] of front) {
|
||||
if (core.plugin.skillTree.getSkillLevel(skill) < level) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际升级效果
|
||||
* @param {number} skill
|
||||
*/
|
||||
export function upgradeSkill(skill) {
|
||||
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) {
|
||||
levels = data ?? [];
|
||||
}
|
||||
|
||||
core.plugin.skillTree = {
|
||||
getSkillConsume,
|
||||
getSkillFromIndex,
|
||||
getSkillLevel,
|
||||
saveSkillTree,
|
||||
loadSkillTree,
|
||||
upgradeSkill,
|
||||
openTree,
|
||||
resetSkillLevel
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
// 所有的主动技能效果
|
||||
var ignoreInJump = {
|
||||
event: ['X20007', 'X20001', 'X20006', 'X20014', 'X20010', 'X20007'],
|
||||
bg: [
|
||||
'X20037',
|
||||
'X20038',
|
||||
'X20039',
|
||||
'X20045',
|
||||
'X20047',
|
||||
'X20053',
|
||||
'X20054',
|
||||
'X20055',
|
||||
'X20067',
|
||||
'X20068',
|
||||
'X20075',
|
||||
'X20076'
|
||||
]
|
||||
};
|
||||
|
||||
/** @type {FloorIds[]} */
|
||||
export const jumpIgnoreFloor = [
|
||||
'MT31',
|
||||
'snowTown',
|
||||
'MT36',
|
||||
'MT37',
|
||||
'MT38',
|
||||
'MT39',
|
||||
'MT40',
|
||||
'MT42',
|
||||
'MT43',
|
||||
'MT44',
|
||||
'MT45',
|
||||
'MT46',
|
||||
'MT47',
|
||||
'MT48'
|
||||
];
|
||||
// 跳跃
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
core.plugin.skillEffects = {
|
||||
jumpSkill,
|
||||
jumpIgnoreFloor
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
// 负责勇士技能:学习
|
||||
const values = {
|
||||
1: ['crit'],
|
||||
6: ['n'],
|
||||
7: ['hungry'],
|
||||
8: ['together'],
|
||||
10: ['courage'],
|
||||
11: ['charge']
|
||||
};
|
||||
|
||||
const cannotStudy = [9, 12, 14, 15, 24];
|
||||
|
||||
export function canStudySkill(number) {
|
||||
const s = (core.status.hero.special ??= { num: [], last: [] });
|
||||
if (core.plugin.skillTree.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, number) {
|
||||
core.status.hero.special ??= { num: [], last: [] };
|
||||
const s = core.status.hero.special;
|
||||
const specials = core.getSpecials();
|
||||
let special = specials[number - 1][1];
|
||||
if (special instanceof Function) special = special(enemy);
|
||||
if (!canStudySkill(number)) {
|
||||
if (!main.replayChecking) {
|
||||
core.tip('error', `无法学习${special}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
s.num.push(number);
|
||||
s.last.push(core.plugin.skillTree.getSkillLevel(11) * 3 + 2);
|
||||
const value = values[number] ?? [];
|
||||
for (const key of value) {
|
||||
s[key] = enemy[key];
|
||||
}
|
||||
}
|
||||
|
||||
export function forgetStudiedSkill(num, i) {
|
||||
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[number] ?? [];
|
||||
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) {
|
||||
this.forgetStudiedSkill(void 0, i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
core.plugin.study = {
|
||||
canStudySkill,
|
||||
studySkill,
|
||||
forgetStudiedSkill,
|
||||
declineStudiedSkill,
|
||||
checkStudiedSkill
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,100 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
export {};
|
||||
|
||||
(function () {
|
||||
if (main.replayChecking)
|
||||
return (core.plugin.gameUi = {
|
||||
openItemShop: () => 0,
|
||||
showChapter: () => 0,
|
||||
openSkill: () => 0
|
||||
});
|
||||
|
||||
function openItemShop(itemShopId) {
|
||||
if (!core.isReplaying()) {
|
||||
core.plugin.openedShopId = itemShopId;
|
||||
core.plugin.shopOpened.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
function updateVueStatusBar() {
|
||||
if (main.replayChecking) return;
|
||||
core.plugin.statusBarStatus.value = !core.plugin.statusBarStatus.value;
|
||||
core.checkMarkedEnemy();
|
||||
}
|
||||
|
||||
ui.prototype.drawBook = function () {
|
||||
if (!core.isReplaying()) return (core.plugin.bookOpened.value = true);
|
||||
};
|
||||
|
||||
ui.prototype._drawToolbox = function () {
|
||||
if (!core.isReplaying()) return (core.plugin.toolOpened.value = true);
|
||||
};
|
||||
|
||||
ui.prototype._drawEquipbox = function () {
|
||||
if (!core.isReplaying()) return (core.plugin.equipOpened.value = true);
|
||||
};
|
||||
|
||||
ui.prototype.drawFly = function () {
|
||||
if (!core.isReplaying()) return (core.plugin.flyOpened.value = true);
|
||||
};
|
||||
|
||||
control.prototype.updateStatusBar_update = function () {
|
||||
core.control.updateNextFrame = false;
|
||||
if (!core.isPlaying() || core.hasFlag('__statistics__')) return;
|
||||
core.control.controldata.updateStatusBar();
|
||||
if (!core.control.noAutoEvents) core.checkAutoEvents();
|
||||
core.control._updateStatusBar_setToolboxIcon();
|
||||
core.clearRouteFolding();
|
||||
core.control.noAutoEvents = true;
|
||||
// 更新vue状态栏
|
||||
updateVueStatusBar();
|
||||
};
|
||||
|
||||
control.prototype.showStatusBar = function () {
|
||||
if (main.mode == 'editor') return;
|
||||
core.removeFlag('hideStatusBar');
|
||||
core.plugin.showStatusBar.value = true;
|
||||
core.dom.tools.hard.style.display = 'block';
|
||||
core.dom.toolBar.style.display = 'block';
|
||||
};
|
||||
|
||||
control.prototype.hideStatusBar = function (showToolbox) {
|
||||
if (main.mode == 'editor') return;
|
||||
|
||||
// 如果原本就是隐藏的,则先显示
|
||||
if (!core.domStyle.showStatusBar) this.showStatusBar();
|
||||
if (core.isReplaying()) showToolbox = true;
|
||||
core.plugin.showStatusBar.value = false;
|
||||
|
||||
var toolItems = core.dom.tools;
|
||||
core.setFlag('hideStatusBar', true);
|
||||
core.setFlag('showToolbox', showToolbox || null);
|
||||
if (
|
||||
(!core.domStyle.isVertical && !core.flags.extendToolbar) ||
|
||||
!showToolbox
|
||||
) {
|
||||
for (var i = 0; i < toolItems.length; ++i)
|
||||
toolItems[i].style.display = 'none';
|
||||
}
|
||||
if (!core.domStyle.isVertical && !core.flags.extendToolbar) {
|
||||
core.dom.toolBar.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
function showChapter(chapter) {
|
||||
if (core.isReplaying()) return;
|
||||
core.plugin.chapterContent.value = chapter;
|
||||
core.plugin.chapterShowed.value = true;
|
||||
}
|
||||
|
||||
function openSkill() {
|
||||
if (core.isReplaying()) return;
|
||||
core.plugin.skillOpened.value = true;
|
||||
}
|
||||
|
||||
core.plugin.gameUi = {
|
||||
openItemShop,
|
||||
openSkill,
|
||||
showChapter
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,54 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
|
||||
/**
|
||||
* 滑动数组
|
||||
* @param {any[]} arr
|
||||
* @param {number} delta
|
||||
*/
|
||||
export function slide(arr, delta) {
|
||||
if (delta === 0) return arr;
|
||||
delta %= arr.length;
|
||||
if (delta > 0) {
|
||||
arr.unshift(...arr.splice(arr.length - delta, delta));
|
||||
return arr;
|
||||
}
|
||||
if (delta < 0) {
|
||||
arr.push(...arr.splice(0, -delta));
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
export function backDir(dir) {
|
||||
return {
|
||||
up: 'down',
|
||||
down: 'up',
|
||||
left: 'right',
|
||||
right: 'left'
|
||||
}[dir];
|
||||
}
|
||||
|
||||
export function has(v) {
|
||||
return v !== null && v !== void 0;
|
||||
}
|
||||
|
||||
export function maxGameScale(n = 0) {
|
||||
const index = core.domStyle.availableScale.indexOf(core.domStyle.scale);
|
||||
core.control.setDisplayScale(
|
||||
core.domStyle.availableScale.length - 1 - index - n
|
||||
);
|
||||
if (!core.isPlaying() && core.flags.enableHDCanvas) {
|
||||
core.domStyle.ratio = Math.max(
|
||||
window.devicePixelRatio || 1,
|
||||
core.domStyle.scale
|
||||
);
|
||||
core.resize();
|
||||
}
|
||||
}
|
||||
|
||||
core.plugin.utils = {
|
||||
slide,
|
||||
backDir,
|
||||
has,
|
||||
maxGameScale
|
||||
};
|
||||
core.has = has;
|
||||
+1
-3
@@ -92,7 +92,7 @@ export function useDrag(
|
||||
target.addEventListener('mouseup', mouseUp as EventListener);
|
||||
target.addEventListener('touchend', touchUp as EventListener);
|
||||
}
|
||||
dragFnMap.set(fn, [mouseFn, touchFn, mouseUp, touchUp]);
|
||||
dragFnMap.set(fn, [mouseUp, touchUp]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,8 +103,6 @@ export function cancelGlobalDrag(fn: DragFn): void {
|
||||
const fns = dragFnMap.get(fn);
|
||||
dragFnMap.delete(fn);
|
||||
if (!fns) return;
|
||||
document.removeEventListener('mousemove', fns[0]);
|
||||
document.removeEventListener('touchmove', fns[1]);
|
||||
document.removeEventListener('mouseup', fns[0]);
|
||||
document.removeEventListener('touchend', fns[1]);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user