插件完全ts化

This commit is contained in:
AncTe committed 2023-05-02 15:37:38 +08:00
1 parent b2cb89a7f6
commit 2e7e64b399
32 files changed
+147 -112

No files matched your search

+23
View File
@@ -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
};
+9
View File
@@ -0,0 +1,9 @@
///<reference path="../../../src/types/core.d.ts" />
export class EnemyCollection {}
export class Enemy {}
core.plugin.damage = {
Enemy
};
+198
View File
@@ -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)
};
};
+54
View File
@@ -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
};
+84
View File
@@ -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
};
+58
View File
@@ -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;
};
+226
View File
@@ -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);
}
})();
})();
+36
View File
@@ -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
};
+128
View File
@@ -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++;
}
}
+230
View File
@@ -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
};
+121
View File
@@ -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);
}
}
+72
View File
@@ -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
};
+115
View File
@@ -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
};
+121
View File
@@ -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
};
+74
View File
@@ -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
};
+318
View File
@@ -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
};
+183
View File
@@ -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
};
+77
View File
@@ -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
+100
View File
@@ -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
};
})();
+54
View File
@@ -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
View File
@@ -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]);
}