插件结构改造

This commit is contained in:
unanmed 2023-02-28 17:49:34 +08:00
parent 8ab10d5612
commit 023d913ea7
53 changed files with 9640 additions and 9816 deletions

View File

@ -1,3 +1,4 @@
vite.config.ts
public/project/data.js
story.md
story.md
public/project/floors/*.js

View File

@ -11,7 +11,8 @@
"preview": "vite preview",
"preview-node": "cd dist && node server.cjs",
"update": "ts-node-esm script/update.ts",
"declare": "ts-node-esm script/declare.ts"
"declare": "ts-node-esm script/declare.ts",
"type": "vue-tsc --noEmit"
},
"dependencies": {
"@ant-design/icons-vue": "^6.1.0",

File diff suppressed because it is too large Load Diff

View File

@ -3850,7 +3850,7 @@ isShopVisited_e
/* isShopVisited_e
default : ['shop1']
allShops : ['IdString_0']
var code = 'core.isShopVisited(\'' + IdString_0 + '\')';
var code = 'core.plugin.shop.isShopVisited(\'' + IdString_0 + '\')';
return [code, Blockly.JavaScript.ORDER_ATOMIC];
*/;

View File

@ -13,74 +13,8 @@ var plugins_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string'",
"_data": "自定义插件"
"_data": "双击查看插件详情"
},
"shop": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string'",
"_data": "全局商店"
},
"drawLight": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "灯光效果"
},
"removeMap": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "砍层插件"
},
"fiveLayers": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "五图层(背景前景2)"
},
"itemShop": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "道具商店"
},
"enemyLevel": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "手册显示怪物境界"
},
"dynamicHp": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "动态血量变化"
},
"multiHeros": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "多角色"
},
"itemCategory": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "物品分类插件"
},
"heroFourFrames": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "勇士四帧行走动画"
},
"startCanvas": {
"_leaf": true,
"_type": "textarea",
"_range": "typeof(thiseval)=='string' || thiseval==null",
"_data": "自绘标题界面居中"
}
}
if (obj[key]) return obj[key];
return {

View File

@ -1623,7 +1623,7 @@ actions.prototype._keyUpViewMaps = function (keycode) {
////// 快捷商店界面时的点击操作 //////
actions.prototype._clickQuickShop = function (x, y) {
var shopIds = core.listShopIds();
var shopIds = core.plugin.shop.listShopIds();
if (this._out(x)) return;
var topIndex =
this._HY_ -
@ -1631,15 +1631,15 @@ actions.prototype._clickQuickShop = function (x, y) {
(core.status.event.ui.offset || 0);
if (y >= topIndex && y < topIndex + shopIds.length) {
var shopId = shopIds[y - topIndex];
if (!core.canOpenShop(shopId)) {
if (!core.plugin.shop.canOpenShop(shopId)) {
core.playSound('操作失败');
core.drawTip('当前项尚未开启');
return;
}
var message = core.canUseQuickShop(shopId);
var message = core.plugin.shop.canUseQuickShop(shopId);
if (message == null) {
// core.ui.closePanel();
core.openShop(shopIds[y - topIndex], false);
core.plugin.shop.openShop(shopIds[y - topIndex], false);
} else {
core.playSound('操作失败');
core.drawTip(message);
@ -1660,7 +1660,7 @@ actions.prototype._keyUpQuickShop = function (keycode) {
return;
}
this._selectChoices(
core.listShopIds().length + 1,
core.plugin.shop.listShopIds().length + 1,
keycode,
this._clickQuickShop
);

View File

@ -1499,7 +1499,7 @@ control.prototype._checkBlock_disableQuickShop = function () {
// 禁用快捷商店
if (core.flags.disableShopOnDamage) {
Object.keys(core.status.shops).forEach(function (shopId) {
core.setShopVisited(shopId, false);
core.plugin.shop.setShopVisited(shopId, false);
});
}
};
@ -1695,7 +1695,7 @@ control.prototype.drawDamage = function (ctx) {
control.prototype._drawDamage_draw = function (ctx, onMap) {
if (!core.hasItem('book')) return;
core.drawHalo(ctx, onMap);
core.plugin.halo.drawHalo(ctx, onMap);
core.setFont(ctx, "14px 'normal'");
core.setTextAlign(ctx, 'left');
@ -3058,7 +3058,7 @@ control.prototype.getRealStatus = function (name) {
////// 从status中获得实际属性增幅后的如果不存在则从勇士属性中获取 //////
control.prototype.getRealStatusOrDefault = function (status, name) {
return core.getHeroStatusOf(status, name);
return core.plugin.hero.getHeroStatusOf(status, name);
};
////// 获得勇士原始属性(无装备和衰弱影响) //////

View File

@ -6,11 +6,6 @@
'use strict';
// /**
// * @type {CoreMixin}
// */
// const core = (() => {
function core() {
this._WIDTH_ = 15;
this._HEIGHT_ = 15;
@ -613,6 +608,24 @@ core.prototype._init_plugins = function () {
}
}
if (!main.pluginUseCompress) {
(async function () {
const pluginList = main.plugin;
for await (const one of pluginList) {
const script = document.createElement('script');
script.src = `project/plugin/${one}.js`;
document.body.appendChild(script);
await new Promise(res => {
script.addEventListener('load', res);
});
}
})();
} else {
const script = document.createElement('script');
script.src = `project/plugin.min.js`;
document.body.appendChild(script);
}
core._forwardFunc('plugin');
if (!main.replayChecking && main.mode === 'play') {
main.forward();
@ -682,7 +695,4 @@ core.prototype.doFunc = function (func, _this) {
return func.apply(_this, Array.prototype.slice.call(arguments, 2));
};
// return new Core();
// })();
var core = new core();

View File

@ -2206,13 +2206,13 @@ events.prototype._action_unloadEquip = function (data, x, y, prefix) {
};
events.prototype._action_openShop = function (data, x, y, prefix) {
core.setShopVisited(data.id, true);
if (data.open) core.openShop(data.id, true);
core.plugin.shop.setShopVisited(data.id, true);
if (data.open) core.plugin.shop.openShop(data.id, true);
core.doAction();
};
events.prototype._action_disableShop = function (data, x, y, prefix) {
core.setShopVisited(data.id, false);
core.plugin.shop.setShopVisited(data.id, false);
core.doAction();
};
@ -3436,18 +3436,18 @@ events.prototype.openQuickShop = function (fromUserAction) {
if (Object.keys(core.status.shops).length == 1) {
var shopId = Object.keys(core.status.shops)[0];
if (core.status.event.id != null) return;
if (!core.canOpenShop(shopId)) {
if (!core.plugin.shop.canOpenShop(shopId)) {
core.playSound('操作失败');
core.drawTip('当前无法打开快捷商店!');
return;
}
var message = core.canUseQuickShop(shopId);
var message = core.plugin.shop.canUseQuickShop(shopId);
if (message != null) {
core.playSound('操作失败');
core.drawTip(message);
return;
}
core.openShop(shopId, false);
core.plugin.shop.openShop(shopId, false);
return;
}

View File

@ -3101,11 +3101,11 @@ ui.prototype._drawNotes = function () {
ui.prototype._drawQuickShop = function () {
core.status.event.id = 'selectShop';
var shopList = core.status.shops,
keys = core.listShopIds();
keys = core.plugin.shop.listShopIds();
var choices = keys.map(function (shopId) {
return {
text: shopList[shopId].textInList,
color: core.isShopVisited(shopId) ? null : '#999999'
color: core.plugin.shop.isShopVisited(shopId) ? null : '#999999'
};
});
choices.push('返回游戏');

View File

@ -314,7 +314,25 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"font": "normal"
},
"splitImages": [],
"plugin": []
"plugin": [
"utils",
"ui",
"shop",
"study",
"hero",
"fiveLayer",
"loopMap",
"removeMap",
"heroFourFrames",
"itemDetail",
"skills",
"towerBoss",
"popup",
"hotReload",
"replay",
"skillTree",
"halo"
]
},
"firstData": {
"title": "人类:开天辟地",

View File

@ -35,7 +35,7 @@ main.floors.MT0=
"\r[red]注意!!!\r[]该塔新增了很多新的功能同时对样板的ui进行了大幅度的改动操作也有改变由于内容过多这里不再一一描述具体请在道具栏查看百科全书百科全书是在你面前的几个道具中的其中一个",
{
"type": "function",
"function": "function(){\ncore.showChapter('序章 起源');\n}"
"function": "function(){\ncore.plugin.gameUi.showChapter('序章 起源');\n}"
}
],
"parallelDo": "",

View File

@ -136,7 +136,7 @@ main.floors.MT16=
},
{
"type": "function",
"function": "function(){\ncore.autoFixRouteBoss(true);\n}"
"function": "function(){\ncore.plugin.towerBoss.autoFixRouteBoss(true);\n}"
}
]
},
@ -413,7 +413,7 @@ main.floors.MT16=
"这里是漏怪检测,将会检测\r[gold]洞穴、山路、山脚、平原\r[white]地区的怪物是否清完",
{
"type": "function",
"function": "function(){\nconst enemy = core.getRemainEnemyString(core.floorIds.slice(5, 17));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
"function": "function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString(core.floorIds.slice(5, 17));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
},
{
"type": "loadBgm",

View File

@ -118,7 +118,7 @@ main.floors.MT21 = {
{
type: 'function',
function:
"function(){\ncore.showChapter('第二章 智慧');\ncore.removeMaps('tower1', 'tower7', true);\ndelete flags.tower1;\ndelete flags.wordsTimeOut;\ndelete flags.boom;\ndelete flags.booming;\n}"
"function(){\ncore.plugin.gameUi.showChapter('第二章 智慧');\ncore.plugin.removeMap.removeMaps('tower1', 'tower7', true);\ndelete flags.tower1;\ndelete flags.wordsTimeOut;\ndelete flags.boom;\ndelete flags.booming;\n}"
},
{
type: 'setValue',

View File

@ -119,7 +119,7 @@ main.floors.MT31 = {
{
type: 'function',
function:
'function(){\nconst enemy = core.getRemainEnemyString(core.floorIds.slice(17, 22));\nif (enemy.length === 0) {\n\tcore.insertAction([\'当前无剩余怪物!\', { "type": "hide", "remove": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}'
'function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString(core.floorIds.slice(17, 22));\nif (enemy.length === 0) {\n\tcore.insertAction([\'当前无剩余怪物!\', { "type": "hide", "remove": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}'
}
]
},

View File

@ -16,7 +16,7 @@ main.floors.MT32=
"firstArrive": [
{
"type": "function",
"function": "function(){\ncore.removeMaps('MT17', 'MT21', true)\n}"
"function": "function(){\ncore.plugin.removeMap.removeMaps('MT17', 'MT21', true)\n}"
}
],
"eachArrive": [],

View File

@ -76,7 +76,7 @@ main.floors.MT35=
"这里是漏怪检测,会检测\r[gold]智慧小径\r[]区域是否有遗漏怪物",
{
"type": "function",
"function": "function(){\nconst enemy = core.getRemainEnemyString(core.floorIds.slice(30, 40));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
"function": "function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString(core.floorIds.slice(30, 40));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
}
],
"7,0": [
@ -101,7 +101,7 @@ main.floors.MT35=
},
{
"type": "function",
"function": "function(){\ncore.removeMaps('MT22', 'MT31', true);\n}"
"function": "function(){\ncore.plugin.removeMap.removeMaps('MT22', 'MT31', true);\n}"
},
{
"type": "changeFloor",

View File

@ -120,7 +120,7 @@ main.floors.MT5=
"这里是漏怪检测,会检测\r[gold]山洞\r[]区域的怪物是否清空",
{
"type": "function",
"function": "function(){\nconst enemy = core.getRemainEnemyString(core.floorIds.slice(0, 5));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
"function": "function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString(core.floorIds.slice(0, 5));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
}
]
},

View File

@ -63,7 +63,7 @@ main.floors.MT6=
"\t[原始人]\b[down,hero]感觉好像可以学习一些简单的东西了。",
{
"type": "function",
"function": "function(){\ncore.showChapter('第一章 勇气');\n}"
"function": "function(){\ncore.plugin.gameUi.showChapter('第一章 勇气');\n}"
},
{
"type": "setValue",

View File

@ -21,7 +21,7 @@ main.floors.tower5=
"这里是漏怪检测,会检测\r[gold]智慧之塔\r[]区域是否有遗漏怪物",
{
"type": "function",
"function": "function(){\nconst enemy = core.getRemainEnemyString([\"tower1\", \"tower2\", \"tower3\", \"tower4\", \"tower5\", \"tower6\"]);\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
"function": "function(){\nconst enemy = core.plugin.remainEnemy.getRemainEnemyString([\"tower1\", \"tower2\", \"tower3\", \"tower4\", \"tower5\", \"tower6\"]);\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
}
]
},

View File

@ -157,7 +157,7 @@ main.floors.tower7=
},
{
"type": "function",
"function": "function(){\ncore.initTowerBoss();\n}"
"function": "function(){\ncore.plugin.towerBoss.initTowerBoss();\n}"
}
],
"eachArrive": [],

View File

@ -120,6 +120,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
// 正在切换楼层过程中执行的操作;此函数的执行时间是“屏幕完全变黑“的那一刻
// floorId为要切换到的楼层IDheroLoc表示勇士切换到的位置
const { checkLoopMap } = core.plugin.loopMap;
flags.floorChanging = true;
// ---------- 此时还没有进行切换当前floorId还是原来的 ---------- //
@ -136,7 +138,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
}
// 根据分区信息自动砍层与恢复
if (core.autoRemoveMaps) core.autoRemoveMaps(floorId);
if (core.plugin.removeMap.autoRemoveMaps)
core.plugin.removeMap.autoRemoveMaps(floorId);
// 重置画布尺寸
core.maps.resizeMap(floorId);
@ -177,7 +180,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
if (weather) core.setWeather(weather[0], weather[1]);
else core.setWeather();
core.checkLoopMap();
checkLoopMap();
// ...可以新增一些其他内容,比如创建个画布在右上角显示什么内容等等
},
@ -365,8 +368,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
flags[`night_${floorId}`] += enemy.day;
}
if (core.getSkillLevel(11) > 0) {
core.declineStudiedSkill();
if (core.plugin.skillTree.getSkillLevel(11) > 0) {
core.plugin.study.declineStudiedSkill();
}
// 如果是融化怪,需要特殊标记一下
@ -618,7 +621,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
core.formatBigNumber(
Math.max(
(enemy.value || 0) -
core.getHeroStatusOn('def')
core.plugin.hero.getHeroStatusOn('def')
)
) +
'点伤害'
@ -740,7 +743,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
def: hero_def,
mdef: hero_mdef,
hp: hero_hp
} = core.getHeroStatusOf(
} = core.plugin.hero.getHeroStatusOf(
hero,
['atk', 'def', 'mdef', 'hp'],
hero?.x,
@ -919,6 +922,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
// 后面三个参数主要是可以在光环等效果上可以适用
floorId = floorId || core.status.floorId;
const { backDir } = core.plugin.utils;
// 勇士位置应该在这里进行计算,四个位置依次遍历,去重
let toMap = [];
if (
@ -947,12 +952,12 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
}
if (
core.noPass(nx, ny) ||
!core.canMoveHero(nx, ny, core.backDir(dir), floorId)
!core.canMoveHero(nx, ny, backDir(dir), floorId)
) {
continue;
}
const toGet = ['atk', 'def'];
const status = core.getHeroStatusOf(
const status = core.plugin.hero.getHeroStatusOf(
hero,
toGet,
nx,
@ -982,13 +987,14 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
def: mon_def,
special: mon_special
} = enemyInfo;
let { atk: hero_atk, def: hero_def } = core.getHeroStatusOf(
hero,
['atk', 'def'],
x,
y,
floorId
);
let { atk: hero_atk, def: hero_def } =
core.plugin.hero.getHeroStatusOf(
hero,
['atk', 'def'],
x,
y,
floorId
);
let hero_hp = core.getRealStatusOrDefault(hero, 'hp'),
hero_IQ = core.getRealStatusOrDefault(hero, 'mdef'),
@ -1277,7 +1283,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
!core.status.floorId.startsWith('tower') &&
flags.skill2
) {
core.jumpSkill();
core.plugin.skillEffects.jumpSkill();
core.status.route.push('key:50'); // 将按键记在录像中
} else {
if (core.hasItem('pickaxe')) {
@ -1332,7 +1338,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
version: core.firstData.version,
guid: core.getGuid(),
time: new Date().getTime(),
skills: core.saveSkillTree()
skills: core.plugin.skillTree.saveSkillTree()
};
return data;
@ -1381,7 +1387,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
core.setFlag('__fromLoad__', true);
// TODO增加自己的一些读档处理
core.loadSkillTree(data.skills);
core.plugin.skillTree.loadSkillTree(data.skills);
// 切换到对应的楼层
core.changeFloor(data.floorId, null, data.hero.loc, 0, function () {
@ -1452,7 +1458,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
// 已学习的技能
if (
core.getSkillLevel(11) > 0 &&
core.plugin.skillTree.getSkillLevel(11) > 0 &&
(core.status.hero.special?.num ?? []).length > 0
) {
core.plugin.showStudiedSkill.value = true;
@ -1733,6 +1739,8 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
// 2, 将楼层属性中的cannotMoveDirectly这个开关勾上即禁止在该层楼使用瞬移。
// 3. 将flag:cannotMoveDirectly置为true即可使用flag控制在某段剧情范围内禁止瞬移。
const { checkLoopMap } = core.plugin.loopMap;
// 增加步数
core.status.hero.steps++;
// 更新跟随者状态,并绘制
@ -1779,7 +1787,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
);
}
core.checkLoopMap();
checkLoopMap();
// 追猎
if (core.status.checkBlock.haveHunt) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,207 @@
'use strict';
(function () {
// 创建新图层
function createCanvas(name, zIndex) {
if (!name) return;
var canvas = document.createElement('canvas');
canvas.id = name;
canvas.className = 'gameCanvas';
// 编辑器模式下设置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)
};
};
})();

View File

@ -0,0 +1,56 @@
'use strict';
(function () {
/**
* 绘制光环范围
* @param {CanvasRenderingContext2D} ctx
* @param {boolean} onMap
*/
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
};
})();

View File

@ -0,0 +1,86 @@
'use strict';
(function () {
/**
* 获取勇士在某一点的属性
* @param {keyof HeroStatus | 'all'} name
* @param {number} x
* @param {number} y
* @param {FloorIds} floorId
*/
function getHeroStatusOn(name, x, y, floorId) {
return this.getRealStatusOf(core.status.hero, name, x, y, floorId);
}
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
};
})();

View File

@ -0,0 +1,59 @@
'use strict';
(function () {
['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;
};
})();

View File

@ -0,0 +1,245 @@
'use strict';
(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(`/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(data) {
if (data === 'plugins') {
// 插件编写比较好办
const before = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1;
// 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了
const script = document.createElement('script');
script.src = `/project/plugins.js?v=${Date.now()}`;
document.body.appendChild(script);
await new Promise(res => {
script.onload = () => res('success');
});
const after = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1;
// 找到差异的函数
for (const id in before) {
const fn = before[id];
if (typeof fn !== 'function') continue;
if (fn.toString() !== after[id]?.toString()) {
try {
core.plugin[id] = after[id];
core.plugin[id].call(core.plugin);
core.updateStatusBar(true, true);
console.log(`plugin hot reload: ${id}`);
} catch (e) {
console.error(e);
}
}
}
} else if (data === 'functions') {
// 脚本编辑略微麻烦点
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);
}
}
}
}
}
}
/**
* 属性热重载包括全塔属性等
* @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);
});
}, 1000);
}
})();
})();

View File

@ -0,0 +1,129 @@
'use strict';
(function () {
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++;
}
}
})();

View File

@ -0,0 +1,239 @@
'use strict';
(function () {
const { slide } = core.plugin.utils;
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);
}
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
};
})();

View File

@ -0,0 +1,123 @@
'use strict';
(function () {
// 伤害弹出
// 复写阻激夹域检测
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'
].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);
}
}
})();

View File

@ -0,0 +1,75 @@
'use strict';
(function () {
/**
* 检查漏怪
* @param {FloorIds[]} floorIds
*/
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
*/
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
};
})();

View File

@ -0,0 +1,117 @@
'use strict';
(function () {
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();
}
}
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层
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;
};
// 分区砍层
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
};
})();

View File

@ -0,0 +1,90 @@
'use strict';
(function () {
const { studySkill, canStudySkill } = core.plugin.study;
const replayableSettings = ['autoSkill'];
// 注册修改设置的录像操作
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.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.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.replay();
return true;
});
// 商店
let shopOpened = false;
let openedShopId = '';
core.registerReplayAction('openShop', name => {
if (!name.startsWith('openShop:')) return false;
openedShopId = name.slice(9);
shopOpened = true;
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[id];
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.replay();
return true;
});
core.registerReplayAction('closeShop', name => {
if (name !== 'closeShop') return false;
if (!shopOpened) return false;
shopOpened = false;
openedShopId = '';
core.replay();
return true;
});
})();

View File

@ -0,0 +1,76 @@
'use strict';
(function () {
const { openItemShop } = core.plugin.gameUi;
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;
}
/// 是否访问过某个快捷商店
function isShopVisited(id) {
flags.__shops__ ??= {};
var shops = core.getFlag('__shops__');
if (!shops[id]) shops[id] = {};
return shops[id].visited;
}
/// 当前应当显示的快捷商店列表
function listShopIds() {
return Object.keys(core.status.shops).filter(id => {
return (
core.plugin.shop.isShopVisited(id) ||
!core.status.shops[id].mustEnable
);
});
}
/// 是否能够打开某个商店
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;
}
/// 启用或禁用某个快捷商店
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;
}
/// 能否使用快捷商店
function canUseQuickShop() {
// 如果返回一个字符串,表示不能,字符串为不能使用的提示
// 返回null代表可以使用
// 检查当前楼层的canUseQuickShop选项是否为false
if (core.status.thisMap.canUseQuickShop === false)
return '当前楼层不能使用快捷商店。';
return null;
}
core.plugin.shop = {
openShop,
isShopVisited,
listShopIds,
canOpenShop,
setShopVisited,
canUseQuickShop
};
})();

View File

@ -0,0 +1,319 @@
'use strict';
(function () {
/**
* @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>,战斗时智慧会充当等量护盾'
],
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: '500 * level + 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 * level ** 2 + 2500',
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;
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
*/
function getSkillLevel(skill) {
return (levels[skill] ??= 0);
}
function getSkillConsume(skill) {
return eval(
this.getSkillFromIndex(skill).consume.replace(
/level(:\d+)?/g,
(str, $1) => {
if ($1) return `core.plugin.skillTree.getSkillLevel(${$1})`;
else return `core.plugin.skillTree.getSkillLevel(${skill})`;
}
)
);
}
function openTree() {
if (main.replayChecking) return;
core.plugin.skillTreeOpened.value = true;
}
/**
* 能否升级某个技能
* @param {number} skill
*/
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
*/
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;
}
function saveSkillTree() {
return levels.slice();
}
function loadSkillTree(data) {
levels = data ?? [];
}
core.plugin.skillTree = {
getSkillConsume,
getSkillFromIndex,
getSkillLevel,
saveSkillTree,
loadSkillTree,
upgradeSkill,
openTree
};
})();

View File

@ -0,0 +1,181 @@
'use strict';
(function () {
// 所有的主动技能效果
var ignoreInJump = {
event: ['X20007', 'X20001', 'X20006', 'X20014', 'X20010', 'X20007'],
bg: [
'X20037',
'X20038',
'X20039',
'X20045',
'X20047',
'X20053',
'X20054',
'X20055',
'X20067',
'X20068',
'X20075',
'X20076'
]
};
/** @type {FloorIds[]} */
const jumpIgnoreFloor = ['MT31', 'snowTown'];
// 跳跃
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' &&
flags['jump_' + core.status.floorId] == 2 &&
!flags.MT14Jump
) {
if (
!(
core.status.hero.loc.x === 77 &&
core.status.hero.loc.y === 5 &&
core.status.hero.loc.direction === 'right'
)
) {
return core.drawTip('该地图还有一个必跳的地方,你还没有跳');
} else flags.MT14Jump = true;
}
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
};
})();

View File

@ -0,0 +1,79 @@
'use strict';
(function () {
// 负责勇士技能:学习
const values = {
1: ['crit'],
6: ['n'],
7: ['hungry'],
8: ['together'],
10: ['courage'],
11: ['charge']
};
const cannotStudy = [9, 12, 14, 15, 24];
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;
}
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];
}
}
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];
}
}
function declineStudiedSkill() {
const s = (core.status.hero.special ??= { num: [], last: [] });
s.last = s.last.map(v => v - 1);
}
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

View File

@ -0,0 +1,94 @@
'use strict';
(function () {
if (main.replayChecking) return (core.plugin.gameUi = {});
function openItemShop(itemShopId) {
if (!main.replayChecking) {
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
};
})();

View File

@ -1,51 +1,55 @@
/**
* 滑动数组
* @param {any[]} arr
* @param {number} delta
*/
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;
'use strict';
(function () {
/**
* 滑动数组
* @param {any[]} arr
* @param {number} delta
*/
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;
}
}
if (delta < 0) {
arr.push(...arr.splice(0, -delta));
return arr;
function backDir(dir) {
return {
up: 'down',
down: 'up',
left: 'right',
right: 'left'
}[dir];
}
}
function backDir(dir) {
return {
up: 'down',
down: 'up',
left: 'right',
right: 'left'
}[dir];
}
function has(v) {
return v !== null && v !== void 0;
}
function has(v) {
return v !== null && v !== void 0;
}
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
function maxGameScale(n = 0) {
const index = core.domStyle.availableScale.indexOf(core.domStyle.scale);
core.control.setDisplayScale(
core.domStyle.availableScale.length - 1 - index - n
);
core.resize();
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.plugin.utils = {
slide,
backDir,
has,
maxGameScale
};
})();

File diff suppressed because it is too large Load Diff

View File

@ -98,7 +98,9 @@ import { exec } from 'child_process';
// 3. 压缩js插件
try {
exec(
'babel ./dist/project/plugin --out-file ./dist/project/plugin.min.js'
`babel ${data.main.plugin
.map(v => `./dist/project/plugin/${v}.js`)
.join(' ')} --out-file ./dist/project/plugin.min.js`
).on('close', async () => {
const main = await fs.readFile('./dist/main.js', 'utf-8');
await fs.writeFile(

View File

@ -51,17 +51,19 @@
"<br>",
"游戏版本V1.0.0",
"<br>",
"游戏作者:古祠",
"<br>",
"游戏开源地址:<a href=\"https://github.com/unanmed/HumanBreak\" target=\"_blank\">",
"https://github.com/unanmed/HumanBreak</a>",
"<br>",
"游戏作者:古祠",
"<br>",
"本塔遵循MIT开源协议。<a href=\"LICENSE\" target=\"_blank\">查看开源协议</a>",
"<br>",
"BGM来源:网易云音乐等",
"音乐来源:网易云音乐等",
"<br>",
"素材来源:大素材库、爱给网、网站素材库等",
"<br>",
"<span style=\"color: gold\">特别说明:素材与音乐均来自网络,不得用于商业用途,仅用于参考与学习</span>",
"<br>",
"特别鸣谢:无名甲烷菌(提供部分特殊属性与机制想法)",
"<br>",
"测试:"
@ -450,14 +452,14 @@
},
"study": {
"text": "学习",
"condition": "core.getSkillLevel(11) > 0",
"condition": "core.plugin.skillTree.getSkillLevel(11) > 0",
"desc": [
"本条目会详细说明学习的机制与所有可以被学习的技能被学习后的效果。当前已经学习的技能会以与状态栏类似的盒子展示出来。",
"<br>",
"<br>",
"首先学习技能消耗的智慧点会越来越多初始消耗的智慧点为500每学习一次增加250。",
"学习的技能可以持续5场战斗在技能树界面每升级一次增加3场",
"<span style=\"color: gold\">当前为${core.getSkillLevel(11) * 3 + 2}场</span>。",
"<span style=\"color: gold\">当前为${core.plugin.skillTree.getSkillLevel(11) * 3 + 2}场</span>。",
"学习后对应属性的值,例如抱团怪增加的属性百分比,会与被学习的怪物相同。学习界面可以使用背包中的道具或点击状态栏打开。",
"<br>",
"<br>",

View File

@ -8,7 +8,7 @@
},
"blade": {
"text": "1断灭之刃",
"opened": "core.getSkillLevel(2) > 0",
"opened": "core.plugin.skillTree.getSkillLevel(2) > 0",
"desc": [
"<span style=\"color: gold\">快捷键1</span>,开启后勇士攻击增加${level:2 * 10}%",
"同时防御减少${level:2 * 10}%。",
@ -31,7 +31,7 @@
},
"shield": {
"text": "3铸剑为盾",
"opened": "core.getSkillLevel(10) > 0",
"opened": "core.plugin.skillTree.getSkillLevel(10) > 0",
"desc": [
"<span style=\"color: gold\">快捷键3</span>,开启后勇士防御增加${level:10 * 10}%",
"同时攻击减少${level:10 * 10}%。",

View File

@ -576,7 +576,7 @@ export function para3(chase: Chase) {
'MT14',
async () => {
flags.finishChase1 = true;
core.autoFixRouteBoss();
core.plugin.towerBoss.autoFixRouteBoss();
core.showStatusBar();
ani.time(750).apply('rect', 0);
chase.end();

View File

@ -105,16 +105,17 @@ function resetFlag() {
}
export async function triggerFullscreen() {
const { maxGameScale } = core.plugin.utils;
if (document.fullscreenElement) {
await document.exitFullscreen();
requestAnimationFrame(() => {
core.maxGameScale(1);
maxGameScale(1);
});
fullscreen.value = false;
} else {
await document.body.requestFullscreen();
requestAnimationFrame(() => {
core.maxGameScale();
maxGameScale();
});
fullscreen.value = true;
}

2
src/types/data.d.ts vendored
View File

@ -68,6 +68,8 @@ interface MainData {
*
*/
readonly splitImages: SplitImageData;
readonly plugin: string[];
}
interface FirstData {

92
src/types/plugin.d.ts vendored
View File

@ -18,10 +18,20 @@ interface PluginDeclaration
extends PluginUtils,
PluginUis,
PluginUse,
SkillTree,
MiniMap,
HeroRealStatus,
PluginAchievement {
/**
*
*/
utils: GamePluginUtils;
loopMap: GamePluginLoopMap;
towerBoss: GamePluginBoss;
skillTree: GamePluginSkillTree;
study: GamePluginStudy;
hero: GamePluginHeroRealStatus;
skills: Record<Chapter, Skill[]>;
/**
* 使core.addPop或core.plugin.addPop调用
* @param px
@ -83,6 +93,45 @@ interface PluginDeclaration
resetFlagSettings(): void;
}
interface GamePluginUtils {
/**
* undefined或null
* @param value
*/
has<T>(value: T): value is NonNullable<T>;
/**
*
* @param arr
* @param delta
*/
slide<T>(arr: T[], delta: number): T[];
/**
*
* @param dir
*/
backDir(dir: Dir): Dir;
/**
*
* @param n
*/
maxGameScale(n?: number): void;
}
interface GamePluginLoopMap {
checkLoopMap(): void;
}
interface GamePluginBoss {
/**
* boss战的录像
* @param isStart
*/
autoFixRouteBoss(isStart?: boolean);
}
interface PluginUtils {
/**
* undefined或null
@ -116,37 +165,6 @@ interface PluginUtils {
* @param index
*/
startChase(index: number): Promise<void>;
/**
* boss战的录像
* @param isStart
*/
autoFixRouteBoss(isStart?: boolean);
/**
*
* @param arr
* @param delta
*/
slide<T>(arr: T[], delta: number): T[];
/**
*
* @param dir
*/
backDir(dir: Dir): Dir;
/**
* undefined和null
* @param value
*/
has<T>(value: T): value is NonNullable<T>;
/**
*
* @param n
*/
maxGameScale(n?: number): void;
}
interface PluginUis {
@ -277,9 +295,7 @@ interface PluginUse {
useUp(ele: HTMLElement, fn: DragFn): void;
}
interface SkillTree {
skills: Record<Chapter, Skill[]>;
interface GamePluginSkillTree {
/**
*
* @param skill
@ -323,7 +339,7 @@ interface MiniMap {
splitArea(): void;
}
interface Study {
interface GamePluginStudy {
/**
*
* @param enemy
@ -332,7 +348,7 @@ interface Study {
studySkill(enemy: Enemy, num: number): void;
}
interface HeroRealStatus {
interface GamePluginHeroRealStatus {
/**
*
* @param name

View File

@ -77,7 +77,7 @@
<span
class="selectable"
:selected="selected === 'showStudied'"
v-if="core.getSkillLevel(11) > 0"
v-if="core.plugin.skillTree.getSkillLevel(11) > 0"
@click="click('showStudied')"
>展示已学习技能:&nbsp;&nbsp;&nbsp;{{
showStudied ? 'ON' : 'OFF'

View File

@ -51,7 +51,10 @@ const content = computed(() => {
} else return v;
})
.join('')
.replace(/level:(\d+)/g, 'core.getSkillLevel($1)') +
.replace(
/level:(\d+)/g,
'core.plugin.skillTree.getSkillLevel($1)'
) +
'`'
);
});

View File

@ -108,7 +108,7 @@ const mdef = ref(core.status.hero.mdef);
const skill = computed(() => {
update.value;
return core.getSkillFromIndex(selected.value);
return core.plugin.skillTree.getSkillFromIndex(selected.value);
});
const skills = computed(() => {
@ -119,8 +119,9 @@ const desc = computed(() => {
return eval(
'`' +
splitText(skill.value.desc).replace(/level(:\d+)?/g, (str, $1) => {
if ($1) return `core.getSkillLevel(${$1})`;
else return `core.getSkillLevel(${skill.value.index})`;
if ($1) return `core.plugin.skillTree.getSkillLevel(${$1})`;
else
return `core.plugin.skillTree.getSkillLevel(${skill.value.index})`;
}) +
'`'
);
@ -134,9 +135,10 @@ const effect = computed(() => {
skill.value.effect
.join('')
.replace(/level(:\d+)?/g, (str, $1) => {
if ($1) return `(core.getSkillLevel(${$1}) + ${v})`;
if ($1)
return `(core.plugin.skillTree.getSkillLevel(${$1}) + ${v})`;
else
return `(core.getSkillLevel(${skill.value.index}) + ${v})`;
return `(core.plugin.skillTree.getSkillLevel(${skill.value.index}) + ${v})`;
}) +
'`'
);
@ -154,20 +156,20 @@ const dict = computed(() => {
const front = computed(() => {
return skill.value.front.map(v => {
return `${core.getSkillLevel(v[0]) >= v[1] ? 'a' : 'b'}${v[1]}${
skills.value[dict.value[v[0]]].title
}`;
return `${
core.plugin.skillTree.getSkillLevel(v[0]) >= v[1] ? 'a' : 'b'
}${v[1]} ${skills.value[dict.value[v[0]]].title}`;
});
});
const consume = computed(() => {
update.value;
return core.getSkillConsume(selected.value);
return core.plugin.skillTree.getSkillConsume(selected.value);
});
const level = computed(() => {
update.value;
return core.getSkillLevel(selected.value);
return core.plugin.skillTree.getSkillLevel(selected.value);
});
function exit() {
@ -195,9 +197,9 @@ function draw() {
ctx.lineTo(
...(s.loc.map(v => (v * 2 - 1) * per + per / 2) as LocArr)
);
if (core.getSkillLevel(s.index) < v.front[i][1])
if (core.plugin.skillTree.getSkillLevel(s.index) < v.front[i][1])
ctx.strokeStyle = '#aaa';
else if (core.getSkillLevel(s.index) === s.max)
else if (core.plugin.skillTree.getSkillLevel(s.index) === s.max)
ctx.strokeStyle = '#ff0';
else ctx.strokeStyle = '#0f8';
ctx.lineWidth = devicePixelRatio;
@ -206,7 +208,7 @@ function draw() {
});
skills.value.forEach(v => {
const [x, y] = v.loc.map(v => v * 2 - 1);
const level = core.getSkillLevel(v.index);
const level = core.plugin.skillTree.getSkillLevel(v.index);
//
ctx.save();
ctx.lineWidth = per * 0.06;
@ -247,7 +249,7 @@ function click(e: MouseEvent) {
}
function upgrade(index: number) {
const success = core.upgradeSkill(index);
const success = core.plugin.skillTree.upgradeSkill(index);
if (!success) tip('error', '升级失败!');
else {
tip('success', '升级成功!');

View File

@ -149,7 +149,7 @@ const skill = ref<string>('无');
const up = ref(0);
const spring = ref<number>();
const skillOpened = ref(core.getFlag('chapter', 0) > 0);
const studyOpened = ref(core.getSkillLevel(11) > 0);
const studyOpened = ref(core.plugin.skillTree.getSkillLevel(11) > 0);
/**
* 要展示的勇士属性
*/
@ -191,7 +191,7 @@ function update() {
spring.value = 50 - flags.springCount;
}
skillOpened.value = core.getFlag('chapter', 0) > 0;
studyOpened.value = core.getSkillLevel(11) > 0;
studyOpened.value = core.plugin.skillTree.getSkillLevel(11) > 0;
}
function openSkillTree() {