Eustia/project/plugins.js

23930 lines
725 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
{
"init": function () {
this._afterLoadResources = function () {
// 本函数将在所有资源加载完毕后,游戏开启前被执行
core.ui.statusBar.init();
core.dom.playGame.style.fontFamily = "pala";
core.dom.loadGame.style.fontFamily = "pala";
core.dom.CGMode.style.fontFamily = "pala";
core.dom.musicMode.style.fontFamily = "pala";
core.dom.replayGame.style.fontFamily = "pala";
core.registerEvent("changeMouse", function (data) {
if (!main.replayChecking && !core.isReplaying())
core.changeMouse(
data.icon,
data.div,
data.translate[0],
data.translate[1],
data.scale[0],
data.scale[1],
data.angel,
data.px,
data.py
);
core.doAction();
});
core.registerEvent("removeMouse", function (data) {
if (!main.replayChecking && !core.isReplaying())
core.removeMouse(data.div);
core.doAction();
});
core.registerEvent("addPop", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
data.value = core.replaceText(data.value);
core.addPop(
data.value,
data.px,
data.py,
data.color,
data.boldColor,
data.left,
data.jump,
data.time,
data.show,
data.font,
data.speed
);
}
core.doAction();
});
core.registerEvent("drawWarning", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
data.text = core.replaceText(data.text);
data.text2 = core.replaceText(data.text2);
core.drawWarning(
data.x,
data.y,
data?.text,
data?.text2,
data?.warning,
data.large,
data.size
);
setTimeout(() => core.doAction(), 3100);
} else {
core.doAction();
}
});
core.registerEvent("over", function (data) {
let image = data.image ?? "";
let time = data.time ?? 3000;
let sound = data.sound ?? "";
let textColor = data.textColor ?? "#FFFFFF";
let boldColor = data.boldColor ?? "#000000";
let font = data.font ?? "bold 48px Verdana";
let text = data.text ?? "";
let hidetime = data.hidetime ?? 100;
if (!main.replayChecking && !core.isReplaying()) {
core.over(
image,
data.memory,
time,
hidetime,
sound,
textColor,
boldColor,
font,
text
);
} else {
core.doAction();
}
});
core.registerEvent("changebg", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
core.changebg(
data.img1,
data.memory1,
data.img2,
data.memory2,
data.time,
data.style
);
} else {
core.doAction();
}
});
core.registerEvent("overlist", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
core.overlist(
data.image,
data.memory,
data.hidetime || 30,
data.list || [{
text: "",
sound: "",
time: 50,
textColor: "#FFFFFF",
boldColor: "#000000",
font: "bold 48px Verdana",
frame: 0,
}, ]
);
} else {
core.doAction();
}
});
core.registerEvent("op", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
core.openvideo();
} else {
core.doAction();
}
});
core.registerEvent("animationDrawable", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
core.animationDrawable(
data.allFarme,
data.color,
data.globalAlpha,
data.imageList,
data.soundList
);
} else {
core.doAction();
}
});
core.registerEvent("setanimate", function (data) {
data.px = data.px ?? 0;
data.py = data.py ?? 0;
core.setanimate(
data.name,
data.px,
data.py,
data.width,
data.height,
data.allFarme,
data.imageList,
data.soundList
);
core.doAction();
});
core.registerEvent("clearanimate", function (data) {
core.plugin.playing.clear();
core.doAction();
});
core.registerEvent("deleteanimate", function (data) {
core.deleteanimate(data.name);
core.doAction();
});
core.registerEvent("playanimate", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
data.x = data.x ?? 0;
data.y = data.y ?? 0;
data.scalex = data.scalex ?? 1;
data.scaley = data.scaley ?? 1;
core.playanimate(
data.name,
data.x,
data.y,
data.hero,
data.scalex,
data.scaley
);
core.doAction();
} else {
core.doAction();
}
});
core.registerEvent("cgtextList", function (data) {
core.ui.cgText.textList = core.plugin[data.textList];
core.doAction();
});
core.registerEvent("cgtext", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
core.ui.cgText.image = data.bg;
core.ui.cgText.nobg = data.nobg ?? false;
core.ui.cgText.memory = data.memory;
core.ui.cgText.head = core.clone(data.head);
core.ui.cgText.index = data.index;
core.ui.cgText.name = core.ui.cgText.textList[data.index][0];
core.ui.cgText.text = data.text ?
data.text :
core.ui.cgText.textList[data.index][1];
core.ui.cgText.time = data.time;
core.ui.cgText.wait = data.wait;
core.ui.cgText.WindowSkin = data.WindowSkin;
core.ui.cgText.sound =
data.sound === "" ?
data.sound :
core.ui.cgText.textList[data.index][2] || "";
core.ui.cgText.bodyList = core.clone(data.bodyList);
main.dom.cgText.style.display = "block";
core.ui.cgText.update();
} else {
core.doAction();
}
});
core.registerEvent("introAndLoop", function (data) {
if (!main.replayChecking && !core.isReplaying()) {
core.plugin.introAndLoop(data.intro, data.time, data.loop);
core.doAction();
} else {
core.doAction();
}
});
core.registerEvent("setq", function (data) {
core.setFlag("任务地点", data.id);
core.doAction();
});
core.registerEvent("setmusics", function (data) {
if (
(core.getLocalStorage("musics") &&
core.getLocalStorage("musics").length === 0) ||
!core.getLocalStorage("musics")
)
core.setLocalStorage("musics", ["theme.mp3"]);
let a = core.getLocalStorage("musics");
if (!data.bgm) {
core.setLocalStorage("musics", ["theme.mp3"]);
} else {
if (!a.includes(data.bgm)) a.push(data.bgm);
core.setLocalStorage("musics", a);
}
core.doAction();
});
core.registerEvent("setcgs", function (data) {
if (!data.img) {
core.setLocalStorage("cgs", []);
} else {
let a = core.getLocalStorage("cgs") ?? [];
if (!a.includes(data.img)) a.push(data.img);
core.setLocalStorage("cgs", a);
}
core.doAction();
});
};
},
"drawLight": function () {
// 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...)
// 【参数说明】
// name必填要绘制到的画布名可以是一个系统画布或者是个自定义画布如果不存在则创建
// color可选只能是一个0~1之间的数为不透明度的值。不填则默认为0.9。
// lights可选一个数组定义了每个独立的灯光。
// 其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标r为该灯光的半径。
// lightDec可选0到1之间光从多少百分比才开始衰减在此范围内保持全亮不设置默认为0。
// 比如lightDec为0.5代表每个灯光部分内圈50%的范围全亮50%以后才开始快速衰减。
// 【调用样例】
// core.plugin.drawLight('curtain'); // 在curtain层绘制全图不透明度0.9,等价于更改画面色调为[0,0,0,0.9]。
// core.plugin.drawLight('ui', 0.95, [[25,11,46]]); // 在ui层绘制全图不透明度0.95,其中在(25,11)点存在一个半径为46的灯光效果。
// core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果灯光中心不透明度0.1。
// core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层且存在三个灯光效果分别是中心(25,11)半径46中心(105,121)半径88中心(301,221)半径106。
// core.plugin.drawLight('xxx', 0.3, [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果它们在内圈40%范围内保持全亮40%后才开始衰减。
this.drawLight = function (name, color, lights, lightDec) {
// 清空色调层也可以修改成其它层比如animate/weather层或者用自己创建的canvas
var ctx = core.getContextByName(name);
if (ctx == null) {
if (typeof name == "string")
ctx = core.createCanvas(
name,
0,
0,
core._PX_ || core.__PIXELS__,
core._PY_ || core.__PIXELS__,
98
);
else return;
}
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
core.clearMap(name);
// 绘制色调层,默认不透明度
if (color == null) color = 0.9;
ctx.fillStyle = "rgba(0,0,0," + color + ")";
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
lightDec = core.clamp(lightDec, 0, 1);
ctx.globalCompositeOperation = "destination-out";
lights.forEach(function (light) {
// 坐标,半径,中心不透明度
var x = light[0],
y = light[1],
r = light[2];
// 计算衰减距离
var decDistance = parseInt(r * lightDec);
// 正方形区域的直径和左上角坐标
var grd = ctx.createRadialGradient(x, y, decDistance, x, y, r);
grd.addColorStop(0, "rgba(0,0,0,1)");
grd.addColorStop(1, "rgba(0,0,0,0)");
ctx.beginPath();
ctx.fillStyle = grd;
ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.fill();
});
ctx.globalCompositeOperation = "source-over";
// 可以在任何地方如afterXXX或自定义脚本事件调用函数方法为 core.plugin.xxx();
};
},
"shop": function () {
// 【全局商店】相关的功能
//
// 打开一个全局商店
// shopId要打开的商店idnoRoute是否不计入录像
this.openShop = function (shopId, noRoute) {
var shop = core.status.shops[shopId];
// Step 1: 检查能否打开此商店
if (!this.canOpenShop(shopId)) {
core.drawTip("该商店尚未开启");
return false;
}
// Step 2: (如有必要)记录打开商店的脚本事件
if (!noRoute) {
core.status.route.push("shop:" + shopId);
}
// Step 3: 检查道具商店 or 公共事件
if (shop.item) {
if (core.openItemShop) {
core.openItemShop(shopId);
} else {
core.playSound("操作失败");
core.insertAction("道具商店插件不存在!请检查是否存在该插件!");
}
return;
}
if (shop.commonEvent) {
core.insertCommonEvent(shop.commonEvent, shop.args);
return;
}
_shouldProcessKeyUp = true;
// Step 4: 执行标准公共商店
core.insertAction(this._convertShop(shop));
return true;
};
////// 将一个全局商店转变成可预览的公共事件 //////
this._convertShop = function (shop) {
return [
{
type: "function",
function: "function() {core.addFlag('@temp@shop', 1);}",
},
{
type: "while",
condition: "true",
data: [
// 检测能否访问该商店
{
type: "if",
condition: "core.isShopVisited('" + shop.id + "')",
true: [
// 可以访问,直接插入执行效果
{
type: "function",
function:
"function() { core.plugin._convertShop_replaceChoices('" +
shop.id +
"', false) }",
},
],
false: [
// 不能访问的情况下:检测能否预览
{
type: "if",
condition: shop.disablePreview,
true: [
// 不可预览,提示并退出
{ type: "playSound", name: "操作失败" },
"当前无法访问该商店!",
{ type: "break" },
],
false: [
// 可以预览:将商店全部内容进行替换
{ type: "tip", text: "当前处于预览模式,不可购买" },
{
type: "function",
function:
"function() { core.plugin._convertShop_replaceChoices('" +
shop.id +
"', true) }",
},
],
},
],
},
],
},
{
type: "function",
function: "function() {core.addFlag('@temp@shop', -1);}",
},
];
};
this._convertShop_replaceChoices = function (shopId, previewMode) {
var shop = core.status.shops[shopId];
var choices = (shop.choices || [])
.filter(function (choice) {
if (choice.condition == null || choice.condition == "") return true;
try {
return core.calValue(choice.condition);
} catch (e) {
return true;
}
})
.map(function (choice) {
var ableToBuy = core.calValue(choice.need);
return {
text: core.replaceText(choice.text),
icon: choice.icon,
color:
ableToBuy && !previewMode ? choice.color : [153, 153, 153, 1],
action:
ableToBuy && !previewMode
? [{ type: "playSound", name: "商店" }].concat(choice.action)
: [
{ type: "playSound", name: "操作失败" },
{
type: "tip",
text: previewMode ? "预览模式下不可购买" : "购买条件不足",
},
],
};
})
.concat({
text: "离开",
action: [{ type: "playSound", name: "取消" }, { type: "break" }],
});
core.insertAction({
type: "choices",
text: core.replaceText(shop.text),
choices: choices,
});
};
/// 是否访问过某个快捷商店
this.isShopVisited = function (id) {
if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {});
var shops = core.getFlag("__shops__");
if (!shops[id]) shops[id] = {};
return shops[id].visited;
};
/// 当前应当显示的快捷商店列表
this.listShopIds = function () {
return Object.keys(core.status.shops).filter(function (id) {
return core.isShopVisited(id) || !core.status.shops[id].mustEnable;
});
};
/// 是否能够打开某个商店
this.canOpenShop = function (id) {
if (this.isShopVisited(id)) return true;
var shop = core.status.shops[id];
if (shop.item || shop.commonEvent || shop.mustEnable) return false;
return true;
};
/// 启用或禁用某个快捷商店
this.setShopVisited = function (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;
};
/// 能否使用快捷商店
this.canUseQuickShop = function (id) {
// 如果返回一个字符串,表示不能,字符串为不能使用的提示
// 返回null代表可以使用
// 检查当前楼层的canUseQuickShop选项是否为false
if (core.status.thisMap.canUseQuickShop === false)
return "当前楼层不能使用快捷商店。";
return null;
};
var _shouldProcessKeyUp = true;
/// 允许商店X键退出
core.registerAction(
"keyUp",
"shops",
function (keycode) {
if (!core.status.lockControl || core.status.event.id != "action")
return false;
if ((keycode == 13 || keycode == 32) && !_shouldProcessKeyUp) {
_shouldProcessKeyUp = true;
return true;
}
if (
!core.hasFlag("@temp@shop") ||
core.status.event.data.type != "choices"
)
return false;
var data = core.status.event.data.current;
var choices = data.choices;
var topIndex = core.actions._getChoicesTopIndex(choices.length);
if (keycode == 88 || keycode == 27) {
// X, ESC
core.actions._clickAction(
core._HALF_WIDTH_ || core.__HALF_SIZE__,
topIndex + choices.length - 1
);
return true;
}
return false;
},
60
);
/// 允许长按空格或回车连续执行操作
core.registerAction(
"keyDown",
"shops",
function (keycode) {
if (
!core.status.lockControl ||
!core.hasFlag("@temp@shop") ||
core.status.event.id != "action"
)
return false;
if (core.status.event.data.type != "choices") return false;
core.status.onShopLongDown = true;
var data = core.status.event.data.current;
var choices = data.choices;
var topIndex = core.actions._getChoicesTopIndex(choices.length);
if (keycode == 13 || keycode == 32) {
// Space, Enter
core.actions._clickAction(
core._HALF_WIDTH_ || core.__HALF_SIZE__,
topIndex + core.status.event.selection
);
_shouldProcessKeyUp = false;
return true;
}
return false;
},
60
);
// 允许长按屏幕连续执行操作
core.registerAction(
"longClick",
"shops",
function (x, y, px, py) {
if (
!core.status.lockControl ||
!core.hasFlag("@temp@shop") ||
core.status.event.id != "action"
)
return false;
if (core.status.event.data.type != "choices") return false;
var data = core.status.event.data.current;
var choices = data.choices;
var topIndex = core.actions._getChoicesTopIndex(choices.length);
if (
Math.abs(x - (core._HALF_WIDTH_ || core.__HALF_SIZE__)) <= 2 &&
y >= topIndex &&
y < topIndex + choices.length
) {
core.actions._clickAction(x, y);
return true;
}
return false;
},
60
);
},
"removeMap": function () {
// 高层塔砍层插件,删除后不会存入存档,不可浏览地图也不可飞到。
// 推荐用法:
// 对于超高层或分区域塔当在1区时将2区以后的地图删除1区结束时恢复2区进二区时删除1区地图以此类推
// 这样可以大幅减少存档空间,以及加快存读档速度
// 删除楼层
// core.removeMaps("MT1", "MT300") 删除MT1~MT300之间的全部层
// core.removeMaps("MT10") 只删除MT10层
this.removeMaps = function (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.__visited__ = flags.__visited__ || {};
flags.__removed__ = flags.__removed__ || [];
flags.__disabled__ = flags.__disabled__ || {};
flags.__leaveLoc__ = flags.__leaveLoc__ || {};
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(function (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;
}
};
// 恢复楼层
// core.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层
// core.resumeMaps("MT10") 只恢复MT10层
this.resumeMaps = function (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;
flags.__removed__ = flags.__removed__.filter(function (f) {
return f != floorId;
});
core.status.maps[floorId] = core.loadFloor(floorId);
}
};
// 分区砍层相关
var inAnyPartition = function (floorId) {
var inPartition = false;
(core.floorPartitions || []).forEach(function (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;
};
// 分区砍层
this.autoRemoveMaps = function (floorId) {
if (main.mode != "play" || !inAnyPartition(floorId)) return;
// 根据分区信息自动砍层与恢复
(core.floorPartitions || []).forEach(function (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.resumeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]);
} else {
core.removeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]);
}
});
};
},
"fiveLayers": function () {
// 是否启用五图层增加背景2层和前景2层 将__enable置为true即会启用启用后请保存后刷新编辑器
// 背景层2将会覆盖背景层 被事件层覆盖 前景层2将会覆盖前景层
// 另外 请注意加入两个新图层 会让大地图的性能降低一些
// 插件作者ad
var __enable = true;
if (!__enable) return;
// 创建新图层
function createCanvas(name, zIndex) {
if (!name) return;
var canvas = document.createElement("canvas");
canvas.id = name;
canvas.className = "gameCanvas 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;
canvas.width = core._PX_ || core.__PIXELS__;
canvas.height = core._PY_ || core.__PIXELS__;
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 = function (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 = function () {
editor.uifunctions.setLayerMod(value);
};
return input;
};
var createCanvasBtn_mobile = function (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("bg2");
// 插入事件层前(即新插入的背景层2前)
parent.insertBefore(txt, child);
// 向最后插入前景层2(即插入前景层后)
parent.appendChild(input2);
var txt2 = document.createTextNode("fg2");
parent.appendChild(txt2);
parent.childNodes[2].replaceWith("bg");
parent.childNodes[6].replaceWith("事件");
parent.childNodes[8].replaceWith("fg");
} 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);
}
}
var _loadFloor_doNotCopy = core.maps._loadFloor_doNotCopy;
core.maps._loadFloor_doNotCopy = function () {
return ["bg2map", "fg2map"].concat(_loadFloor_doNotCopy());
};
////// 绘制背景和前景层 //////
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),
};
};
},
"itemShop": function () {
// 道具商店相关的插件
// 可在全塔属性-全局商店中使用「道具商店」事件块进行编辑(如果找不到可以在入口方块中找)
var shopId = null; // 当前商店ID
var type = 0; // 当前正在选中的类型0买入1卖出
var selectItem = 0; // 当前正在选中的道具
var selectCount = 0; // 当前已经选中的数量
var page = 0;
var totalPage = 0;
var totalMoney = 0;
var list = [];
var shopInfo = null; // 商店信息
var choices = []; // 商店选项
var use = "money";
var useText = "金币";
var bigFont = core.ui._buildFont(20, false),
middleFont = core.ui._buildFont(18, false);
this._drawItemShop = function () {
// 绘制道具商店
// Step 1: 背景和固定的几个文字
core.ui._createUIEvent();
core.clearMap("uievent");
core.ui.clearUIEventSelector();
core.setTextAlign("uievent", "left");
core.setTextBaseline("uievent", "top");
core.fillRect("uievent", 0, 0, 416, 416, "black");
core.drawWindowSkin("winskin.webp", "uievent", 0, 0, 416, 56);
core.drawWindowSkin("winskin.webp", "uievent", 0, 56, 312, 56);
core.drawWindowSkin("winskin.webp", "uievent", 0, 112, 312, 304);
core.drawWindowSkin("winskin.webp", "uievent", 312, 56, 104, 56);
core.drawWindowSkin("winskin.webp", "uievent", 312, 112, 104, 304);
core.setFillStyle("uievent", "white");
core.setStrokeStyle("uievent", "white");
core.fillText("uievent", "购买", 32, 74, "white", bigFont);
core.fillText("uievent", "卖出", 132, 74);
core.fillText("uievent", "离开", 232, 74);
core.fillText("uievent", "当前" + useText, 324, 66, null, middleFont);
core.setTextAlign("uievent", "right");
core.fillText(
"uievent",
core.formatBigNumber(core.status.hero[use]),
405,
89
);
core.setTextAlign("uievent", "left");
core.ui.drawUIEventSelector(
1,
"winskin.webp",
22 + 100 * type,
66,
60,
33
);
if (selectItem != null) {
core.setTextAlign("uievent", "center");
core.fillText(
"uievent",
type == 0 ? "买入个数" : "卖出个数",
364,
320,
null,
bigFont
);
core.fillText("uievent", "< " + selectCount + " >", 364, 350);
core.fillText("uievent", "确定", 364, 380);
}
// Step 2获得列表并展示
list = choices.filter(function (one) {
if (one.condition != null && one.condition != "") {
try {
if (!core.calValue(one.condition)) return false;
} catch (e) {}
}
return (
(type == 0 && one.money != null) || (type == 1 && one.sell != null)
);
});
var per_page = 6;
totalPage = Math.ceil(list.length / per_page);
page = Math.floor((selectItem || 0) / per_page) + 1;
// 绘制分页
if (totalPage > 1) {
var half = 156;
core.setTextAlign("uievent", "center");
core.fillText(
"uievent",
page + " / " + totalPage,
half,
388,
null,
middleFont
);
if (page > 1) core.fillText("uievent", "上一页", half - 80, 388);
if (page < totalPage)
core.fillText("uievent", "下一页", half + 80, 388);
}
core.setTextAlign("uievent", "left");
// 绘制每一项
var start = (page - 1) * per_page;
for (var i = 0; i < per_page; ++i) {
var curr = start + i;
if (curr >= list.length) break;
var item = list[curr];
core.drawIcon("uievent", item.id, 10, 125 + i * 40);
core.setTextAlign("uievent", "left");
core.fillText(
"uievent",
core.material.items[item.id].name,
50,
132 + i * 40,
null,
bigFont
);
core.setTextAlign("uievent", "right");
core.fillText(
"uievent",
(type == 0 ? core.calValue(item.money) : core.calValue(item.sell)) +
useText +
"/个",
300,
133 + i * 40,
null,
middleFont
);
core.setTextAlign("uievent", "left");
if (curr == selectItem) {
// 绘制描述,文字自动放缩
var text =
core.replaceText(core.material.items[item.id].text) ||
"该道具暂无描述";
if (text[0] == "," || text[0] == "") text = text.substring(1);
try {
text = core.replaceText(text);
} catch (e) {}
for (var fontSize = 20; fontSize >= 8; fontSize -= 2) {
var config = { left: 10, fontSize: fontSize, maxWidth: 403 };
var height = core.getTextContentHeight(text, config);
if (height <= 50) {
config.top = (56 - height) / 2;
core.drawTextContent("uievent", text, config);
break;
}
}
core.ui.drawUIEventSelector(
2,
"winskin.webp",
8,
120 + i * 40,
295,
40
);
if (type == 0 && item.number != null) {
core.fillText("uievent", "存货", 324, 132, null, bigFont);
core.setTextAlign("uievent", "right");
core.fillText("uievent", item.number, 406, 132, null, null, 40);
} else if (type == 1) {
core.fillText("uievent", "数量", 324, 132, null, bigFont);
core.setTextAlign("uievent", "right");
core.fillText(
"uievent",
core.itemCount(item.id),
406,
132,
null,
null,
40
);
}
core.setTextAlign("uievent", "left");
core.fillText("uievent", "预计" + useText, 324, 250);
core.setTextAlign("uievent", "right");
totalMoney =
selectCount *
(type == 0 ? core.calValue(item.money) : core.calValue(item.sell));
core.fillText("uievent", core.formatBigNumber(totalMoney), 405, 280);
core.setTextAlign("uievent", "left");
core.fillText(
"uievent",
type == 0 ? "已购次数" : "已卖次数",
324,
170
);
core.setTextAlign("uievent", "right");
core.fillText(
"uievent",
(type == 0 ? item.money_count : item.sell_count) || 0,
405,
200
);
}
}
core.setTextAlign("uievent", "left");
core.setTextBaseline("uievent", "alphabetic");
};
var _add = function (item, delta) {
if (item == null) return;
selectCount = core.clamp(
selectCount + delta,
0,
Math.min(
type == 0
? Math.floor(core.status.hero[use] / core.calValue(item.money))
: core.itemCount(item.id),
type == 0 && item.number != null
? item.number
: Number.MAX_SAFE_INTEGER
)
);
};
var _confirm = function (item) {
if (item == null || selectCount == 0) return;
if (type == 0) {
core.status.hero[use] -= totalMoney;
core.getItem(item.id, selectCount);
core.stopSound();
core.playSound("确定");
if (item.number != null) item.number -= selectCount;
item.money_count = (item.money_count || 0) + selectCount;
} else {
core.status.hero[use] += totalMoney;
core.removeItem(item.id, selectCount);
core.playSound("确定");
core.drawTip(
"成功卖出" + selectCount + "个" + core.material.items[item.id].name,
item.id
);
if (item.number != null) item.number += selectCount;
item.sell_count = (item.sell_count || 0) + selectCount;
}
selectCount = 0;
};
this._performItemShopKeyBoard = function (keycode) {
var item = list[selectItem] || null;
// 键盘操作
switch (keycode) {
case 38: // up
if (selectItem == null) break;
if (selectItem == 0) selectItem = null;
else selectItem--;
selectCount = 0;
break;
case 37: // left
if (selectItem == null) {
if (type > 0) type--;
break;
}
_add(item, -1);
break;
case 39: // right
if (selectItem == null) {
if (type < 2) type++;
break;
}
_add(item, 1);
break;
case 40: // down
if (selectItem == null) {
if (list.length > 0) selectItem = 0;
break;
}
if (list.length == 0) break;
selectItem = Math.min(selectItem + 1, list.length - 1);
selectCount = 0;
break;
case 13:
case 32: // Enter/Space
if (selectItem == null) {
if (type == 2) core.insertAction({ type: "break" });
else if (list.length > 0) selectItem = 0;
break;
}
_confirm(item);
break;
case 27: // ESC
if (selectItem == null) {
core.insertAction({ type: "break" });
break;
}
selectItem = null;
break;
}
};
this._performItemShopClick = function (px, py) {
var item = list[selectItem] || null;
// 鼠标操作
if (px >= 22 && px <= 82 && py >= 71 && py <= 102) {
// 买
if (type != 0) {
type = 0;
selectItem = null;
selectCount = 0;
}
return;
}
if (px >= 122 && px <= 182 && py >= 71 && py <= 102) {
// 卖
if (type != 1) {
type = 1;
selectItem = null;
selectCount = 0;
}
return;
}
if (px >= 222 && px <= 282 && py >= 71 && py <= 102)
// 离开
return core.insertAction({ type: "break" });
// < >
if (px >= 318 && px <= 341 && py >= 348 && py <= 376)
return _add(item, -1);
if (px >= 388 && px <= 416 && py >= 348 && py <= 376)
return _add(item, 1);
// 确定
if (px >= 341 && px <= 387 && py >= 380 && py <= 407)
return _confirm(item);
// 上一页/下一页
if (px >= 45 && px <= 105 && py >= 388) {
if (page > 1) {
selectItem -= 6;
selectCount = 0;
}
return;
}
if (px >= 208 && px <= 268 && py >= 388) {
if (page < totalPage) {
selectItem = Math.min(selectItem + 6, list.length - 1);
selectCount = 0;
}
return;
}
// 实际区域
if (px >= 9 && px <= 300 && py >= 120 && py < 360) {
if (list.length == 0) return;
var index = parseInt((py - 120) / 40);
var newItem = 6 * (page - 1) + index;
if (newItem >= list.length) newItem = list.length - 1;
if (newItem != selectItem) {
selectItem = newItem;
selectCount = 0;
}
return;
}
};
this._performItemShopAction = function () {
if (flags.type == 0) return this._performItemShopKeyBoard(flags.keycode);
else return this._performItemShopClick(flags.px, flags.py);
};
this.openItemShop = function (itemShopId) {
shopId = itemShopId;
type = 0;
page = 0;
selectItem = null;
selectCount = 0;
core.isShopVisited(itemShopId);
shopInfo = flags.__shops__[shopId];
if (shopInfo.choices == null)
shopInfo.choices = core.clone(core.status.shops[shopId].choices);
choices = shopInfo.choices;
use = core.status.shops[shopId].use;
if (use != "exp") use = "money";
useText = use == "money" ? "金币" : "经验";
core.insertAction([
{
type: "while",
condition: "true",
data: [
{
type: "function",
function: "function () { core.plugin._drawItemShop(); }",
},
{ type: "wait" },
{
type: "function",
function: "function() { core.plugin._performItemShopAction(); }",
},
],
},
{
type: "function",
function:
"function () { core.deleteCanvas('uievent'); core.ui.clearUIEventSelector(); }",
},
]);
};
},
"enemyLevel": function () {
// 此插件将提供怪物手册中的怪物境界显示
// 使用此插件需要先给每个怪物定义境界,方法如下:
// 点击怪物的【配置表格】,找到“【怪物】相关的表格配置”,然后在【名称】仿照增加境界定义:
/*
"level": {
"_leaf": true,
"_type": "textarea",
"_string": true,
"_data": "境界"
},
*/
// 然后保存刷新,可以看到怪物的属性定义中出现了【境界】。再开启本插件即可。
// 是否开启本插件,默认禁用;将此改成 true 将启用本插件。
var __enable = false;
if (!__enable) return;
// 这里定义每个境界的显示颜色;可以写'red', '#RRGGBB' 或者[r,g,b,a]四元数组
var levelToColors = {
萌新一阶: "red",
萌新二阶: "#FF0000",
萌新三阶: [255, 0, 0, 1],
};
// 复写 _drawBook_drawName
var originDrawBook = core.ui._drawBook_drawName;
core.ui._drawBook_drawName = function (index, enemy, top, left, width) {
// 如果没有境界,则直接调用原始代码绘制
if (!enemy.level)
return originDrawBook.call(core.ui, index, enemy, top, left, width);
// 存在境界,则额外进行绘制
core.setTextAlign("ui", "center");
if (enemy.specialText.length == 0) {
core.fillText(
"ui",
enemy.name,
left + width / 2,
top + 27,
"#DDDDDD",
this._buildFont(17, true)
);
core.fillText(
"ui",
enemy.level,
left + width / 2,
top + 51,
core.arrayToRGBA(levelToColors[enemy.level] || "#DDDDDD"),
this._buildFont(14, true)
);
} else {
core.fillText(
"ui",
enemy.name,
left + width / 2,
top + 20,
"#DDDDDD",
this._buildFont(17, true),
width
);
switch (enemy.specialText.length) {
case 1:
core.fillText(
"ui",
enemy.specialText[0],
left + width / 2,
top + 38,
core.arrayToRGBA((enemy.specialColor || [])[0] || "#FF6A6A"),
this._buildFont(14, true),
width
);
break;
case 2:
// Step 1: 计算字体
var text = enemy.specialText[0] + " " + enemy.specialText[1];
core.setFontForMaxWidth(
"ui",
text,
width,
this._buildFont(14, true)
);
// Step 2: 计算总宽度
var totalWidth = core.calWidth("ui", text);
var leftWidth = core.calWidth("ui", enemy.specialText[0]);
var rightWidth = core.calWidth("ui", enemy.specialText[1]);
// Step 3: 绘制
core.fillText(
"ui",
enemy.specialText[0],
left + (width + leftWidth - totalWidth) / 2,
top + 38,
core.arrayToRGBA((enemy.specialColor || [])[0] || "#FF6A6A")
);
core.fillText(
"ui",
enemy.specialText[1],
left + (width + totalWidth - rightWidth) / 2,
top + 38,
core.arrayToRGBA((enemy.specialColor || [])[1] || "#FF6A6A")
);
break;
default:
core.fillText(
"ui",
"多属性...",
left + width / 2,
top + 38,
"#FF6A6A",
this._buildFont(14, true),
width
);
}
core.fillText(
"ui",
enemy.level,
left + width / 2,
top + 56,
core.arrayToRGBA(levelToColors[enemy.level] || "#DDDDDD"),
this._buildFont(14, true)
);
}
};
// 也可以复写其他的属性颜色如怪物攻防等,具体参见下面的例子的注释部分
core.ui._drawBook_drawRow1 = function (
index,
enemy,
top,
left,
width,
position
) {
// 绘制第一行
core.setTextAlign("ui", "left");
var b13 = this._buildFont(13, true),
f13 = this._buildFont(13, false);
var col1 = left,
col2 = left + (width * 9) / 25,
col3 = left + (width * 17) / 25;
core.fillText("ui", "生命", col1, position, "#DDDDDD", f13);
core.fillText(
"ui",
core.formatBigNumber(enemy.hp || 0),
col1 + 30,
position,
/*'red' */ null,
b13
);
core.fillText("ui", "攻击", col2, position, null, f13);
core.fillText(
"ui",
core.formatBigNumber(enemy.atk || 0),
col2 + 30,
position,
/* '#FF0000' */ null,
b13
);
core.fillText("ui", "防御", col3, position, null, f13);
core.fillText(
"ui",
core.formatBigNumber(enemy.def || 0),
col3 + 30,
position,
/* [255, 0, 0, 1] */ null,
b13
);
};
},
"multiHeros": function () {
// 多角色插件
// Step 1: 启用本插件
// Step 2: 定义每个新的角色各项初始数据(参见下方注释)
// Step 3: 在游戏中的任何地方都可以调用 `core.changeHero()` 进行切换;也可以 `core.changeHero(1)` 来切换到某个具体的角色上
// 是否开启本插件,默认禁用;将此改成 true 将启用本插件。
var __enable = false;
if (!__enable) return;
// 在这里定义全部的新角色属性
// 请注意,在这里定义的内容不会多角色共用,在切换时会进行恢复。
// 你也可以自行新增或删除,比如不共用金币则可以加上"money"的初始化,不共用道具则可以加上"items"的初始化,
// 多角色共用hp的话则删除hp等等。总之不共用的属性都在这里进行定义就好。
var hero1 = {
floorId: "MT0", // 该角色初始楼层ID如果共用楼层可以注释此项
image: "brave.webp", // 角色的行走图名称;此项必填不然会报错
name: "1号角色",
lv: 1,
hp: 10000, // 如果HP共用可注释此项
atk: 1000,
def: 1000,
mdef: 0,
// "money": 0, // 如果要不共用金币则取消此项注释
// "exp": 0, // 如果要不共用经验则取消此项注释
loc: { x: 0, y: 0, direction: "up" }, // 该角色初始位置;如果共用位置可注释此项
items: {
tools: {}, // 如果共用消耗道具(含钥匙)则可注释此项
// "constants": {}, // 如果不共用永久道具(如手册)可取消注释此项
equips: {}, // 如果共用在背包的装备可注释此项
},
equipment: [], // 如果共用装备可注释此项;此项和上面的「共用在背包的装备」需要拥有相同状态,不然可能出现问题
};
// 也可以类似新增其他角色
// 新增的角色,各项属性共用与不共用的选择必须和上面完全相同,否则可能出现问题。
// var hero2 = { ...
var heroCount = 2; // 包含默认角色在内总共多少个角色,该值需手动修改。
this.initHeros = function () {
core.setFlag("hero1", core.clone(hero1)); // 将属性值存到变量中
// core.setFlag("hero2", core.clone(hero2)); // 更多的角色也存入变量中;每个定义的角色都需要新增一行
// 检测是否存在装备
if (hero1.equipment) {
if (!hero1.items || !hero1.items.equips) {
alert("多角色插件的equipment和道具中的equips必须拥有相同状态");
}
// 存99号套装为全空
var saveEquips = core.getFlag("saveEquips", []);
saveEquips[99] = [];
core.setFlag("saveEquips", saveEquips);
} else {
if (hero1.items && hero1.items.equips) {
alert("多角色插件的equipment和道具中的equips必须拥有相同状态");
}
}
};
// 在游戏开始注入initHeros
var _startGame_setHard = core.events._startGame_setHard;
core.events._startGame_setHard = function () {
_startGame_setHard.call(core.events);
core.initHeros();
};
// 切换角色
// 可以使用 core.changeHero() 来切换到下一个角色
// 也可以 core.changeHero(1) 来切换到某个角色默认角色为0
this.changeHero = function (toHeroId) {
var currHeroId = core.getFlag("heroId", 0); // 获得当前角色ID
if (toHeroId == null) {
toHeroId = (currHeroId + 1) % heroCount;
}
if (currHeroId == toHeroId) return;
var saveList = Object.keys(hero1);
// 保存当前内容
var toSave = {};
// 暂时干掉 drawTip 和 音效,避免切装时的提示
var _drawTip = core.ui.drawTip;
core.ui.drawTip = function () {};
var _playSound = core.control.playSound;
core.control.playSound = function () {};
// 记录当前录像,因为可能存在换装问题
core.clearRouteFolding();
var routeLength = core.status.route.length;
// 优先判定装备
if (hero1.equipment) {
core.items.quickSaveEquip(100 + currHeroId);
core.items.quickLoadEquip(99);
}
saveList.forEach(function (name) {
if (name == "floorId")
toSave[name] = core.status.floorId; // 楼层单独设置
else if (name == "items") {
toSave.items = core.clone(core.status.hero.items);
Object.keys(toSave.items).forEach(function (one) {
if (!hero1.items[one]) delete toSave.items[one];
});
} else toSave[name] = core.clone(core.status.hero[name]); // 使用core.clone()来创建新对象
});
core.setFlag("hero" + currHeroId, toSave); // 将当前角色信息进行保存
var data = core.getFlag("hero" + toHeroId); // 获得要切换的角色保存内容
// 设置角色的属性值
saveList.forEach(function (name) {
if (name == "floorId");
else if (name == "items") {
Object.keys(core.status.hero.items).forEach(function (one) {
if (data.items[one])
core.status.hero.items[one] = core.clone(data.items[one]);
});
} else {
core.status.hero[name] = core.clone(data[name]);
}
});
// 最后装上装备
if (hero1.equipment) {
core.items.quickLoadEquip(100 + toHeroId);
}
core.ui.drawTip = _drawTip;
core.control.playSound = _playSound;
core.status.route = core.status.route.slice(0, routeLength);
core.control._bindRoutePush();
// 插入事件:改变角色行走图并进行楼层切换
var toFloorId = data.floorId || core.status.floorId;
var toLoc = data.loc || core.status.hero.loc;
core.insertAction([
{ type: "setHeroIcon", name: data.image || "hero.webp" }, // 改变行走图
// 同层则用changePos不同层则用changeFloor这是为了避免共用楼层造成触发eachArrive
toFloorId != core.status.floorId
? {
type: "changeFloor",
floorId: toFloorId,
loc: [toLoc.x, toLoc.y],
direction: toLoc.direction,
time: 0, // 可以在这里设置切换时间
}
: {
type: "changePos",
loc: [toLoc.x, toLoc.y],
direction: toLoc.direction,
},
// 你还可以在这里执行其他事件,比如增加或取消跟随效果
]);
core.setFlag("heroId", toHeroId); // 保存切换到的角色ID
};
},
"heroFourFrames": function () {
// 样板的勇士/跟随者移动时只使用2、4两帧观感较差。本插件可以将四帧全用上。
// 是否启用本插件
var __enable = true;
if (!__enable) return;
["up", "down", "left", "right"].forEach(function (one) {
// 指定中间帧动画
core.material.icons.hero[one].midFoot = 2;
});
var heroMoving = function (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;
};
},
"routeFixing": function () {
// 是否开启本插件true 表示启用false 表示禁用。
var __enable = true;
if (!__enable) return;
/*
使用说明启用本插件后录像回放时您可以用数字键1或6分别切换到原速或24倍速
暂停播放时按数字键7电脑按N可以单步播放。手机端可以点击难度单词切换出数字键
数字键2-5可以进行录像自助精修具体描述见下实际弹窗请求您输入时不要带有任何空格
up down left right 勇士向某个方向「行走一步或撞击」
item:ID 使用某件道具,如 item:bomb 表示使用炸弹
unEquip:n 卸掉身上第(n+1)件装备n从0开始如 unEquip:1 默认表示卸掉盾牌
equip:ID 穿上某件装备,如 equip:sword1 表示装上铁剑
saveEquip:n 将身上的当前套装保存到第n套快捷套装n从0开始
loadEquip:n 快捷换上之前保存好的第n套套装
fly:ID 使用楼传飞到某一层,如 fly:MT10 表示飞到主塔10层
choices:none 确认框/选择项「超时」(作者未设置超时时间则此项视为缺失)
choices:n 确认框/选择项选择第(n+1)项选择项n从0开始确认框n为0表示「确定」1表示「取消」
选择项n为负数时表示选择倒数第 -n 项,如 -1 表示最后一项V2.8.2起标准全局商店的「离开」项)
此项缺失的话,确认框将选择作者指定的默认项(初始光标位置),选择项将弹窗请求补选(后台录像验证中选最后一项,可以复写函数来修改)
shop:ID 打开某个全局商店,如 shop:itemShop 表示打开道具商店。因此连载塔千万不要中途修改商店ID
turn 单击勇士Z键转身core.turnHero() 会产生此项,因此通过事件等方式强制让勇士转向应该用 core.setHeroLoc()
turn:dir 勇士转向某个方向dir 可以为 up down left right此项一般是读取自动存档产生的属于样板的不良特性请勿滥用
getNext 轻按获得身边道具,优先获得面前的(面前没有则按上下左右顺序依次获得),身边如果没有道具则此项会被跳过
input:none “等待用户操作事件”中超时(作者未设置超时时间则此项会导致报错)
input:xxx 可能表示“等待用户操作事件”的一个操作(如按键操作将直接记录 input:keycode
也可能表示一个“接受用户输入数字”的输入,后者的情况下 xxx 为输入的整数。此项缺失的话前者将直接报错后者将用0代替后者现在支持负数了
input2:xxx 可能表示“读取全局存储core.getGlobal”读取到的值也可能表示一个“接受用户输入文本”的输入
两种情况下 xxx 都为 base64 编码。此项缺失的话前者将重新现场读取,后者将用空字符串代替
no 走到可穿透的楼梯上不触发楼层切换事件,通过本插件可以让勇士停在旁边没有障碍物的楼梯上哦~
move:x:y 尝试瞬移到 [x,y] 点(不改变朝向),该点甚至可以和勇士相邻或者位于视野外
key:n 松开键值为n的键如 key:49 表示松开大键盘数字键1默认会触发使用破墙镐
click:n:px:py 点击自绘状态栏n为0表示横屏1表示竖屏[px,py] 为点击的像素坐标
random:n 生成了随机数n即 core.rand2(num) 的返回结果n必须在 [0,num-1] 范围num必须为正整数。此项缺失将导致现场重新随机生成数值可能导致回放结果不一致
作者自定义的新项一般为js对象可以先JSON.stringify()再core.encodeBase64()得到纯英文数字的内容)需要用(半角圆括弧)括起来。
当您使用数字键5将一些项追加到即将播放内容的开头时请注意要逆序逐项追加或者每追加一项就按下数字键7或字母键N单步播放一步。
但是【input input2 random choices】是被动读取的单步播放如果触发了相应的事件就会连续读取这时候只能提前逐项追加好。
电脑端熟练以后推荐直接在控制台操作 core.status.route 和 core.status.replay.toReplay后者录像回放时才有配合 core.push() 和 core.unshift() 更加灵活自由哦!
*/
core.actions.registerAction(
"onkeyUp",
"_sys_onkeyUp_replay",
function (e) {
if (this._checkReplaying()) {
if (e.keyCode == 27)
// ESCAPE
core.stopReplay();
else if (e.keyCode == 90)
// Z
core.speedDownReplay();
else if (e.keyCode == 67)
// C
core.speedUpReplay();
else if (e.keyCode == 32)
// SPACE
core.triggerReplay();
else if (e.keyCode == 65)
// A
core.rewindReplay();
else if (e.keyCode == 83)
// S
core.control._replay_SL();
else if (e.keyCode == 88)
// X
core.control._replay_book();
else if (e.keyCode == 33 || e.keyCode == 34)
// PgUp/PgDn
core.control._replay_viewMap();
else if (e.keyCode == 78)
// N
core.stepReplay();
else if (e.keyCode == 84)
// T
core.control._replay_toolbox();
else if (e.keyCode == 81)
// Q
core.control._replay_equipbox();
else if (e.keyCode == 66)
// B
core.ui._drawStatistics();
else if (e.keyCode == 49 || e.keyCode == 54)
// 1/6原速/24倍速播放
core.setReplaySpeed(e.keyCode == 49 ? 1 : 24);
else if (e.keyCode > 49 && e.keyCode < 54) {
// 2-5录像精修
switch (e.keyCode - 48) {
case 2: // pop
alert(
"您已移除已录制内容的最后一项:" + core.status.route.pop()
);
break;
case 3: // push
core.utils.myprompt(
"请输入您要追加到已录制内容末尾的项:",
"",
function (value) {
if (value != null) core.status.route.push(value);
}
);
break;
case 4: // shift
alert(
"您已移除即将播放内容的第一项:" +
core.status.replay.toReplay.shift()
);
break;
case 5: // unshift
core.utils.myprompt(
"请输入您要追加到即将播放内容开头的项:",
"",
function (value) {
if (value != null)
core.status.replay.toReplay.unshift(value);
}
);
}
}
return true;
}
},
100
);
},
"numpad": function () {
// 样板自带的整数输入事件为白屏弹窗且可以误输入任意非法内容但不支持负整数观感较差。本插件可以将其美化成仿RM样式使其支持负整数同时带有音效
// 另一方面4399等第三方平台不允许使用包括 core.myprompt() 和 core.myconfirm() 在内的弹窗,因此也需要此插件来替代,不然类似生命魔杖的道具就不好实现了
// 关于负整数输入V2.8.2原生支持其录像的压缩和解压,只是默认的 core.events._action_input() 函数将负数取了绝对值,可以只复写下面的 core.isReplaying() 部分来取消
// 是否启用本插件false表示禁用true表示启用
var __enable = true;
if (!__enable) return;
core.events._action_input = function (data, x, y, prefix) {
// 复写整数输入事件
if (core.isReplaying()) {
// 录像回放时,处理方式不变,但增加负整数支持
core.events.__action_getInput(
core.replaceText(data.text, prefix),
false,
function (value) {
value = parseInt(value) || 0; // 去掉了取绝对值的步骤
core.status.route.push("input:" + value);
core.setFlag("input", value);
core.doAction();
}
);
} else {
// 正常游戏中,采用暂停录制的方式然后用事件流循环“绘制-等待-变量操作”三板斧实现按照13*13适配的
// 您可以自行修改循环内的内容来适配15*15或其他需求或干脆作为公共事件编辑。
core.insertAction(
[
// 记录当前录像长度,下面的循环结束后裁剪。达到“暂停录制”的效果
{
type: "function",
function:
"function(){flags['@temp@length']=core.status.route.length}",
},
{ type: "setValue", name: "flag:input", value: "0" },
{
type: "while",
condition: "true",
data: [
{
type: "drawBackground",
background: "winskin.webp",
x: 16,
y: 16,
width: 384,
height: 384,
},
{ type: "drawIcon", id: "X10181", x: 32, y: 288 },
{ type: "drawIcon", id: "X10185", x: 64, y: 288 },
{ type: "drawIcon", id: "X10186", x: 96, y: 288 },
{ type: "drawIcon", id: "X10187", x: 128, y: 288 },
{ type: "drawIcon", id: "X10188", x: 160, y: 288 },
{ type: "drawIcon", id: "X10189", x: 192, y: 288 },
{ type: "drawIcon", id: "X10193", x: 224, y: 288 },
{ type: "drawIcon", id: "X10194", x: 256, y: 288 },
{ type: "drawIcon", id: "X10195", x: 288, y: 288 },
{ type: "drawIcon", id: "X10196", x: 320, y: 288 },
{ type: "drawIcon", id: "X10197", x: 352, y: 288 },
{ type: "drawIcon", id: "X10286", x: 32, y: 352 },
{ type: "drawIcon", id: "X10169", x: 96, y: 352 },
{ type: "drawIcon", id: "X10232", x: 128, y: 352 },
{ type: "drawIcon", id: "X10185", x: 320, y: 352 },
{ type: "drawIcon", id: "X10242", x: 352, y: 352 },
{
type: "fillBoldText ",
x: 48,
y: 256,
style: [255, 255, 255, 1],
font: "bold 32px Consolas",
text: "${flag:input}",
},
{
type: "fillBoldText",
x: 32,
y: 48,
style: [255, 255, 255, 1],
font: "16px Consolas",
text: core.replaceText(data.text, prefix),
},
{
type: "wait",
forceChild: true,
data: [
{
case: "keyboard",
keycode: "48,49,50,51,52,53,54,55,56,57",
action: [
// 按下数字键追加到已输入内容的末尾但禁止越界。变量keycode-48就是末位数字
{ type: "playSound", name: "光标移动" },
{
type: "if",
condition: "(flag:input<0)",
true: [
{
type: "setValue",
name: "flag:input",
value: "10*flag:input-(flag:keycode-48)",
},
],
false: [
{
type: "setValue",
name: "flag:input",
value: "10*flag:input+(flag:keycode-48)",
},
],
},
{
type: "setValue",
name: "flag:input",
value: "core.clamp(flag:input,-9e15,9e15)",
},
],
},
{
case: "keyboard",
keycode: "189",
action: [
// 按下减号键,变更已输入内容的符号
{ type: "playSound", name: "跳跃" },
{
type: "setValue",
name: "flag:input",
value: "-flag:input",
},
],
},
{
case: "keyboard",
keycode: "8",
action: [
// 按下退格键,从已输入内容的末尾删除一位
{ type: "playSound", name: "取消" },
{
type: "setValue",
name: "flag:input",
operator: "//=",
value: "10",
},
],
},
{
case: "keyboard",
keycode: "27",
action: [
// 按下ESC键清空已输入内容
{ type: "playSound", name: "读档" },
{ type: "setValue", name: "flag:input", value: "0" },
],
},
{
case: "keyboard",
keycode: "13",
action: [
// 按下回车键,确定
{ type: "break", n: 1 },
],
},
{
case: "mouse",
px: [32, 63],
py: [288, 320],
action: [
// 点击减号变号。右边界写63防止和下面重叠
{ type: "playSound", name: "跳跃" },
{
type: "setValue",
name: "flag:input",
value: "-flag:input",
},
],
},
{
case: "mouse",
px: [64, 384],
py: [288, 320],
action: [
// 点击数字追加到已输入内容的末尾但禁止越界。变量x-2就是末位数字
{ type: "playSound", name: "光标移动" },
{
type: "if",
condition: "(flag:input<0)",
true: [
{
type: "setValue",
name: "flag:input",
value: "10*flag:input-(flag:x-2)",
},
],
false: [
{
type: "setValue",
name: "flag:input",
value: "10*flag:input+(flag:x-2)",
},
],
},
{
type: "setValue",
name: "flag:input",
value: "core.clamp(flag:input,-9e15,9e15)",
},
],
},
{
case: "mouse",
px: [32, 64],
py: [352, 384],
action: [
// 点击左箭头,退格
{ type: "playSound", name: "取消" },
{
type: "setValue",
name: "flag:input",
operator: "//=",
value: "10",
},
],
},
{
case: "mouse",
px: [96, 160],
py: [352, 384],
action: [
// 点击CE清空
{ type: "playSound", name: "读档" },
{ type: "setValue", name: "flag:input", value: "0" },
],
},
{
case: "mouse",
px: [320, 384],
py: [352, 384],
action: [
// 点击OK确定
{ type: "break", n: 1 },
],
},
],
},
],
},
{ type: "clearMap" },
// 裁剪录像,只保留'input:n',然后继续录制
{
type: "function",
function:
"function(){core.status.route.splice(flags['@temp@length']);core.status.route.push('input:'+core.getFlag('input',0))}",
},
],
x,
y
);
core.events.doAction();
}
};
},
"sprites": function () {
// 基于canvas的sprite化摘编整理自万宁魔塔
//
// ---------------------------------------- 第一部分 js代码 (必装) --------------------------------------- //
/* ---------------- 用法说明 ---------------- *
* 1. 创建sprite: var sprite = new Sprite(x, y, w, h, z, reference, name);
* 其中x y w h为画布的横纵坐标及长宽reference为参考系只能填game相对于游戏画面和window相对于窗口
* 且当为相对游戏画面时长宽与坐标将会乘以放缩比例相当于用createCanvas创建
* z为纵深表示不同元素之间的覆盖关系大的覆盖小的
* name为自定义名称可以不填
* 2. 删除: sprite.destroy();
* 3. 设置css特效: sprite.setCss(css);
* 其中css直接填 box-shadow: 0px 0px 10px black;的形式即可与style标签与css文件内写法相同
* 对于已设置的特效,如果之后不需要再次设置,可以不填
* 4. 添加事件监听器: sprite.addEventListener(); 用法与html元素的addEventListener完全一致
* 5. 移除事件监听器: sprite.removeEventListener(); 用法与html元素的removeEventListener完全一致
* 6. 属性列表
* (1) sprite.x | sprite.y | sprite.width | sprite.height | sprite.zIndex | sprite.reference 顾名思义
* (2) sprite.canvas 该sprite的画布
* (3) sprite.context 该画布的CanvasRenderingContext2d对象即样板中常见的ctx
* (4) sprite.count 不要改这个玩意
* 7. 使用样板api进行绘制
* 示例:
* var ctx = sprite.context;
* core.fillText(ctx, 'xxx', 100, 100);
* core.fillRect(ctx, 0, 0, 50, 50);
* 当然也可以使用原生js
* ctx.moveTo(0, 0);
* ctx.bezierCurveTo(50, 50, 100, 0, 100, 50);
* ctx.stroke();
* ---------------- 用法说明 ---------------- */
var count = 0;
/** 创建一个sprite画布
* @param {number} x
* @param {number} y
* @param {number} w
* @param {number} h
* @param {number} z
* @param {'game' | 'window'} reference 参考系,游戏画面或者窗口
* @param {string} name 可选sprite的名称方便通过core.dymCanvas获取
*/
function Sprite(x, y, w, h, z, reference, name) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.zIndex = z;
this.reference = reference;
this.canvas = null;
this.context = null;
this.count = 0;
this.name = name || "_sprite_" + count;
this.style = null;
/** 初始化 */
this.init = function () {
if (reference === "window") {
var canvas = document.createElement("canvas");
this.canvas = canvas;
this.context = canvas.getContext("2d");
canvas.width = w;
canvas.height = h;
canvas.style.width = w + "px";
canvas.style.height = h + "px";
canvas.style.position = "absolute";
canvas.style.top = y + "px";
canvas.style.left = x + "px";
canvas.style.zIndex = z.toString();
document.body.appendChild(canvas);
this.style = canvas.style;
} else {
this.context = core.createCanvas(
this.name || "_sprite_" + count,
x,
y,
w,
h,
z
);
this.canvas = this.context.canvas;
this.canvas.style.pointerEvents = "auto";
this.style = this.canvas.style;
}
this.count = count;
count++;
};
this.init();
/** 设置css特效
* @param {string} css
*/
this.setCss = function (css) {
css = css.replace("\n", ";").replace(";;", ";");
var effects = css.split(";");
var self = this;
effects.forEach(function (v) {
var content = v.split(":");
var name = content[0];
var value = content[1];
name = name
.trim()
.split("-")
.reduce(function (pre, curr, i, a) {
if (i === 0 && curr !== "") return curr;
if (a[0] === "" && i === 1) return curr;
return pre + curr.toUpperCase()[0] + curr.slice(1);
}, "");
var canvas = self.canvas;
if (name in canvas.style) canvas.style[name] = value;
});
return this;
};
/**
* 移动sprite
* @param {boolean} isDelta 是否是相对位置如果是那么sprite会相对于原先的位置进行移动
*/
this.move = function (x, y, isDelta) {
if (x !== undefined && x !== null) this.x = x;
if (y !== undefined && y !== null) this.y = y;
if (this.reference === "window") {
var ele = this.canvas;
ele.style.left =
x + (isDelta ? parseFloat(ele.style.left) : 0) + "px";
ele.style.top = y + (isDelta ? parseFloat(ele.style.top) : 0) + "px";
} else core.relocateCanvas(this.context, x, y, isDelta);
return this;
};
/**
* 重新设置sprite的大小
* @param {boolean} styleOnly 是否只修改css效果如果是那么将会不高清如果不是那么会清空画布
*/
this.resize = function (w, h, styleOnly) {
if (w !== undefined && w !== null) this.w = w;
if (h !== undefined && h !== null) this.h = h;
if (reference === "window") {
var ele = this.canvas;
ele.style.width = w + "px";
ele.style.height = h + "px";
if (!styleOnly) {
ele.width = w;
ele.height = h;
}
} else core.resizeCanvas(this.context, w, h, styleOnly);
return this;
};
/**
* 旋转画布
*/
this.rotate = function (angle, cx, cy) {
if (this.reference === "window") {
var left = this.x;
var top = this.y;
this.canvas.style.transformOrigin =
cx - left + "px " + (cy - top) + "px";
if (angle === 0) {
canvas.style.transform = "";
} else {
canvas.style.transform = "rotate(" + angle + "deg)";
}
} else {
core.rotateCanvas(this.context, angle, cx, cy);
}
return this;
};
/**
* 清除sprite
*/
this.clear = function (x, y, w, h) {
if (this.reference === "window") {
this.context.clearRect(x, y, w, h);
} else {
core.clearMap(this.context, x, y, w, h);
}
return this;
};
/** 删除 */
this.destroy = function () {
if (this.reference === "window") {
if (this.canvas) document.body.removeChild(this.canvas);
} else {
core.deleteCanvas(this.name || "_sprite_" + this.count);
}
};
/** 添加事件监听器 */
this.addEventListener = function () {
this.canvas.addEventListener.apply(this.canvas, arguments);
};
/** 移除事件监听器 */
this.removeEventListener = function () {
this.canvas.removeEventListener.apply(this.canvas, arguments);
};
}
window.Sprite = Sprite;
},
"hotReload": function () {
/* ---------- 功能说明 ---------- *
1. 当 libs/ main.js index.html 中的任意一个文件被更改后,会自动刷新塔的页面
2. 修改楼层文件后自动在塔的页面上显示出来,不需要刷新
3. 修改脚本编辑或插件编写后也能自动更新更改的插件或脚本,但不保证不会出问题(一般都不会有问题的
4. 修改图块属性、怪物属性等后会自动更新
5. 当全塔属性被修改时,会自动刷新塔的页面
6. 样板的 styles.css 被修改后也可以直接显示,不需要刷新
7. 其余内容修改后不会自动更新也不会刷新
/* ---------- 使用方式 ---------- *
1. 前往 https://nodejs.org/en/ 下载node.js的LTS版本点左边那个绿色按钮并安装
2. 将该插件复制到插件编写中
3. 在造塔群的群文件-魔塔样板·改中找到server.js下载并放到塔的根目录与启动服务同一级
4. 在该目录下按下shift+鼠标右键win11只按右键即可选择在终端打开或在powershell打开
5. 运行node server.js即可
*/
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 = (e) => {
if (xhr.status !== 200) {
console.error(`hot reload: http ${xhr.status}`);
res("@error");
} else res("success");
};
xhr.onerror = (e) => {
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 all = Array.from(document.getElementsByTagName("link"));
all.forEach((v) => {
if (v.rel !== "stylesheet") return;
if (v.href === `http://127.0.0.1:3000/${data}`) {
v.remove();
const link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = data;
document.head.appendChild(link);
console.log(`css hot reload: ${data}`);
}
});
}
/**
* 热重载楼层
* @param {string} data
*/
async function reloadFloor(data) {
// 首先重新加载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);
core.setWeather(
core.animateFrame.weather.type,
core.animateFrame.weather.level
);
}
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);
}
})();
},
"statusBar": function () {
main.dom.floorMsgGroup.style.display = "none";
main.dom.statusBar.style.display = "none";
main.dom.toolBar.style.display = "none";
//所有数据*3是为了实现高清画布
const GAMEVIEW_WIDTH = 676 * 3; //横屏画面宽度
const GAMEVIEW_HEIGHT = 416 * 3; //横屏画面高度
const GAMEVIEW_WIDTH_VERTICAL = 416 * 3; //竖屏画面宽度
const GAMEVIEW_HEIGHT_VERTICAL = 676 * 3; //竖屏画面高度
const BAR_WIDTH = 130 * 3; //横屏左侧额外距离(即边栏宽度)
const BAR_HEIGHT_VERTICAL = 130 * 3; //竖屏上侧额外距离(即边栏高度)
const BORDER_WIDTH = 0; //游戏画面左侧偏移距离
const BORDER_HEIGHT = 0; //游戏画面上侧偏移距离
const ITEM_BOX_LEFT = 549 * 3; //横屏道具栏左侧距离右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT
const ITEM_BOX_TOP = 175 * 3; //横屏道具栏上侧距离
const ITEM_BOX_LEFT_VERTICAL = 160 * 3; //竖屏道具栏左侧距离
const ITEM_BOX_TOP_VERTICAL = 549 * 3; //竖屏道具栏上侧距离下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL
const EQUIP_BLOCK_LEFT = 549 * 3; //横屏装备栏左侧距离右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT
const EQUIP_BLOCK_TOP = 10 * 3; //横屏装备栏上侧距离
const EQUIP_BLOCK_LEFT_VERTICAL = 10 * 3; //竖屏装备栏左侧距离
const EQUIP_BLOCK_TOP_VERTICAL = 549 * 3; //竖屏装备栏上侧距离下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL
const MAP_BLOCK_LEFT = 551 * 3; //横屏小地图左侧距离右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT
const MAP_BLOCK_TOP = 0; //横屏小地图上侧距离
const MAP_BLOCK_LEFT_VERTICAL = 0; //竖屏小地图左侧距离
const MAP_BLOCK_TOP_VERTICAL = 551 * 3; //竖屏小地图上侧距离下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL
const KEY_BLOCK_LEFT = EQUIP_BLOCK_LEFT; //横屏钥匙栏左侧距离右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT
const KEY_BLOCK_TOP = 130 * 3; //横屏钥匙栏上侧距离
const KEY_BLOCK_LEFT_VERTICAL = 110 * 3; //竖屏钥匙栏左侧距离
const KEY_BLOCK_TOP_VERTICAL = EQUIP_BLOCK_TOP_VERTICAL; //竖屏钥匙栏上侧距离下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL
const INFO_BLOCK_LEFT = 10 * 3; //横屏道具说明左侧距离右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT
const INFO_BLOCK_TOP = 290 * 3; //横屏道具说明上侧距离
const INFO_BLOCK_LEFT_VERTICAL = 113 * 3; //竖屏道具说明左侧距离
const INFO_BLOCK_TOP_VERTICAL = 8 * 3; //竖屏道具说明上侧距离下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL
const TOOL_BOX_LEFT = EQUIP_BLOCK_LEFT; //横屏工具栏左侧距离右侧边栏需增加BAR_WIDTH+GAMEVIEW_HEIGHT
const TOOL_BOX_TOP = 348 * 3; //横屏工具栏上侧距离
const TOOL_BOX_LEFT_VERTICAL = 348 * 3; //竖屏工具栏左侧距离
const TOOL_BOX_TOP_VERTICAL = 549 * 3; //竖屏工具栏上侧距离下侧边栏需增加BAR_HEIGHT_VERTICAL+GAMEVIEW_WIDTH_VERTICAL
const TOOL_ICON_OUTER_SIZE = 34 * 3;
const TEXT_COLOR = "#FFFFFF"; //默认文字颜色
const globalAlpha = 0.7; //默认底框透明度
const FORCE_COUNTABLE_ITEMS = ["centerFly"]; //常态显示数量的非永久道具如果道具不在此数组中则只有道具多余1时显示数量
const outerBackground = document.createElement("canvas"); //背景画布设置
let globalAlphafloor = 0,
globalAlphafloorStatus = 4;
outerBackground.style.position = "absolute";
outerBackground.style.zIndex = 5;
outerBackground.id = "outerBackground";
main.dom.outerBackground = outerBackground;
main.dom.startPanel.insertAdjacentElement("afterend", outerBackground);
const outerUI = document.createElement("canvas"); //额外ui画布设置状态栏所有绘制、点击都在额外ui上
outerUI.style.position = "absolute";
outerUI.style.zIndex = 165;
outerUI.id = "outerUI";
main.dom.outerUI = outerUI;
outerBackground.insertAdjacentElement("afterend", outerUI);
setTimeout(function () {
// Should be executed immediately after init()
main.canvas.outerUI = outerUI.getContext("2d");
});
outerUI.onclick = function (e) {
try {
e.preventDefault();
if (!core.isPlaying()) return false;
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor((e.clientX - left) / core.domStyle.scale),
py = Math.floor((e.clientY - top) / core.domStyle.scale);
core.ui.statusBar.onclick(px * 3, py * 3);
} catch (ee) {
main.log(ee);
}
};
const _resize_gameGroup = function (obj) {
//游戏画面自适应调节
const gameGroup = core.dom.gameGroup;
gameGroup.style.width = obj.totalWidth + "px";
gameGroup.style.height = obj.totalHeight + "px";
gameGroup.style.left = (obj.clientWidth - obj.totalWidth) / 2 + "px";
gameGroup.style.top = (obj.clientHeight - obj.totalHeight) / 2 + "px";
//floorMsgGroup为切换楼层中生效显示时间可通过全塔属性——切换楼层时间或游戏内设置调整
//显示内容为游戏名/版本号/楼层名
// floorMsgGroup
var floorMsgGroup = core.dom.floorMsgGroup;
var globalAttribute =
core.status.globalAttribute || core.initStatus.globalAttribute;
floorMsgGroup.style = globalAttribute.floorChangingStyle;
floorMsgGroup.style.height = floorMsgGroup.style.width =
(GAMEVIEW_HEIGHT / 3) * core.domStyle.scale + "px";
floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px";
if (core.domStyle.isVertical) {
floorMsgGroup.style.left = "0px";
floorMsgGroup.style.top =
((GAMEVIEW_HEIGHT_VERTICAL / 3 - GAMEVIEW_WIDTH_VERTICAL / 3) *
core.domStyle.scale) /
2 +
"px";
} else {
floorMsgGroup.style.left =
((GAMEVIEW_WIDTH / 3 - GAMEVIEW_HEIGHT / 3) * core.domStyle.scale) /
2 +
"px";
floorMsgGroup.style.top = "0px";
}
core.dom.musicBtn.style.right =
(obj.clientWidth - obj.totalWidth) / 2 + "px";
core.dom.musicBtn.style.bottom =
(obj.clientHeight - obj.totalHeight) / 2 - 27 + "px";
let startBackground = core.domStyle.isVertical ?
main.styles.startVerticalBackground || main.styles.startBackground :
main.styles.startBackground;
if (main.dom.startBackground.getAttribute("__src__") != startBackground) {
main.dom.startBackground.setAttribute("__src__", startBackground);
main.dom.startBackground.src = startBackground;
}
const span = document
.getElementById("startButtons")
.getElementsByTagName("span");
let font = (GAMEVIEW_WIDTH / 100) * core.domStyle.scale;
if (core.domStyle.isVertical)
font = ((GAMEVIEW_WIDTH_VERTICAL * 2) / 100) * core.domStyle.scale;
core.dom.playGame.style.fontSize = font + "px";
core.dom.loadGame.style.fontSize = font + "px";
core.dom.CGMode.style.fontSize = font + "px";
core.dom.musicMode.style.fontSize = font + "px";
core.dom.replayGame.style.fontSize = font + "px";
core.dom.startButtonGroup.style.padding = font * 0.3 + "px 25px";
};
const _resize_canvas = function (obj) {
//自适应画布
main.dom.outerBackground.style.width = obj.totalWidth + "px";
main.dom.outerBackground.style.height = obj.totalHeight + "px";
main.dom.outerUI.style.width = obj.totalWidth + "px";
main.dom.outerUI.style.height = obj.totalHeight + "px";
if (main.dom.CGUI) {
main.dom.CGUI.style.width = obj.totalWidth + 3 + "px";
main.dom.CGUI.style.height = obj.totalHeight + 3 + "px";
}
if (main.dom.music) {
main.dom.music.style.width = obj.totalWidth + 3 + "px";
main.dom.music.style.height = obj.totalHeight + 3 + "px";
}
if (main.dom.cgText) {
main.dom.cgText.style.width = obj.totalWidth + 3 + "px";
main.dom.cgText.style.height = obj.totalHeight + 3 + "px";
}
if (main.dom.logcanvas) {
main.dom.logcanvas.style.width = obj.totalWidth + 3 + "px";
main.dom.logcanvas.style.height = obj.totalHeight + 3 + "px";
}
if (main.dom.over) {
main.dom.over.style.width = obj.totalWidth + 3 + "px";
main.dom.over.style.height = obj.totalHeight + 3 + "px";
}
if (main.dom.book) {
main.dom.book.style.width = obj.totalWidth + 3 + "px";
main.dom.book.style.height = obj.totalHeight + 3 + "px";
}
if (main.dom.video) {
main.dom.video.style.width = obj.totalWidth + 3 + "px";
main.dom.video.style.height = obj.totalHeight + 3 + "px";
if (core.domStyle.isVertical)
main.dom.video.style.width = obj.totalHeight + 3 + "px";
if (core.domStyle.isVertical)
main.dom.video.style.height = obj.totalWidth + 3 + "px";
main.dom.video.style.top = "50%";
main.dom.video.style.left = "50%";
main.dom.video.style.transform = "translate(-50%,-50%)";
if (core.domStyle.isVertical)
main.dom.video.style.transform = "translate(-50%,-50%) rotate(90deg)";
}
if (main.dom.video1) {
main.dom.video1.style.width = obj.totalWidth + 3 + "px";
main.dom.video1.style.height = obj.totalHeight + 3 + "px";
}
main.dom.boss1.style.width = obj.totalWidth + 3 + "px";
main.dom.boss1.style.height = obj.totalHeight + 3 + "px";
main.dom.boss2.style.width = obj.totalWidth + 3 + "px";
main.dom.boss2.style.height = obj.totalHeight + 3 + "px";
main.dom.boss3.style.width = obj.totalWidth + 3 + "px";
main.dom.boss3.style.height = obj.totalHeight + 3 + "px";
main.dom.boss4.style.width = obj.totalWidth + 3 + "px";
main.dom.boss4.style.height = obj.totalHeight + 3 + "px";
main.dom.boss5.style.width = obj.totalWidth + 3 + "px";
main.dom.boss5.style.height = obj.totalHeight + 3 + "px";
main.dom.boss6.style.width = obj.totalWidth + 3 + "px";
main.dom.boss6.style.height = obj.totalHeight + 3 + "px";
main.dom.boss7.style.width = obj.totalWidth + 3 + "px";
main.dom.boss7.style.height = obj.totalHeight + 3 + "px";
main.dom.boss8.style.width = obj.totalWidth + 3 + "px";
main.dom.boss8.style.height = obj.totalHeight + 3 + "px";
main.dom.boss.style.width = obj.totalWidth + 3 + "px";
main.dom.boss.style.height = obj.totalHeight + 3 + "px";
const innerSize = obj.canvasWidth * core.domStyle.scale + "px";
for (let i = 0; i < core.dom.gameCanvas.length; ++i)
core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[
i
].style.height = innerSize;
core.dom.gif.style.width = core.dom.gif.style.height = innerSize;
core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize;
core.dom.gameDraw.style.width = core.dom.gameDraw.style.height =
innerSize;
core.dom.gameDraw.style.top =
obj.gameDrawBox.top * core.domStyle.scale + "px";
core.dom.gameDraw.style.left =
obj.gameDrawBox.left * core.domStyle.scale + "px";
// resize bigmap
core.bigmap.canvas.forEach(function (cn) {
const ratio = core.canvas[cn].canvas.hasAttribute("isHD") ?
core.domStyle.ratio :
1;
core.canvas[cn].canvas.style.width =
(innerSize / ratio) * core.domStyle.scale + "px";
core.canvas[cn].canvas.style.height =
(innerSize / ratio) * core.domStyle.scale + "px";
});
// resize dynamic canvas
for (const name in core.dymCanvas) {
const ctx = core.dymCanvas[name],
canvas = ctx.canvas;
const ratio = canvas.hasAttribute("isHD") ? core.domStyle.ratio : 1;
canvas.style.width = (innerSize / ratio) * core.domStyle.scale + "px";
canvas.style.height = (innerSize / ratio) * core.domStyle.scale + "px";
canvas.style.left =
parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px";
canvas.style.top =
parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px";
}
// resize next
main.dom.next.style.width = main.dom.next.style.height =
5 * core.domStyle.scale + "px";
main.dom.next.style.borderBottomWidth =
main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px";
};
const bgctx = main.dom.outerBackground.getContext("2d");
const uictx = main.dom.outerUI.getContext("2d");
let now = 0;
core.registerAnimationFrame("lightFloor", true, function (timestamp) {
if (timestamp - now > 1000 / 60) {
now = timestamp;
globalAlphafloor += globalAlphafloorStatus;
if (globalAlphafloor === 100) globalAlphafloorStatus = -2;
if (globalAlphafloor === 0) globalAlphafloorStatus = 2;
if (core.domStyle.isVertical) {
core.clearMap(
uictx,
MAP_BLOCK_LEFT_VERTICAL,
MAP_BLOCK_TOP_VERTICAL,
340,
360
);
if (core.status.event.id === "viewMaps") {
core.ui.statusBar._update_map(core.status.event.data.floorId);
} else {
core.ui.statusBar._update_map();
}
uictx.globalAlpha = globalAlphafloor / 100;
core.drawImage(
uictx,
"green.webp",
MAP_BLOCK_LEFT_VERTICAL + 135,
MAP_BLOCK_TOP_VERTICAL + 170
);
uictx.globalAlpha = 1;
} else {
core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360);
if (core.status.event.id === "viewMaps") {
core.ui.statusBar._update_map(core.status.event.data.floorId);
} else {
core.ui.statusBar._update_map();
}
uictx.globalAlpha = globalAlphafloor / 100;
core.drawImage(
uictx,
"green.webp",
MAP_BLOCK_LEFT + 150,
MAP_BLOCK_TOP + 180
);
uictx.globalAlpha = 1;
}
}
});
core.control.resize = function () {
//自适应,可实现横竖屏切换
if (main.mode == "editor") return;
const clientWidth = main.dom.body.clientWidth,
clientHeight = main.dom.body.clientHeight;
const canvasWidth = core.__PIXELS__;
const isVertical = clientHeight > clientWidth;
core.domStyle.isVertical = isVertical;
const totalWidth = isVertical ?
GAMEVIEW_WIDTH_VERTICAL / 3 :
GAMEVIEW_WIDTH / 3,
totalHeight = isVertical ?
GAMEVIEW_HEIGHT_VERTICAL / 3 :
GAMEVIEW_HEIGHT / 3;
const maxRatio = Math.min(
clientWidth / totalWidth,
clientHeight / totalHeight
);
core.domStyle.availableScale = [];
[1, 1.25, 1.5, 1.75, 2].forEach(function (v) {
if (maxRatio >= v) {
core.domStyle.availableScale.push(v);
}
});
if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) {
core.domStyle.scale = Math.min(1, maxRatio);
} else if (
core.getLocalStorage("scale") == null &&
core.domStyle.availableScale.length >= 2
) {
core.domStyle.scale =
core.domStyle.availableScale[core.domStyle.availableScale.length - 2];
core.setLocalStorage("scale", core.domStyle.scale);
}
const totalWidthScaled = totalWidth * core.domStyle.scale,
totalHeightScaled = totalHeight * core.domStyle.scale;
const gameDrawBox = isVertical ? {
left: BORDER_WIDTH / 3,
top: BAR_HEIGHT_VERTICAL / 3 + BORDER_HEIGHT / 3,
} : { left: BAR_WIDTH / 3 + BORDER_WIDTH / 3, top: BORDER_HEIGHT / 3 };
const obj = {
clientWidth: clientWidth,
clientHeight: clientHeight,
canvasWidth: canvasWidth,
totalWidth: totalWidthScaled,
totalHeight: totalHeightScaled,
gameDrawBox: gameDrawBox,
globalAttribute: core.status.globalAttribute || core.initStatus.globalAttribute,
};
_resize_gameGroup(obj);
_resize_canvas(obj);
if (core.status.automaticRoute == null) core.status.automaticRoute = {};
core.updateStatusBar();
if (main.dom.CGUI && main.dom.CGUI.style.display === "block")
core.ui.CG.update();
if (main.dom.music && main.dom.music.style.display === "block")
core.ui.music.update();
if (main.dom.cgText && main.dom.cgText.style.display === "block")
core.ui.cgText.update();
if (main.dom.logcanvas && main.dom.logcanvas.style.display === "block")
core.ui.cgText.update();
if (main.dom.boss1 && main.dom.boss1.style.display === "block")
core.ui.boss.update();
};
class StatusBar {
constructor() {
//道具栏列表
this.itemMx = [
//空位用none填充当前ui至多4列6行
["book", "postman", "I369", "fly"],
["cross", "superPotion", "pickaxe"],
["bomb", "centerFly", "upFly"],
["none", "none", "none"],
["downFly", "knife", "snow"],
["bigKey", "earthquake", "coin"],
];
}
//初始化内容(工具栏/录像操作执行函数)
init() {
this.toolbarAction = [
[
main.core.openKeyBoard,
main.core.openQuickShop,
core.openToolbox,
core.doSL,
],
[main.core.save, main.core.load, main.core.openSettings, core.doSL],
];
this.replayAction = [
[core.triggerReplay, core.stopReplay, core.rewindReplay],
[
core.speedDownReplay,
core.speedUpReplay,
function () {
core.control._replay_SL();
},
],
];
}
//更新
update() {
this._update_background(); //更新背景
this._update_props(); //更新属性
this._update_items(); //更新道具
//this._update_equips(); //更新装备
this._update_keys(); //更新钥匙
//this._update_infoWindow(); //更新道具说明
this._update_toolBox(); //更新工具栏
this._redrawMap();
}
_redrawMap() {
if (core.domStyle.isVertical) {
core.clearMap(
uictx,
MAP_BLOCK_LEFT_VERTICAL,
MAP_BLOCK_TOP_VERTICAL,
340,
360
);
this._update_map();
uictx.globalAlpha = globalAlphafloor / 100;
core.drawImage(
uictx,
"green.webp",
MAP_BLOCK_LEFT_VERTICAL + 125,
MAP_BLOCK_TOP_VERTICAL + 170
);
uictx.globalAlpha = 1;
} else {
core.clearMap(uictx, MAP_BLOCK_LEFT, MAP_BLOCK_TOP, 340, 360);
this._update_map();
uictx.globalAlpha = globalAlphafloor / 100;
core.drawImage(
uictx,
"green.webp",
MAP_BLOCK_LEFT + 150,
MAP_BLOCK_TOP + 170
);
uictx.globalAlpha = 1;
}
}
//更新背景
_update_background() {
if (core.domStyle.isVertical) {
bgctx.canvas.width = GAMEVIEW_WIDTH_VERTICAL;
bgctx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL;
uictx.canvas.width = GAMEVIEW_WIDTH_VERTICAL;
uictx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL;
const bg = core.material.images.images["status.webp"]; //竖屏背景(上)
bgctx.drawImage(
bg,
0,
0,
GAMEVIEW_WIDTH_VERTICAL,
BAR_HEIGHT_VERTICAL
);
const bg2 = core.material.images.images["status.webp"]; //竖屏背景(下)
bgctx.drawImage(
bg2,
0,
BAR_HEIGHT_VERTICAL + GAMEVIEW_WIDTH_VERTICAL,
GAMEVIEW_WIDTH_VERTICAL,
BAR_HEIGHT_VERTICAL
);
bgctx.globalAlpha = globalAlpha;
bgctx.globalAlpha = 1;
core.setTextAlign("outerUI", "center");
} else {
bgctx.canvas.width = GAMEVIEW_WIDTH;
bgctx.canvas.height = GAMEVIEW_HEIGHT;
uictx.canvas.width = GAMEVIEW_WIDTH;
uictx.canvas.height = GAMEVIEW_HEIGHT;
const bg = core.material.images.images["status.webp"]; //横屏背景(左)
bgctx.drawImage(bg, 0, 0, BAR_WIDTH, GAMEVIEW_HEIGHT);
const bg2 = core.material.images.images["status.webp"]; //横屏背景(右)
bgctx.drawImage(
bg2,
BAR_WIDTH + GAMEVIEW_HEIGHT,
0,
BAR_WIDTH,
GAMEVIEW_HEIGHT
);
bgctx.globalAlpha = globalAlpha;
bgctx.globalAlpha = 1;
core.setTextAlign("outerUI", "center");
}
}
// 更新属性
_update_props(updatedFloorTitle) {
if (!updatedFloorTitle && core.status.floorId) {
updatedFloorTitle = core.status.maps[core.status.floorId].title;
}
const statusList = [
"hp",
"atk",
"def",
"spell",
"mdef",
"matk",
"mhp",
"speed",
"money",
]; //属性列表图标在函数复写core.statusBar.icons中声明数字为project\materials\icons.png中的图标序号可使用便捷ps追加第一个序号为0
const drawStatusList = (baseX, baseY) => {
let curh = baseY;
core.setTextAlign("outerUI", "right");
statusList.forEach((item) => {
// 绘制图标
/*core.drawIcon(
"outerUI",
item,
baseX - 95 * 3,
curh - 18 * 3,
22 * 3,
22 * 3
);*/
core.strokeRect("outerUI", baseX - 96 * 3,
curh - 16 * 3, 100 * 3, 22 * 3, "#FFFFFF", 3)
core.setFont("outerUI", "bold 24px Verdana");
core.fillBoldText1(
"outerUI",
core.getStatusLabel(item),
baseX - 65 * 3,
curh - 4 * 3,
TEXT_COLOR,
"#000000",
6
);
core.setFont("outerUI", "bold 36px Verdana");
// 四舍五入
core.status.hero[item] = Math.round(core.status.hero[item]);
let text = core.getRealStatus(item);
// 大数据格式化
switch (item) {
case "mdef":
text += "%";
break;
case "matk":
case "mhp":
text = `${Math.floor(core.getRealStatus("spell")* core.getRealStatus(item)/100)}(${text})%`;
break;
}
core.fillBoldText1(
"outerUI",
text,
baseX,
curh,
TEXT_COLOR,
"#000000",
6
);
curh += 24 * 3;
if (curh > 130 * 3 && core.domStyle.isVertical) {
curh = 24 * 3;
baseX += 105 * 3;
}
});
core.setTextAlign("outerUI", "center");
};
if (core.domStyle.isVertical) {
core.clearMap("outerUI", 10 * 3, 0, 210 * 3, 120 * 3);
core.setFont("outerUI", "bold 42px Verdana");
if (updatedFloorTitle) {
core.fillBoldText1(
"outerUI",
updatedFloorTitle,
60 * 3,
22 * 3,
TEXT_COLOR,
"#000000",
6
);
}
drawStatusList(96 * 3, 46 * 3);
//core.drawImage("outerUI", "lane1.png", 0, 0)
//core.drawImage("outerUI", "cao.webp", 0, 0);
} else {
core.clearMap("outerUI", 10 * 3, 40 * 3, 105 * 3, 250 * 3);
core.setFont("outerUI", "bold 48px Verdana");
if (updatedFloorTitle) {
core.fillBoldText1(
"outerUI",
updatedFloorTitle,
62 * 3,
41 * 3,
TEXT_COLOR,
"#000000",
6
);
}
drawStatusList(110 * 3, 93 * 3);
//core.drawImage("outerUI", "lane1.png", 0, 30);
core.drawImage(
"outerUI",
"cao.webp",
0,
0,
400,
350,
0,
30,
360,
315
);
}
}
_update_items() {
//更新道具栏
const drawItemMx = (drawFn) => {
for (let i = 0; i < this.itemMx.length; i++) {
for (let j = 0; j < this.itemMx[i].length; j++) {
var item = this.itemMx[i][j];
drawFn(i, j, item);
}
}
};
const drawItem = (item, posx, posy) => {
const icon = core.material.icons.items[item],
image = core.material.images.items;
core.drawImage(
"outerUI",
image,
0,
32 * icon,
32,
32,
posx,
posy,
30 * 3,
30 * 3
);
const cnt = core.itemCount(item);
if (
(core.items.items[item].cls === "tools" && cnt > 1) ||
FORCE_COUNTABLE_ITEMS.includes(item)
) {
core.fillText(
"outerUI",
cnt,
posx + 25 * 3,
posy + 28 * 3,
"#FFFFFF",
"bold 36px Verdana"
);
}
};
if (core.domStyle.isVertical) {
core.clearMap(
"outerUI",
ITEM_BOX_LEFT_VERTICAL,
ITEM_BOX_TOP_VERTICAL,
185 * 3,
125 * 3
);
drawItemMx((i, j, item) => {
if (core.hasItem(item)) {
const posx = ITEM_BOX_LEFT_VERTICAL + i * 30 * 3,
posy = ITEM_BOX_TOP_VERTICAL + j * 31 * 3;
drawItem(item, posx, posy);
}
});
} else {
core.clearMap(
"outerUI",
ITEM_BOX_LEFT,
ITEM_BOX_TOP,
125 * 3,
185 * 3
);
drawItemMx((i, j, item) => {
if (core.hasItem(item)) {
const posx = ITEM_BOX_LEFT + j * 30 * 3,
posy = ITEM_BOX_TOP + i * 31 * 3;
drawItem(item, posx, posy);
}
});
}
}
_update_map(floorId = core.status.floorId) {
const x = core.domStyle.isVertical ?
MAP_BLOCK_LEFT_VERTICAL :
MAP_BLOCK_LEFT;
const y = core.domStyle.isVertical ?
MAP_BLOCK_TOP_VERTICAL :
MAP_BLOCK_TOP;
if (!floorId) return;
const info = core.plugin.getMapDrawInfo(floorId, Infinity, true);
core.setTextAlign("outerUI", "center");
core.plugin.drawSmallMap(uictx, info, floorId, x, y, 300, 300);
}
_update_equips() {
return;
core.setFont("outerUI", "bold 48px Verdana");
const drawEquip = (baseX, baseY, id, color, back) => {
if (!id)
core.fillText(
"outerUI",
back,
baseX + 20 * 3,
baseY + 22 * 3,
color
);
else {
var icon = core.material.icons.items[id];
core.drawImage(
"outerUI",
core.material.images.items,
0,
32 * icon,
32,
32,
baseX + 5 * 3,
baseY,
32 * 3,
32 * 3
);
}
};
if (core.domStyle.isVertical) {
core.clearMap(
"outerUI",
EQUIP_BLOCK_LEFT_VERTICAL,
EQUIP_BLOCK_TOP_VERTICAL,
90 * 3,
130 * 3
);
drawEquip(
EQUIP_BLOCK_LEFT_VERTICAL,
EQUIP_BLOCK_TOP_VERTICAL,
core.getEquip(0),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3,
EQUIP_BLOCK_TOP_VERTICAL,
core.getEquip(1),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT_VERTICAL,
EQUIP_BLOCK_TOP_VERTICAL + 45 * 3,
core.getEquip(2),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3,
EQUIP_BLOCK_TOP_VERTICAL + 45 * 3,
core.getEquip(3),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT_VERTICAL,
EQUIP_BLOCK_TOP_VERTICAL + 90 * 3,
core.getEquip(4),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT_VERTICAL + 45 * 3,
EQUIP_BLOCK_TOP_VERTICAL + 90 * 3,
core.getEquip(5),
"#D1CEFF",
"无"
);
} else {
core.clearMap(
"outerUI",
EQUIP_BLOCK_LEFT,
EQUIP_BLOCK_TOP,
130 * 3,
95 * 3
);
drawEquip(
EQUIP_BLOCK_LEFT,
EQUIP_BLOCK_TOP,
core.getEquip(0),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT + 42 * 3,
EQUIP_BLOCK_TOP,
core.getEquip(1),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT + 85 * 3,
EQUIP_BLOCK_TOP,
core.getEquip(2),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT,
EQUIP_BLOCK_TOP + 45 * 3,
core.getEquip(3),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT + 42 * 3,
EQUIP_BLOCK_TOP + 45 * 3,
core.getEquip(4),
"#D1CEFF",
"无"
);
drawEquip(
EQUIP_BLOCK_LEFT + 85 * 3,
EQUIP_BLOCK_TOP + 45 * 3,
core.getEquip(5),
"#D1CEFF",
"无"
);
}
}
_update_keys() {
const drawKeyList = (baseX, baseY) => {
const todraw = [],
keyList = ["yellowKey", "blueKey", "redKey", "greenKey"];
let total = 0;
keyList.forEach(function (key, i) {
todraw[i] = core.itemCount(key);
total += todraw[i];
});
let dn = 3;
for (let i = 0; i <= dn; i++) {
let delta = i * 32 * 3;
if (core.domStyle.isVertical) {
this.drawKey(keyList[i], baseX, baseY + delta);
} else {
this.drawKey(keyList[i], baseX + delta, baseY);
}
core.setFont("outerUI", "bold 48px Verdana");
core.setTextAlign("outerUI", "left");
if (core.domStyle.isVertical) {
core.fillText(
"outerUI",
todraw[i],
baseX + 20 * 3,
baseY + 14 * 3 + delta,
TEXT_COLOR
);
} else {
core.fillText(
"outerUI",
todraw[i],
baseX + delta,
baseY + 32 * 3,
TEXT_COLOR
);
}
}
};
if (core.domStyle.isVertical) {
core.clearMap(
"outerUI",
KEY_BLOCK_LEFT_VERTICAL,
KEY_BLOCK_TOP_VERTICAL,
45 * 3,
130 * 3
);
drawKeyList(
KEY_BLOCK_LEFT_VERTICAL + 3 * 3,
KEY_BLOCK_TOP_VERTICAL + 5 * 3
);
} else {
core.clearMap(
"outerUI",
KEY_BLOCK_LEFT,
KEY_BLOCK_TOP,
130 * 3,
45 * 3
);
drawKeyList(KEY_BLOCK_LEFT + 10 * 3, KEY_BLOCK_TOP);
}
}
drawKey(key, x, y) {
let sx = 0,
sy = 0;
if (key == "yellowKey") sx += 13;
else if (key == "blueKey") sx += 26;
else if (key == "greenKey") sx += 39;
core.drawImage(
"outerUI",
"maba.webp",
sx,
sy,
13,
26,
x,
y,
13 * 3,
26 * 3
);
}
_update_infoWindow() {
const itemId = this.selectedItem;
let text = "";
if (this.selectedItem) {
text = core.replaceText(core.material.items[itemId]?.text);
if (text[0] == "," || text[0] == "") text = text.substring(1);
}
if (core.domStyle.isVertical) {
core.clearMap(
"outerUI",
INFO_BLOCK_LEFT_VERTICAL,
INFO_BLOCK_TOP_VERTICAL,
300 * 3,
120 * 3
);
if (this.selectedItem) {
const icon = core.material.icons.items[itemId];
core.setTextAlign("outerUI", "left");
core.fillText(
"outerUI",
core.material.items[itemId].name,
INFO_BLOCK_LEFT_VERTICAL + 50 * 3,
INFO_BLOCK_TOP_VERTICAL + 27 * 3,
"#D1CEFF"
);
core.drawImage(
"outerUI",
core.material.images.items,
0,
32 * icon,
32,
32,
INFO_BLOCK_LEFT_VERTICAL + 10 * 3,
INFO_BLOCK_TOP_VERTICAL + 8 * 3,
32 * 3,
32 * 3
);
core.ui.drawTextContent("outerUI", text, {
left: INFO_BLOCK_LEFT_VERTICAL + 10 * 3,
top: INFO_BLOCK_TOP_VERTICAL + 40 * 3,
maxWidth: 275 * 3,
color: "#D1CEFF",
fontSize: 24,
});
}
} else {
core.clearMap(
"outerUI",
INFO_BLOCK_LEFT,
INFO_BLOCK_TOP,
115 * 3,
230 * 3
);
core.setFont("outerUI", "bold 36px Verdana")
if (this.selectedItem) {
const icon = core.material.icons.items[itemId];
core.setTextAlign("outerUI", "center");
core.fillText(
"outerUI",
core.material.items[itemId].name,
INFO_BLOCK_LEFT + 60 * 3,
INFO_BLOCK_TOP + 25 * 3,
"#D1CEFF"
);
core.drawImage(
"outerUI",
core.material.images.items,
0,
32 * icon,
32,
32,
INFO_BLOCK_LEFT + 45 * 3,
INFO_BLOCK_TOP + 30 * 3,
32 * 3,
32 * 3
);
core.ui.drawTextContent("outerUI", text, {
left: INFO_BLOCK_LEFT + 10 * 3,
top: INFO_BLOCK_TOP + 60 * 3,
maxWidth: 105 * 3,
color: "#D1CEFF",
fontSize: 24,
});
}
}
}
showItemInfo(itemId) {
//展示道具说明
this.selectedItem = itemId;
this._update_infoWindow();
}
clearItemInfo() {
//清除道具说明
this.selectedItem = null;
this._update_infoWindow();
}
_update_toolBox() {
const tools = core.isReplaying() ? [
[core.status.replay.pausing ? "play" : "pause", "stop", "rewind"],
["speedDown", "speedUp", "save"],
] : [
["keyboard", "shop", "pack", "T332"],
["save", "load", "settings", "T331"],
];
if (core.domStyle.isVertical) {
core.clearMap(
"outerUI",
TOOL_BOX_LEFT_VERTICAL,
TOOL_BOX_TOP_VERTICAL,
115,
130
);
for (let i = 0; i < tools.length; i++) {
for (let j = 0; j < tools[i].length; j++) {
core.drawIcon(
"outerUI",
tools[i][j],
TOOL_BOX_LEFT_VERTICAL + i * 31 * 3,
TOOL_BOX_TOP_VERTICAL + j * 31 * 3,
30 * 3,
30 * 3
);
}
}
} else {
core.clearMap(
"outerUI",
TOOL_BOX_LEFT,
TOOL_BOX_TOP,
130 * 3,
80 * 3
);
for (let i = 0; i < tools.length; i++) {
for (let j = 0; j < tools[i].length; j++) {
core.drawIcon(
"outerUI",
tools[i][j],
TOOL_BOX_LEFT + j * 31 * 3,
TOOL_BOX_TOP + i * 31 * 3,
30 * 3,
30 * 3
);
}
}
}
}
onclick(x, y) {
const makeBox = ([x, y], [w, h]) => {
return [
[x, y],
[x + w, y + h],
];
};
const gridify = ([x, y], [gw, gh]) => {
return [Math.floor(x / gw), Math.floor(y / gh)];
};
const useItem = (itemId) => {
if (!core.hasItem(itemId)) return;
if (itemId != this.selectedItem) {
this.showItemInfo(itemId);
} else {
switch (itemId) {
case "centerFly":
core.ui._drawCenterFly();
break;
case "book":
core.openBook(true);
break;
default:
core.useItem(itemId);
}
}
};
const inRect = ([x, y], [
[sx, sy],
[dx, dy]
]) => {
return sx <= x && x <= dx && sy <= y && y <= dy;
};
const relativeTo = ([x, y], [ax, ay]) => {
return [x - ax, y - ay];
};
const pos = [x, y];
if (core.domStyle.isVertical) {
const itemBox = makeBox(
[ITEM_BOX_LEFT_VERTICAL, ITEM_BOX_TOP_VERTICAL],
[30 * 6 * 3, 31 * 4 * 3]
);
if (inRect(pos, itemBox)) {
const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [
30 * 3,
31 * 3,
]);
const itemId = this.itemMx[gx][gy];
if (
(core.status.event.id == "viewMaps" ||
core.status.event.id == "fly") &&
itemId === "book"
)
core.openBook(true);
if (
core.isReplaying() ||
core.status.lockControl ||
core.isMoving()
)
return;
useItem(itemId);
return;
}
const toolBox = makeBox(
[TOOL_BOX_LEFT_VERTICAL, TOOL_BOX_TOP_VERTICAL],
[31 * 2 * 3, 31 * 4 * 3]
);
if (inRect(pos, toolBox)) {
const [col, row] = gridify(relativeTo(pos, toolBox[0]), [
31 * 3,
31 * 3,
]);
if (core.status.lockControl || core.isMoving()) return;
if (core.isReplaying()) {
this.replayAction[col][row].call(core);
} else if (core.isPlaying()) {
if (col === 0 && row === 3) {
core.doSL("autoSave", "load");
} else if (col === 1 && row === 3) {
core.doSL("autoSave", "reload");
} else {
this.toolbarAction[col][row].call(core, true);
}
}
return;
}
const mapBox = makeBox(
[MAP_BLOCK_LEFT_VERTICAL, MAP_BLOCK_TOP_VERTICAL],
[350, 350]
);
if (inRect(pos, mapBox)) {
if (
core.isReplaying() ||
core.status.lockControl ||
core.isMoving()
)
return;
core.useItem("fly");
return;
}
/*const equipBox = makeBox([EQUIP_BLOCK_LEFT_VERTICAL, EQUIP_BLOCK_TOP_VERTICAL], [90 * 3, 130 * 3])
if (inRect(pos, equipBox)) {
if (core.isReplaying() || core.status.lockControl || core.isMoving()) return;
core.openEquipbox(true)
return;
}*/
} else {
const mapBox = makeBox([MAP_BLOCK_LEFT, MAP_BLOCK_TOP], [350, 350]);
if (inRect(pos, mapBox)) {
if (
core.isReplaying() ||
core.status.lockControl ||
core.isMoving()
)
return;
core.useItem("fly");
return;
}
/*
const equipBox = makeBox([EQUIP_BLOCK_LEFT, EQUIP_BLOCK_TOP], [130, 95])
if (inRect(pos, equipBox)) {
if (core.isReplaying() || core.status.lockControl || core.isMoving()) return;
core.openEquipbox(true)
return;
}*/
const itemBox = makeBox(
[ITEM_BOX_LEFT, ITEM_BOX_TOP],
[31 * 4 * 3, 30 * 6 * 3]
);
if (inRect(pos, itemBox)) {
const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [
31 * 3,
30 * 3,
]);
const itemId = this.itemMx[gy][gx];
if (
(core.status.event.id == "viewMaps" ||
core.status.event.id == "fly") &&
itemId === "book"
)
core.openBook(true);
if (
core.isReplaying() ||
core.status.lockControl ||
core.isMoving()
)
return;
useItem(itemId);
return;
}
const toolBox = makeBox(
[TOOL_BOX_LEFT, TOOL_BOX_TOP],
[31 * 4 * 3, 31 * 2 * 3]
);
if (inRect(pos, toolBox)) {
const [row, col] = gridify(relativeTo(pos, toolBox[0]), [
31 * 3,
31 * 3,
]);
if (core.status.lockControl || core.isMoving()) return;
if (core.isReplaying()) {
this.replayAction[col][row].call(core);
} else if (core.isPlaying()) {
if (col === 0 && row === 3) {
core.doSL("autoSave", "load");
} else if (col === 1 && row === 3) {
core.doSL("autoSave", "reload");
} else {
this.toolbarAction[col][row].call(core, true);
}
}
return;
}
}
}
}
core.ui.statusBar = new StatusBar();
core.control.clearStatusBar = function () {
core.clearMap("outerUI");
};
// init() called in `afterLoadResources`.
},
"override": function () {
core.statusBar.icons = {
floor: 0,
name: null,
lv: 1,
hpmax: 2,
hp: 3,
atk: 4,
def: 5,
mdef: 6,
money: 7,
exp: 8,
up: 9,
book: 10,
fly: 11,
toolbox: 12,
keyboard: 13,
shop: 14,
save: 15,
load: 16,
settings: 17,
play: 18,
pause: 19,
stop: 20,
speedDown: 21,
speedUp: 22,
rewind: 23,
equipbox: 24,
mana: 25,
skill: 26,
exit: 27,
btn1: 28,
btn2: 29,
btn3: 30,
btn4: 31,
btn5: 32,
btn6: 33,
btn7: 34,
alt: 35,
keys: 36,
help: 37,
battle: 38,
};
core.actions._getClickLoc = function (x, y) {
var size = 32 * core.domStyle.scale;
var left = main.dom.gameDraw.offsetLeft + main.dom.gameGroup.offsetLeft;
var top = main.dom.gameDraw.offsetTop + main.dom.gameGroup.offsetTop;
var loc = {
x: Math.max(x - left, 0),
y: Math.max(y - top, 0),
size: size,
};
return loc;
};
/* core.ui._drawWindowSelector = function (background, x, y, w, h) {
w = Math.round(w) + 48;
h = Math.round(h);
var ctx = core.ui.createCanvas("_selector", x - 24, y, w, h, 165);
ctx.canvas.id = "";
this._drawSelector(ctx, background, w, h);
};
core.ui._drawSelector = function (ctx, background, w, h, left, top) {
left = left || 0;
top = top || 0;
ctx = this.getContextByName(ctx);
if (!ctx) return;
if (typeof background == "string")
background = core.material.images.images[background];
if (!(background instanceof Image)) return;
// badge
ctx.drawImage(background, 132, 68, 24, 24, left + 4, top + 4, 24, 24);
ctx.drawImage(
background,
132,
68,
24,
24,
w - left - 28,
top + 4,
24,
24
);
};*/
enemys.prototype._nextCriticals_useBinarySearch = function (
enemy,
info,
number,
x,
y,
floorId
) {
var mon_hp = info.mon_hp,
hero_atk = core.status.hero.atk,
mon_def = info.mon_def,
pre = info.damage;
var list = [];
var start_atk = hero_atk;
if (info.__over__) {
start_atk += info.__overAtk__;
list.push([info.__overAtk__, -info.damage]);
}
var calNext = function (currAtk, maxAtk) {
var start = Math.floor(currAtk),
end = Math.floor(maxAtk);
if (start > end) return null;
while (start < end) {
var mid = Math.floor((start + end) / 2);
if (mid - start > end - mid) mid--;
var nextInfo = core.enemys.getDamageInfo(
enemy, { atk: mid },
x,
y,
floorId
);
if (nextInfo == null || typeof nextInfo == "number") return null;
if (pre > nextInfo.damage) end = mid;
else start = mid + 1;
}
var nextInfo = core.enemys.getDamageInfo(
enemy, { atk: start },
x,
y,
floorId
);
return nextInfo == null ||
typeof nextInfo == "number" ||
nextInfo.damage >= pre ?
null : [start, nextInfo.damage];
};
var currAtk = start_atk;
while (true) {
var next = calNext(currAtk + 1, Number.MAX_SAFE_INTEGER, pre);
if (next == null) break;
currAtk = next[0];
pre = next[1];
list.push([currAtk - hero_atk, info.damage - pre]);
if (pre <= 0 && !core.flags.enableNegativeDamage) break;
if (list.length >= number) break;
}
if (list.length == 0) list.push([0, 0]);
return list;
};
core.ui.clearMap = function (name, x, y, width, height) {
if (name == "all") {
for (var m in core.canvas) {
core.canvas[m].clearRect(
-32,
-32,
core.canvas[m].canvas.width + 32,
core.canvas[m].canvas.height + 32
);
}
core.clearMap("outerUI");
core.dom.gif.innerHTML = "";
core.removeGlobalAnimate();
core.deleteCanvas(function (one) {
return one.startsWith("_bigImage_");
});
core.setWeather(null);
} else {
var ctx = this.getContextByName(name);
if (ctx)
ctx.clearRect(
x || 0,
y || 0,
width || ctx.canvas.width,
height || ctx.canvas.height
);
}
};
events.prototype.openBook = function (fromUserAction) {
if (core.isReplaying()) return;
// 如果能恢复事件从callBook事件触发
if (
core.status.event.id == "book" &&
core.events.recoverEvents(core.status.event.interval)
)
return;
// 当前是book且从“浏览地图”打开
if (core.status.event.id == "book" && core.status.event.ui) {
core.status.boxAnimateObjs = [];
core.ui._drawViewMaps(core.status.event.ui);
return;
}
// 从“浏览地图”页面打开
if (core.status.event.id == "viewMaps" || core.status.event.id == "fly") {
fromUserAction = false;
core.status.event.ui = core.status.event.data;
}
if (!this._checkStatus("book", fromUserAction, true)) return;
core.playSound("打开界面");
core.useItem("book", true);
};
////// 怪物手册界面时,放开某个键的操作 //////
core.actions._keyUpBook = function (keycode) {
if (keycode == 27 || keycode == 88) {
core.playSound("取消");
if (core.events.recoverEvents(core.status.event.interval)) {
return;
} else if (core.status.event.ui != null) {
core.status.boxAnimateObjs = [];
if (typeof core.status.event.ui === "number") {
core.status.event.id = "fly";
core.ui.drawFly(core.status.event.ui);
} else {
core.ui._drawViewMaps(core.status.event.ui);
}
} else core.ui.closePanel();
return;
}
if (keycode == 13 || keycode == 32 || keycode == 67) {
var data = core.status.event.data;
if (data != null) {
core.ui._drawBookDetail(data);
}
return;
}
};
////// 怪物手册界面的点击操作 //////
actions.prototype._clickBook = function (x, y) {
var pageinfo = core.ui._drawBook_pageinfo();
// 上一页
if (
(x == this._HX_ - 2 || x == this._HX_ - 3) &&
y === core._HEIGHT_ - 1
) {
core.playSound("光标移动");
core.ui.drawBook(core.status.event.data - pageinfo.per_page);
return;
}
// 下一页
if (
(x == this._HX_ + 2 || x == this._HX_ + 3) &&
y === core._HEIGHT_ - 1
) {
core.playSound("光标移动");
core.ui.drawBook(core.status.event.data + pageinfo.per_page);
return;
}
// 返回
if (x >= this.LAST - 2 && y === core._HEIGHT_ - 1) {
core.playSound("取消");
if (core.events.recoverEvents(core.status.event.interval)) {
return;
} else if (core.status.event.ui != null) {
core.status.boxAnimateObjs = [];
if (typeof core.status.event.ui === "number") {
core.status.event.id = "fly";
core.ui.drawFly(core.status.event.ui);
} else {
core.ui._drawViewMaps(core.status.event.ui);
}
} else core.ui.closePanel();
return;
}
// 怪物信息
var data = core.status.event.data;
if (data != null && y < core._HEIGHT_ - 1) {
var per_page = pageinfo.per_page,
page = parseInt(data / per_page);
var u = (core._HEIGHT_ - 1) / per_page;
for (var i = 0; i < per_page; ++i) {
if (y >= u * i && y < u * (i + 1)) {
var index = per_page * page + i;
core.ui.drawBook(index);
core.ui._drawBookDetail(index);
break;
}
}
return;
}
return;
};
////// 执行当前自定义事件列表中的下一个事件 //////
events.prototype.doAction = function () {
// 清空boxAnimate和UI层
clearInterval(core.status.event.interval);
clearTimeout(core.status.event.interval);
clearInterval(core.status.event.animateUI);
core.status.event.interval = null;
delete core.status.event.aniamteUI;
if (core.status.gameOver || core.status.replay.failed) return;
// 判定是否执行完毕
if (this._doAction_finishEvents()) return;
core.clearUI();
var floorId = core.status.event.data.floorId || core.status.floorId;
// 当前点坐标和前缀
var x = core.status.event.data.x,
y = core.status.event.data.y;
var prefix = [
floorId || ":f",
x != null ? x : "x",
y != null ? y : "y",
].join("@");
var current = core.status.event.data.list[0];
if (this._popEvents(current, prefix)) return;
// 当前要执行的事件
var data = current.todo.shift();
core.status.event.data.current = data;
if (typeof data == "string") data = { type: "text", text: data };
// 该事件块已经被禁用
if (data._disabled) return core.doAction();
if (data.type !== "cgtext") {
core.unregisterAnimationFrame("skip");
core.setFlag("skip", false);
}
data.floorId = data.floorId || floorId;
core.status.event.data.type = data.type;
this.doEvent(data, x, y, prefix);
return;
};
////// 在某个canvas上绘制粗体 //////
core.fillBoldText1 = function (
name,
text,
x,
y,
style,
strokeStyle,
lineWidth,
font,
maxWidth
) {
var ctx = this.getContextByName(name);
if (!ctx) return;
if (font) ctx.font = font;
if (!style) style = ctx.fillStyle;
style = core.arrayToRGBA(style);
if (!strokeStyle) strokeStyle = "#000000";
strokeStyle = core.arrayToRGBA(strokeStyle);
if (maxWidth != null) {
this.setFontForMaxWidth(ctx, text, maxWidth);
}
ctx.strokeStyle = strokeStyle;
if (!lineWidth) lineWidth = 2;
ctx.lineWidth = lineWidth;
ctx.strokeText(text, x, y);
ctx.fillStyle = style;
ctx.fillText(text, x, y);
};
////// 绘制 WindowSkin
ui.prototype.drawWindowSkin = function (
background,
ctx,
x,
y,
w,
h,
direction,
px,
py,
size = 1
) {
background = background || core.status.textAttribute.background;
// 仿RM窗口皮肤 ↓
// 绘制背景
core.drawImage(
ctx,
background,
0,
0,
128,
128,
x + 2 * size,
y + 2 * size,
w - 4 * size,
h - 4 * size
);
// 绘制边框
// 上方
core.drawImage(
ctx,
background,
128,
0,
16,
16,
x,
y,
16 * size,
16 * size
);
for (var dx = 0; dx < w - 64 * size; dx += 32 * size) {
core.drawImage(
ctx,
background,
144,
0,
32,
16,
x + dx + 16 * size,
y,
32 * size,
16 * size
);
core.drawImage(
ctx,
background,
144,
48,
32,
16,
x + dx + 16 * size,
y + h - 16 * size,
32 * size,
16 * size
);
}
core.drawImage(
ctx,
background,
144,
0,
(w - dx - 32 * size) / size,
16,
x + dx + 16 * size,
y,
w - dx - 32 * size,
16 * size
);
core.drawImage(
ctx,
background,
144,
48,
(w - dx - 32 * size) / size,
16,
x + dx + 16 * size,
y + h - 16 * size,
w - dx - 32 * size,
16 * size
);
core.drawImage(
ctx,
background,
176,
0,
16,
16,
x + w - 16 * size,
y,
16 * size,
16 * size
);
// 左右
for (var dy = 0; dy < h - 64 * size; dy += 32 * size) {
core.drawImage(
ctx,
background,
128,
16,
16,
32,
x,
y + dy + 16 * size,
16 * size,
32 * size
);
core.drawImage(
ctx,
background,
176,
16,
16,
32,
x + w - 16 * size,
y + dy + 16 * size,
16 * size,
32 * size
);
}
core.drawImage(
ctx,
background,
128,
16,
16,
(h - dy - 32 * size) / size,
x,
y + dy + 16 * size,
16 * size,
h - dy - 32 * size
);
core.drawImage(
ctx,
background,
176,
16,
16,
(h - dy - 32 * size) / size,
x + w - 16 * size,
y + dy + 16 * size,
16 * size,
h - dy - 32 * size
);
//下方
core.drawImage(
ctx,
background,
128,
48,
16,
16,
x,
y + h - 16 * size,
16 * size,
16 * size
);
core.drawImage(
ctx,
background,
176,
48,
16,
16,
x + w - 16 * size,
y + h - 16 * size,
16 * size,
16 * size
);
// arrow
if (px != null && py != null) {
if (direction == "up") {
core.drawImage(
ctx,
background,
128,
96,
32,
32,
px,
y + h - 3 * size,
32 * size,
32 * size
);
} else if (direction == "down") {
core.drawImage(
ctx,
background,
160,
96,
32,
32,
px,
y - 29 * size,
32 * size,
32 * size
);
}
}
// 仿RM窗口皮肤 ↑
};
events.prototype.battle = function (id, x, y, force, callback) {
core.saveAndStopAutomaticRoute();
id = id || core.getBlockId(x, y);
const cls = core.getClsFromId(id);
if (!id || !cls || !(cls === "enemys" || cls === "enemy48"))
return core.clearContinueAutomaticRoute(callback);
// 非强制战斗
if (core.enemys.getDamage(id, x, y) === null && !force && !core.status.event.id) {
core.stopSound();
core.playSound("操作失败");
core.drawTip("你打不过此怪物!", id);
return core.clearContinueAutomaticRoute(callback);
}
// 自动存档
if (!core.status.event.id) core.autosave(true);
// 战前事件
if (!this.beforeBattle(id, x, y))
return core.clearContinueAutomaticRoute(callback);
// 战后事件
this.afterBattle(id, x, y);
if (callback) callback();
};
actions.prototype._sys_ondown_lockControl = function (x, y, px, py) {
if (core.status.played && !core.status.lockControl) return false;
switch (core.status.event.id) {
case "battle":
core.plugin.battle_onclick(x, y, px, py);
break;
case "centerFly":
this._clickCenterFly(x, y, px, py);
break;
case "book":
this._clickBook(x, y, px, py);
break;
case "book-detail":
this._clickBookDetail(x, y, px, py);
break;
case "fly":
this._clickFly(x, y, px, py);
break;
case "viewMaps":
this._clickViewMaps(x, y, px, py);
break;
case "switchs":
this._clickSwitchs(x, y, px, py);
break;
case "switchs-sounds":
this._clickSwitchs_sounds(x, y, px, py);
break;
case "switchs-display":
this._clickSwitchs_display(x, y, px, py);
break;
case "switchs-action":
this._clickSwitchs_action(x, y, px, py);
break;
case "settings":
this._clickSettings(x, y, px, py);
break;
case "selectShop":
this._clickQuickShop(x, y, px, py);
break;
case "equipbox":
this._clickEquipbox(x, y, px, py);
break;
case "toolbox":
this._clickToolbox(x, y, px, py);
break;
case "save":
case "load":
case "replayLoad":
case "replayRemain":
case "replaySince":
this._clickSL(x, y, px, py);
break;
case "confirmBox":
this._clickConfirmBox(x, y, px, py);
break;
case "keyBoard":
this._clickKeyBoard(x, y, px, py);
break;
case "action":
this._clickAction(x, y, px, py);
break;
case "text":
core.drawText();
break;
case "notes":
this._clickNotes(x, y, px, py);
break;
case "syncSave":
this._clickSyncSave(x, y, px, py);
break;
case "syncSelect":
this._clickSyncSelect(x, y, px, py);
break;
case "localSaveSelect":
this._clickLocalSaveSelect(x, y, px, py);
break;
case "storageRemove":
this._clickStorageRemove(x, y, px, py);
break;
case "cursor":
this._clickCursor(x, y, px, py);
break;
case "replay":
this._clickReplay(x, y, px, py);
break;
case "gameInfo":
this._clickGameInfo(x, y, px, py);
break;
case "about":
case "help":
core.ui.closePanel();
break;
}
// --- 长按判定
if (core.timeout.onDownTimeout == null) {
core.timeout.onDownTimeout = setTimeout(function () {
if (core.interval.onDownInterval == null) {
core.interval.onDownInterval = setInterval(function () {
if (!core.actions.longClick(x, y, px, py)) {
clearInterval(core.interval.onDownInterval);
core.interval.onDownInterval = null;
}
}, 40);
}
}, 500);
}
return true;
};
core.registerAction(
"ondown",
"_sys_ondown_lockControl",
core.actions._sys_ondown_lockControl,
30
);
////// 绘制提示同时播放错误音效 //////
ui.prototype.drawFailTip = function (text, id, frame) {
this.drawTip(text, id, frame);
core.playSound('error.opus');
}
},
"额外信息": function () {
/* 宝石血瓶左下角显示数值
* 注意不要在道具属性中直接操作flags使用core.status.hero.flags或core.setFlag系列函数代替
* 需要将 变量itemDetail改为true才可正常运行
* 请尽量减少勇士的属性数量,否则可能会出现严重卡顿(划掉,现在你放一万个属性也不会卡)
* 注意这里的属性必须是core.status.hero里面的flag无法显示
* 如果不想显示可以core.setFlag("itemDetail", false);
* 然后再core.getItemDetail();
* 如有bug在大群或造塔群@古祠
*/
// 忽略的道具
const ignore = ["superPotion"];
// 取消注释下面这句可以减少超大地图的判定。
// 如果地图宝石过多,可能会略有卡顿,可以尝试取消注释下面这句话来解决。
// core.bigmap.threshold = 256;
const origin = core.control.updateStatusBar;
core.updateStatusBar = core.control.updateStatusBar = function () {
if (core.getFlag("__statistics__")) return;
else return origin.apply(core.control, arguments);
};
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);
core.getItemDetail(floorId); // 宝石血瓶详细信息
this.drawDamage(ctx);
};
// 获取宝石信息 并绘制
this.getItemDetail = function (floorId) {
if (!core.getFlag("itemDetail")) return;
if (!core.status.thisMap) return;
floorId = floorId ?? core.status.thisMap.floorId;
const beforeRatio = core.status.thisMap.ratio;
core.status.thisMap.ratio = core.status.maps[floorId].ratio;
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" ||
ignore.includes(block.event.id) ||
block.disable
)
return;
const x = block.x,
y = block.y;
// v2优化只绘制范围内的部分
if (core.bigmap.v2) {
if (
x < core.bigmap.posX - core.bigmap.extend ||
x > core.bigmap.posX + core._WIDTH_ + core.bigmap.extend ||
y < core.bigmap.posY - core.bigmap.extend ||
y > core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend
) {
return;
}
}
diff = {};
const id = block.event.id;
const item = core.material.items[id];
if (item.cls === "equips") {
// 装备也显示
const diff = 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.thisMap.ratio = beforeRatio;
core.status.hero = before;
window.hero = before;
window.flags = before.flags;
};
// 绘制
function drawItemDetail(diff, x, y) {
const px = 32 * x + 2,
py = 32 * y + 30;
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++;
}
}
},
"跳伤": function () {
// 在此增加新插件
/**
函数使用说明
Dove.MorePerform.ShowDamagePop.PopDamage(
'ui', // 画布名称或画布2d上下文对象
100, 200, // 位置 (x, y)
-50, // 伤害值
24, // 字体大小
"宋体",//字体
'#FF0000', // 字体颜色
'#000000', // 描边颜色
0.5, // 水平速度 (speedX)
-10, // 垂直速度 (speedY)
0.5, // 重力 (gravity)
120 // 显示时长(帧数)
*/
if (!core.registerAnimationFrame) {
throw new Error("require 2.6.1 or higher version");
}
window.Dove = window.Dove || {};
Dove.MorePerform = Dove.MorePerform || {};
Dove.MorePerform.ShowDamagePop = {};
Dove.MorePerform.ShowDamagePop.version = 1.0;
Dove.MorePerform.ShowDamagePop.AllPopingCanvas = [];
// 每帧的处理
Dove.MorePerform.ShowDamagePop.Update = function () {
this.AllPopingCanvas = this.AllPopingCanvas.filter(function (spr) {
spr.update();
return spr.isAlive();
});
if (!this.AllPopingCanvas.length) PopSprite._count = 0;
};
// 弹出伤害气泡
Dove.MorePerform.ShowDamagePop.PopDamage = function (
canvasName,
x,
y,
damageValue,
fontSize,
font,
fontColor,
outlineColor,
speedX,
speedY,
gravity,
duration
) {
if (damageValue) {
var poper = new PopSprite(
canvasName,
x,
y,
damageValue,
fontSize,
font,
fontColor,
outlineColor,
speedX,
speedY,
gravity,
duration
);
Dove.MorePerform.ShowDamagePop.AllPopingCanvas.push(poper);
}
};
// 战斗发生前后记录生命值并处理
Dove.MorePerform.ShowDamagePop.OnBattle = core.events.battle;
core.events.battle = function () {
var hpBeforeBattle = core.status.hero.hp;
Dove.MorePerform.ShowDamagePop.OnBattle.apply(core.events, arguments);
if (core.getFlag("noAnimate"))
Dove.MorePerform.ShowDamagePop.PopDamage(
"ui", // 默认画布名称
core.getHeroLoc("x") * 32, // 英雄位置 x
core.getHeroLoc("y") * 32, // 英雄位置 y
Math.floor(core.status.hero.hp - hpBeforeBattle), // 伤害值
16, // 默认字体大小
"Arial", //默认字体
null, // 默认颜色
null, // 默认描边颜色
null, // 默认水平速度
null, // 默认垂直速度
null, // 默认重力
90 // 默认显示时长(帧数)
);
};
let time = 0;
// 注册每帧事件
core.registerAnimationFrame("ShowDamagePop", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
Dove.MorePerform.ShowDamagePop.Update.bind(
Dove.MorePerform.ShowDamagePop
)();
}
});
// 弹出精灵类
function PopSprite(
canvasName,
x,
y,
damage,
fontSize,
font,
fontColor,
outlineColor,
speedX,
speedY,
gravity,
duration
) {
this.initialize.apply(this, arguments);
}
PopSprite.prototype = Object.create(Object.prototype);
PopSprite.prototype.constructor = PopSprite;
// 常量
PopSprite._count = 0;
PopSprite._baseZOrder = 50;
PopSprite._floorDis = 20;
// 初始化
PopSprite.prototype.initialize = function (
canvasName,
x,
y,
damage,
fontSize,
font,
fontColor,
outlineColor,
speedX,
speedY,
gravity,
duration
) {
this._canvasName = canvasName ?? "ui"; // 默认画布名称
this._x = x;
this._y = y;
this._damage = damage;
this._fontSize = fontSize ?? 16; // 默认字体大小
this._font = font ?? "Arial";
this._fontColor = fontColor ?? (damage > 0 ? "#22FF44" : "lightcoral"); // 默认颜色
this._outlineColor = outlineColor ?? "#FFFFFF"; // 默认描边颜色
this._speedX = speedX ?? -1 + Math.random() * 2; // 水平速度,默认随机
this._speedY = speedY ?? -3 - Math.random() * 4; // 垂直速度,默认随机
this._gravity = gravity ?? 0.3; // 重力加速度,默认 0.3
this._duration = duration ?? 180; // 显示时长(帧数),默认 180 帧
this.initAllMembers();
this.requestCanvas();
};
// 自更新
PopSprite.prototype.update = function () {
if (this._timer < this._duration) {
// 使用传入的显示时长
this._x += this._vx;
this._y += this._vy;
this._vy += this._gravity;
if (this._y >= this._floorY) {
this._y = this._floorY;
this._vy *= -0.75; // 反弹衰减
}
core.relocateCanvas(this._symbol, this._x, this._y);
core.setOpacity(this._symbol, 1 - this._timer / this._duration); // 根据显示时长设置透明度
} else {
this.dispose();
}
this._timer++;
};
// 申请并描绘canvas
PopSprite.prototype.requestCanvas = function () {
core.createCanvas(
this._symbol,
this._x,
this._y,
this._width + 4,
this._height + 4,
this._z
);
var canvas = core.getContextByName(this._symbol);
canvas.font = this._fontSize + "px " + this._font; // 动态设置字体大小
canvas.fillStyle = this._fontColor; // 动态设置字体颜色
canvas.strokeStyle = this._outlineColor; // 动态设置描边颜色
canvas.strokeText(this._text, 2, this._height);
canvas.fillText(this._text, 2, this._height);
};
// 初始化所有成员变量
PopSprite.prototype.initAllMembers = function () {
this._text = String(this._damage);
var uiContext = core.ui.getContextByName(this._canvasName); // 使用指定画布
uiContext.font = this._fontSize + "px " + this._font; // 动态设置字体大小
var textRect = uiContext.measureText(this._text);
this._width = textRect.width + 4;
this._height = this._fontSize + 4; // 动态设置高度
this._z = uiContext.canvas.style.zIndex
? Number(uiContext.canvas.style.zIndex) + PopSprite._count
: PopSprite._baseZOrder + PopSprite._count;
this._symbol = "popSprite" + PopSprite._count++;
this._alive = true;
this._vx = this._speedX; // 使用传入的水平速度
this._vy = this._speedY; // 使用传入的垂直速度
this._floorY = this._y + PopSprite._floorDis;
this._timer = 0;
};
// 判断是否存活
PopSprite.prototype.isAlive = function () {
return this._alive;
};
// 释放
PopSprite.prototype.dispose = function () {
this._alive = false;
core.deleteCanvas(this._symbol);
};
},
"编辑器显伤": function () {
// 在此增加新插件
/////// 用户设置 ///////
// 将__enable置为false将关闭插件
var __enable = true;
// 魔防攻速之类的属性可以在这里加 ['atk', 'def', 'mdef']
var heroStatus = [
"atk",
"def",
"spell",
"mdef",
"matk",
"mhp",
"speed",
"hp",
];
// saveHero为true 将会把每次造塔测试时的角色数据存下来 否则会读取初始属性
// 用不着可以关了 节约缓存空间 (虽然根本没多少 还没一个存档大
// 也可以手动清理 控制台输入core.removeLocalStorage('editorHero')即可
var saveHero = true;
// 下为具体实现 懒得写注释了 大概就是写HTML然后注册交互
if (!__enable || main.mode != "editor") return;
core.plugin.initEditorDamage = false;
if (heroStatus.length >= 4 && !editor.isMobile) {
editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + "px";
editor.dom.mid.style.height = 730 + "px";
document.querySelector("#mid .tools").style.height = 280 + "px";
}
editor.statusRatio = core.getLocalStorage("statusRatio", 1);
editor.saveHero = saveHero;
editor._heroStatus = heroStatus;
editor.dom.mapEdit.appendChild(core.canvas.damage.canvas);
const viewportButtons = document.getElementById("viewportButtons");
var HTML =
"<input type='button' value='←'/><input type='button' value='↑'/><input type='button' value='↓'/><input type='button' value='→'/><input type='button' id='bigmapBtn' value='大地图'' style='margin-left: '5px'/>";
//if (heroStatus.length >= 4 && !editor.isMobile) editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + 'px';
heroStatus.forEach(function (status) {
var id = status + "set",
id2 = status + "add",
id3 = status + "rec",
id4 = status + "help";
HTML +=
"<br/>" +
core.getStatusLabel(status) +
"<text ><input type='text' size='" +
(15 - core.getStatusLabel(status).length * 2.5) +
"' id='" +
id +
"'><input type='button' id='" +
id2 +
"' value = '+'><input type='button' id='" +
id3 +
"' value = '-'><input type='button' value='?' id = '" +
id4 +
"'>";
});
document.getElementById("viewportButtons").innerHTML = HTML;
["set", "add", "rec", "help"].forEach(function (e) {
heroStatus.forEach(function (status) {
editor.dom[status + e] = document.getElementById(status + e);
});
});
var _hasItem = core.items.hasItem;
core.items.hasItem = function (itemId) {
if (itemId == "book" && main.mode == "editor") return true;
return _hasItem.call(core.items, itemId);
};
if (main.mode == "editor") {
var applyList = [
"getDamageString",
"nextCriticals",
"getEnemyInfo",
"getEnemyValue",
];
applyList.forEach(function (name) {
var func = core.enemys[name];
core.enemys[name] = function () {
var args =
arguments.length === 1 ?
[arguments[0]] :
Array.apply(null, arguments);
if (typeof args[0] == "string") args[0] = core.enemys.enemys[args[0]];
return func.apply(core.enemys, args);
};
});
}
////// 获得勇士属性 //////
core.control.getStatus = function (name) {
if (!core.status.hero) return null;
if (name == "x" || name == "y" || name == "direction")
return this.getHeroLoc(name);
/*if ( main.mode == 'editor' && !core.hasFlag('__statistics__')) {
return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero[name];
}*/
return core.status.hero[name];
};
core.control.updateDamage = function (floorId, ctx) {
floorId = floorId || core.status.floorId;
if (!floorId || core.status.gameOver) return;
var onMap = ctx == null;
if (main.mode == "editor") {
ctx = core.canvas.damage;
core.updateCheckBlock();
core.clearMap(ctx);
if (editor.uivalues.bigmap) return;
}
// 没有怪物手册
if (!core.hasItem("book")) return;
core.status.damage.posX = core.bigmap.posX;
core.status.damage.posY = core.bigmap.posY;
if (!onMap) {
var 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);
this.drawDamage(ctx);
};
core.control.drawDamage = function (ctx) {
if (
core.status.gameOver ||
!core.status.damage /* || main.mode != 'play'*/
)
return;
var onMap = false;
if (ctx == null) {
ctx = core.canvas.damage;
core.clearMap("damage");
onMap = true;
}
if (onMap && core.bigmap.v2) {
// 检查是否需要重算...
if (
Math.abs(core.bigmap.posX - core.status.damage.posX) >=
core.bigmap.extend - 1 ||
Math.abs(core.bigmap.posY - core.status.damage.posY) >=
core.bigmap.extend - 1
) {
return this.updateDamage();
}
}
return this._drawDamage_draw(ctx, onMap);
};
////// 以x,y的形式返回每个点的事件 //////
core.maps.getMapBlocksObj = function (floorId, noCache) {
floorId = floorId || core.status.floorId;
if (
core.status.mapBlockObjs[floorId] &&
!noCache &&
main.mode != "editor"
)
return core.status.mapBlockObjs[floorId];
var obj = {};
core.extractBlocks(floorId);
core.status.maps[floorId].blocks.forEach(function (block) {
obj[block.x + "," + block.y] = block;
});
core.status.mapBlockObjs[floorId] = obj;
return obj;
};
this.bignum = function (num, defaultValue) {
if (num == null || num == "") return defaultValue;
num = num + "";
var list = {
w: 1e4,
e: 1e8,
z: 1e12,
j: 1e16,
g: 1e20,
};
// 浮点数问题
function checkFloat(num) {
if (!core.isset(num)) return 0;
num = num + "";
var index = num.indexOf(".");
if (index < 0) return 0;
else return num.slice(index + 1).length;
}
var index = num.search(/w|e|z|j|g/);
if (index <= 0) {
num = parseInt(num);
if (core.isset(num)) return num;
else {
alert("不正确的输入");
return defaultValue;
}
}
for (; index > 0; index = num.search(/w|e|z|j|g/)) {
var p = num[index],
q = list[p],
n = num.slice(0, index),
m = Math.pow(10, checkFloat(n));
num = (n * m * q) / m + num.slice(index + 1);
}
return parseInt(num);
};
this.updateEditorDamage = function (noSave) {
core.updateDamage();
heroStatus.forEach(function (status) {
editor.dom[status + "set"].value = core.status.hero[status];
});
if (!noSave && editor.saveHero)
core.setLocalStorage("editorHero", core.status.hero);
};
var _resizeMap = core.maps.resizeMap;
core.maps.resizeMap = function (floorId) {
_resizeMap.call(core.maps, floorId);
if (!core.plugin.initEditorDamage && main.mode == "editor") {
core.plugin.initEditorDamage = true;
var editorHero = core.getLocalStorage("editorHero");
if (editorHero && saveHero) core.status.hero = editorHero;
else core.removeLocalStorage("editorHero");
editor._heroStatus.forEach(function (e) {
editor.dom[e + "set"].onchange = function () {
var status = this.id.slice(0, -3);
core.status.hero[status] = core.bignum(
this.value,
core.status.hero[status]
);
if (status === "mdef" && core.status.hero[status] > 100)
core.status.hero[status] = 100;
core.updateEditorDamage();
};
editor.dom[e + "add"].onclick = function () {
var status = this.id.slice(0, -3);
core.status.hero[status] += editor.statusRatio;
if (status === "mdef" && core.status.hero[status] > 100)
core.status.hero[status] = 100;
core.updateEditorDamage();
};
editor.dom[e + "rec"].onclick = function () {
var status = this.id.slice(0, -3);
core.status.hero[status] -= editor.statusRatio;
core.updateEditorDamage();
};
editor.dom[e + "help"].onclick = function () {
var status = this.id.slice(0, -4),
name = core.getStatusLabel(status);
var ratio = parseInt(
prompt(
"当前属性:" +
name +
"\n现在的点击按钮变化值:" +
editor.statusRatio +
",请输入按下一次+/-按钮的属性变化量,可以写4w 10.2e这种字母缩写"
)
);
if (!core.isset(ratio)) {
printe("不合法的输入");
return;
}
editor.statusRatio = ratio;
core.setLocalStorage("statusRatio", ratio);
};
});
var _updateMap = editor.updateMap;
editor.updateMap = function () {
_updateMap.call(editor);
core.updateEditorDamage(true);
};
editor.mode.onmode = function (mode, callback) {
if (editor_mode.mode != mode) {
if (mode === "save") {
editor_mode.doActionList(
editor_mode.mode,
editor_mode.actionList,
function () {
if (callback) callback();
core.updateEditorDamage();
}
);
}
if (editor_mode.mode === "nextChange" && mode)
editor_mode.showMode(mode);
if (mode !== "save") editor_mode.mode = mode;
editor_mode.actionList = [];
}
};
}
};
},
"手册区分特殊属性": function () {
// 在此增加新插件
this.arrsame = function (Arraya, Arrayb) {
let a = Arraya || [];
let b = Arrayb || [];
if (typeof a === "number") a = [a];
if (typeof b === "number") b = [b];
let c = [...a, ...b];
for (const i of c)
if (!a.includes(i) || !b.includes(i)) {
return false;
}
return true;
};
enemys.prototype.getCurrentEnemys = function (floorId) {
floorId = floorId || core.status.floorId;
var enemys = [],
used = {};
core.extractBlocks(floorId);
core.status.maps[floorId].blocks.forEach(function (block) {
if (!block.disable && block.event.cls.indexOf("enemy") == 0) {
this._getCurrentEnemys_addEnemy(
block.event.id,
enemys,
used,
block.x,
block.y,
floorId
);
}
}, this);
return this._getCurrentEnemys_sort(enemys);
};
enemys.prototype._getCurrentEnemys_getEnemy = function (enemyId) {
var enemy = core.material.enemys[enemyId];
if (!enemy) return null;
// 检查朝向displayIdInBook
return (
core.material.enemys[enemy.displayIdInBook] ||
core.material.enemys[(enemy.faceIds || {}).down] ||
enemy
);
};
enemys.prototype._getCurrentEnemys_addEnemy = function (
enemyId,
enemys,
used,
x,
y,
floorId
) {
var enemy = this._getCurrentEnemys_getEnemy(enemyId);
if (enemy == null) return;
var id = enemy.id;
var enemyInfo = this.getEnemyInfo(enemy, null, null, null, floorId);
var locEnemyInfo = this.getEnemyInfo(enemy, null, x, y, floorId);
if (
!core.flags.enableEnemyPoint ||
(locEnemyInfo.atk == enemyInfo.atk &&
locEnemyInfo.def == enemyInfo.def &&
locEnemyInfo.hp == enemyInfo.hp &&
core.plugin.arrsame(locEnemyInfo.special, enemyInfo.special))
) {
x = null;
y = null;
} else {
// 检查enemys里面是否使用了存在的内容
for (var i = 0; i < enemys.length; ++i) {
var one = enemys[i];
if (
id == one.id &&
one.locs != null &&
locEnemyInfo.atk == one.atk &&
locEnemyInfo.def == one.def &&
locEnemyInfo.hp == one.hp &&
core.plugin.arrsame(locEnemyInfo.special, one.special)
) {
one.locs.push([x, y]);
return;
}
}
enemyInfo = locEnemyInfo;
}
var id = enemy.id + ":" + x + ":" + y;
if (used[id]) return;
used[id] = true;
var specialText = core.enemys.getSpecialText(enemy, x, y, floorId);
var specialColor = core.enemys.getSpecialColor(enemy, x, y, floorId);
var critical = this.nextCriticals(enemy, 1, x, y, floorId);
if (critical.length > 0) critical = critical[0];
var criticalSpeed = core.nextCriticals_speed(enemy, 1, x, y, floorId);
if (criticalSpeed.length > 0) criticalSpeed = criticalSpeed[0];
var criticalSpell = core.nextCriticals_spell(enemy, 1, x, y, floorId);
if (criticalSpell.length > 0) criticalSpell = criticalSpell[0];
var e = core.clone(enemy);
for (var v in enemyInfo) {
e[v] = enemyInfo[v];
}
if (x != null && y != null) {
e.locs = [
[x, y]
];
}
e.name = core.getEnemyValue(enemy, "name", x, y, floorId);
e.specialText = specialText;
e.specialColor = specialColor;
e.damage = this.getDamage(enemy, x, y, floorId);
e.critical = critical[0];
e.criticalDamage = critical[1];
e.criticalAtk = [critical[0], critical[1]];
e.criticalSpeed = criticalSpeed;
e.criticalSpell = criticalSpell;
e.defDamage = this._getCurrentEnemys_addEnemy_defDamage(
enemy,
x,
y,
floorId
);
enemys.push(e);
};
enemys.prototype._getCurrentEnemys_addEnemy_defDamage = function (
enemy,
x,
y,
floorId
) {
var ratio = core.status.maps[floorId || core.status.floorId].ratio || 1;
return this.getDefDamage(enemy, ratio, x, y, floorId);
};
enemys.prototype._getCurrentEnemys_sort = function (enemys) {
return enemys.sort(function (a, b) {
if (a.damage == b.damage) {
return a.money - b.money;
}
if (a.damage == null) {
return 1;
}
if (b.damage == null) {
return -1;
}
return a.damage - b.damage;
});
};
////// 获得所有特殊属性的名称 //////
enemys.prototype.getSpecialText = function (enemy, x, y, floorId) {
if (typeof enemy == "string")
enemy = this.getEnemyInfo(enemy, null, x, y, floorId);
if (!enemy) return [];
var special = enemy.special;
var text = [];
var specials = this.getSpecials();
if (specials) {
for (var i = 0; i < specials.length; i++) {
if (this.hasSpecial(special, specials[i][0]))
text.push(this._calSpecialContent(enemy, specials[i][1]));
}
}
return text;
};
////// 获得所有特殊属性的颜色 //////
enemys.prototype.getSpecialColor = function (enemy, x, y, floorId) {
if (typeof enemy == "string")
enemy = this.getEnemyInfo(enemy, null, x, y, floorId);
if (!enemy) return [];
var special = enemy.special;
var colors = [];
var specials = this.getSpecials();
if (specials) {
for (var i = 0; i < specials.length; i++) {
if (this.hasSpecial(special, specials[i][0]))
colors.push(specials[i][3] || null);
}
}
return colors;
};
////// 获得所有特殊属性的额外标记 //////
enemys.prototype.getSpecialFlag = function (enemy, x, y, floorId) {
if (typeof enemy == "string")
enemy = getEnemyInfo(enemy, null, x, y, floorId);
if (!enemy) return [];
var special = enemy.special;
var flag = 0;
var specials = this.getSpecials();
if (specials) {
for (var i = 0; i < specials.length; i++) {
if (this.hasSpecial(special, specials[i][0]))
flag |= specials[i][4] || 0;
}
}
return flag;
};
////// 获得每个特殊属性的说明 //////
enemys.prototype.getSpecialHint = function (enemy, special) {
var specials = this.getSpecials();
if (special == null) {
if (specials == null) return [];
var hints = [];
for (var i = 0; i < specials.length; i++) {
if (this.hasSpecial(enemy, specials[i][0]))
hints.push(
"\r[" +
core.arrayToRGBA(specials[i][3] || "#FF6A6A") +
"]\\d" +
this._calSpecialContent(enemy, specials[i][1]) +
"\\d\r[]" +
this._calSpecialContent(enemy, specials[i][2])
);
}
return hints;
}
if (specials == null) return "";
for (var i = 0; i < specials.length; i++) {
if (special == specials[i][0])
return (
"\r[#FF6A6A]\\d" +
this._calSpecialContent(enemy, specials[i][1]) +
"\\d\r[]" +
this._calSpecialContent(enemy, specials[i][2])
);
}
return "";
};
ui.prototype._drawBook_drawName = function (
index,
enemy,
top,
left,
width
) {
// 绘制第零列(名称和特殊属性)
// 如果需要添加自己的比如怪物的称号等,也可以在这里绘制
core.setTextAlign("ui", "center");
if (core.enemys.getSpecialText(enemy).length == 0) {
core.fillText(
"ui",
enemy.name,
left + width / 2,
top + 35,
"#DDDDDD",
this._buildFont(17, true),
width
);
} else {
core.fillText(
"ui",
enemy.name,
left + width / 2,
top + 28,
"#DDDDDD",
this._buildFont(17, true),
width
);
switch (core.enemys.getSpecialText(enemy).length) {
case 1:
core.fillText(
"ui",
core.enemys.getSpecialText(enemy)[0],
left + width / 2,
top + 50,
core.arrayToRGBA(
(core.enemys.getSpecialColor(enemy) || [])[0] || "#FF6A6A"
),
this._buildFont(15, true),
width
);
break;
case 2:
// Step 1: 计算字体
var text =
core.enemys.getSpecialText(enemy)[0] +
" " +
core.enemys.getSpecialText(enemy)[1];
core.setFontForMaxWidth(
"ui",
text,
width,
this._buildFont(15, true)
);
// Step 2: 计算总宽度
var totalWidth = core.calWidth("ui", text);
var leftWidth = core.calWidth(
"ui",
core.enemys.getSpecialText(enemy)[0]
);
var rightWidth = core.calWidth(
"ui",
core.enemys.getSpecialText(enemy)[1]
);
// Step 3: 绘制
core.fillText(
"ui",
core.enemys.getSpecialText(enemy)[0],
left + (width + leftWidth - totalWidth) / 2,
top + 50,
core.arrayToRGBA(
(core.enemys.getSpecialColor(enemy) || [])[0] || "#FF6A6A"
)
);
core.fillText(
"ui",
core.enemys.getSpecialText(enemy)[1],
left + (width + totalWidth - rightWidth) / 2,
top + 50,
core.arrayToRGBA(
(core.enemys.getSpecialColor(enemy) || [])[1] || "#FF6A6A"
)
);
break;
default:
core.fillText(
"ui",
"多属性...",
left + width / 2,
top + 50,
"#FF6A6A",
this._buildFont(15, true),
width
);
}
}
};
ui.prototype._drawBookDetail_getInfo = function (index) {
var floorId =
core.floorIds[(core.status.event.ui || {}).index] ||
core.status.floorId;
// 清除浏览地图时的光环缓存
if (floorId != core.status.floorId && core.status.checkBlock) {
core.status.checkBlock.cache = {};
}
var enemys = core.enemys.getCurrentEnemys(floorId);
console.log(123);
if (enemys.length == 0) return [];
index = core.clamp(index, 0, enemys.length - 1);
var enemy = enemys[index];
var texts = core.enemys.getSpecialHint(enemy);
if (texts.length == 0) texts.push("该怪物无特殊属性。");
if (enemy.description) texts.push(enemy.description + "\r");
this._drawBookDetail_getTexts(enemy, floorId, texts);
texts.push("");
return [enemy, texts];
};
},
"一防减伤": function () {
// 在此增加新插件
//
ui.prototype._drawBook_drawRow3 = function (
index,
enemy,
top,
left,
width,
position
) {
// 绘制第三行
core.setTextAlign("ui", "left");
var b13 = this._buildFont(13, true),
f13 = this._buildFont(13, false);
var col1 = left,
col2 = left + (width * 9) / 25,
col3 = left + (width * 17) / 25;
core.fillText('ui', '攻击临界', col1 - 50, position, '#DDDDDD', f13);
core.fillText('ui', `[${core.formatBigNumber(enemy.criticalAtk?.[0] || 0)},${core.formatBigNumber(enemy.criticalAtk?.[1] || 0)}]`, col1 + 10, position, null, b13);
core.fillText('ui', '法强临界', col2 - 33, position, null, f13);
core.fillText('ui', `[${core.formatBigNumber(enemy.criticalSpell[0] || 0)},${core.formatBigNumber(enemy.criticalSpell?.[1] || 0)}]`, col2 + 27, position, null, b13);
//core.fillText('ui', '加防', col3, position, null, f13);
core.fillText("ui", "1防", col3, position, null, f13);
core.fillText(
"ui",
core.formatBigNumber(enemy.defDamage || 0),
col3 + 30,
position,
null,
b13
);
};
////// 1防减伤计算 //////
enemys.prototype.getDefDamage = function (enemy, k, x, y, floorId) {
if (typeof enemy == "string") enemy = core.material.enemys[enemy];
k = k || 1;
var nowDamage = this._getDamage(enemy, null, x, y, floorId);
var nextDamage = this._getDamage(
enemy, { def: core.status.hero.def + k },
x,
y,
floorId
);
if (nowDamage == null || nextDamage == null) return "???";
return nowDamage - nextDamage;
};
//防御倍数
enemys.prototype._getCurrentEnemys_addEnemy_defDamage = function (
enemy,
x,
y,
floorId
) {
var ratio = core.status.maps[floorId || core.status.floorId].ratio || 1;
//第一行为按照ratio值计算减防第二行为1防减伤
//return this.getDefDamage(enemy, ratio, x, y, floorId);
return this.getDefDamage(enemy, null, x, y, floorId);
};
},
"新道具栏/装备栏": function () {
// 这个插件有点离谱 个人觉得参数过多只会降低可读性,还不如硬编码
// 注:///// *** 裹起来的区域: 该区域内参数可以随意更改调整ui绘制 不会影响总体布局
// 请尽量修改该区域而不是其他区域 修改的时候最好可以对照现有ui修改
///// *** 道具类型
// cls对应name
var itemClsName = {
constants: "永久道具",
tools: "消耗道具",
};
// 一页最大放的道具数量 将把整个道具左栏分成num份 每份是一个道具项
var itemNum = 12;
///// ***
// 背景设置
function drawBoxBackground(ctx) {
core.setTextAlign(ctx, "left");
core.clearMap(ctx);
core.deleteCanvas("_selector");
var info = core.status.thisUIEventInfo || {};
///// *** 背景设置
var max = core.__PIXELS__;
var x = 2,
y = x,
w = max - x * 2,
h = w;
var borderWidth = 2,
borderRadius = 5, // radius:圆角矩形的圆角半径
borderStyle = "#fff";
var backgroundColor = "gray";
// 设置背景不透明度(0.85)
var backgroundAlpha = 0.85;
///// ***
var start_x = x + borderWidth / 2,
start_y = y + borderWidth / 2,
width = max - start_x * 2,
height = max - start_y * 2;
// 渐变色背景的一个例子(黑色渐变白色)
// 有关渐变色的具体知识请网上搜索canvas createGradient了解
/*
var grd = ctx.createLinearGradient(x, y, x + w, y);
grd.addColorStop(0, "black");
grd.addColorStop(1, "white");
backgroundColor = grd;
*/
// 使用图片背景要注释掉下面的strokeRect和fillRoundRect
// 图片背景的一个例子:
/*
core.drawImage(ctx, "xxx.png", x, y, w, h);
core.strokeRect(ctx, x, y, w, h, borderStyle, borderWidth);
*/
core.setAlpha(ctx, backgroundAlpha);
core.strokeRoundRect(
ctx,
x,
y,
w,
h,
borderRadius,
borderStyle,
borderWidth
);
core.fillRoundRect(
ctx,
start_x,
start_y,
width,
height,
borderRadius,
backgroundColor
);
core.setAlpha(ctx, 1);
///// *** 左栏配置
var leftbar_height = height;
// 左边栏宽度(width*0.6) 本身仅为坐标使用 需要与底下的rightbar_width(width*0.4)同时更改
var leftbar_width = width * 0.6;
///// ***
// xxx_right参数 代表最右侧坐标
var leftbar_right = start_x + leftbar_width - borderWidth / 2;
var leftbar_bottom = start_y + leftbar_height;
var leftbar_x = start_x;
var leftbar_y = start_y;
///// *** 道具栏配置
var boxName_color = "#fff";
var boxName_fontSize = 15;
var boxName_font = core.ui._buildFont(boxName_fontSize, true);
var arrow_x = 10 + start_x;
var arrow_y = 10 + start_y;
var arrow_width = 20;
var arrow_style = "white";
// 暂时只能是1 否则不太行 等待新样板(2.7.3)之后对drawArrow做优化
var arrow_lineWidth = 2;
// 右箭头
var rightArrow_right = leftbar_right - 10;
// 道具内栏顶部坐标 本质是通过该项 控制(道具栏顶部文字和箭头)与道具内栏顶部的间隔
var itembar_top = arrow_y + 15;
///// ***
var itembar_right = rightArrow_right;
var boxName =
core.status.event.id == "toolbox" ?
"\r[yellow]道具栏\r | 装备栏" :
"道具栏 | \r[yellow]装备栏\r";
core.drawArrow(
ctx,
arrow_x + arrow_width,
arrow_y,
arrow_x,
arrow_y,
arrow_style,
arrow_lineWidth
);
core.drawArrow(
ctx,
rightArrow_right - arrow_width,
arrow_y,
rightArrow_right,
arrow_y,
arrow_style,
arrow_lineWidth
);
core.setTextAlign(ctx, "center");
core.setTextBaseline(ctx, "middle");
var changeBox = function () {
var id = core.status.event.id;
core.closePanel();
if (id == "toolbox") core.openEquipbox();
else core.openToolbox();
};
core.fillText(
ctx,
boxName,
(leftbar_right + leftbar_x) / 2,
arrow_y + 2,
boxName_color,
boxName_font
);
///// *** 底栏按钮
var pageBtn_radius = 8;
// xxx_left 最左侧坐标
var pageBtn_left = leftbar_x + 3;
var pageBtn_right = leftbar_right - 3;
// xxx_bottom 最底部坐标
var pageBtn_bottom = leftbar_bottom - 2;
var pageBtn_borderStyle = "#fff";
var pageBtn_borderWidth = 2;
var pageText_color = "#fff";
// 底部按钮与上面的道具内栏的间隔大小
var bottomSpace = 8;
///// ***
drawItemListbox_setPageBtn(
ctx,
pageBtn_left,
pageBtn_right,
pageBtn_bottom,
pageBtn_radius,
pageBtn_borderStyle,
pageBtn_borderWidth
);
var page = info.page || 1;
var pageFontSize = pageBtn_radius * 2 - 4;
var pageFont = core.ui._buildFont(pageFontSize);
setPageItems(page);
var num = itemNum;
if (core.status.event.id == "equipbox") num -= 5;
var maxPage = info.maxPage;
var pageText = page + " / " + maxPage;
core.setTextAlign(ctx, "center");
core.setTextBaseline(ctx, "bottom");
core.fillText(
ctx,
pageText,
(leftbar_x + leftbar_right) / 2,
pageBtn_bottom,
pageText_color,
pageFont
);
addUIEventListener(
start_x,
start_y,
leftbar_right - start_x,
arrow_y - start_y + 13,
changeBox
);
var itembar_height = Math.ceil(
pageBtn_bottom -
pageBtn_radius * 2 -
pageBtn_borderWidth / 2 -
bottomSpace -
itembar_top
);
var oneItemHeight = (itembar_height - 4) / itemNum;
return {
x: start_x,
y: start_y,
width: width,
height: height,
leftbar_right: leftbar_right,
obj: {
x: arrow_x,
y: itembar_top,
width: itembar_right - arrow_x,
height: itembar_height,
oneItemHeight: oneItemHeight,
},
};
}
function drawItemListbox(ctx, obj) {
ctx = ctx || core.canvas.ui;
var itembar_x = obj.x,
itembar_y = obj.y,
itembar_width = obj.width,
itembar_height = obj.height,
itemNum = obj.itemNum,
oneItemHeight = obj.oneItemHeight;
var itembar_right = itembar_x + itembar_width;
var info = core.status.thisUIEventInfo || {};
var obj = {};
var page = info.page || 1,
index = info.index,
select = info.select || {};
///// *** 道具栏内栏配置
var itembar_style = "black";
var itembar_alpha = 0.7;
// 一个竖屏下减少道具显示的例子:
// if (core.domStyle.isVertical) itemNum = 10;
// 每个道具项的上下空隙占总高度的比例
var itembar_marginHeightRatio = 0.2;
// 左右间隔空隙
var item_marginLeft = 2;
var item_x = itembar_x + 2,
item_y = itembar_y + 2,
item_right = itembar_right - 2,
itemName_color = "#fff";
// 修改此项以更换闪烁光标
var item_selector = "winskin.webp";
///// ***
core.setAlpha(ctx, itembar_alpha);
core.fillRect(
ctx,
itembar_x,
itembar_y,
itembar_width,
itembar_height,
itembar_style
);
core.setAlpha(ctx, 1);
var pageItems = setPageItems(page);
var marginHeight = itembar_marginHeightRatio * oneItemHeight;
core.setTextBaseline(ctx, "middle");
var originColor = itemName_color;
for (var i = 0; i < pageItems.length; i++) {
itemName_color = originColor;
var item = pageItems[i];
// 设置某个的字体颜色的一个例子
// if (item.id == "xxx") itemName_color = "green";
drawItemListbox_drawItem(
ctx,
item_x,
item_right,
item_y,
oneItemHeight,
item_marginLeft,
marginHeight,
itemName_color,
pageItems[i]
);
if (index == i + 1)
core.ui._drawWindowSelector(
item_selector,
item_x + 1,
item_y - 1,
item_right - item_x - 2,
oneItemHeight - 2
);
item_y += oneItemHeight;
}
}
function drawToolboxRightbar(ctx, obj) {
ctx = ctx || core.canvas.ui;
var info = core.status.thisUIEventInfo || {};
var page = info.page || 1,
index = info.index || 1,
select = info.select || {};
var start_x = obj.x,
start_y = obj.y,
width = obj.width,
height = obj.height;
var toolboxRight = start_x + width,
toolboxBottom = start_y + height;
///// *** 侧边栏(rightbar)背景设置(物品介绍)
var rightbar_width = width * 0.4;
var rightbar_height = height;
var rightbar_lineWidth = 2;
var rightbar_lineStyle = "#fff";
///// ***
var rightbar_x = toolboxRight - rightbar_width - rightbar_lineWidth / 2;
var rightbar_y = start_y;
core.drawLine(
ctx,
rightbar_x,
rightbar_y,
rightbar_x,
rightbar_y + rightbar_height,
rightbar_lineStyle,
rightbar_lineWidth
);
// 获取道具id(有可能为null)
var itemId = select.id;
var item = core.material.items[itemId];
///// *** 侧边栏物品Icon信息
var iconRect_y = rightbar_y + 10;
// space间距
// 这里布局设定iconRect与侧边栏左边框 itemName与工具栏右边框 itemRect与itemName的间距均为space
var space = 15;
var iconRect_x = rightbar_x + space;
var iconRect_radius = 2,
iconRect_width = 32,
iconRect_height = 32,
iconRect_style = "#fff",
iconRect_lineWidth = 2;
///// ***
var iconRect_bottom = iconRect_y + iconRect_height,
iconRect_right = iconRect_x + iconRect_width;
///// *** 侧边栏各项信息
var itemTextFontSize = 15,
itemText_x = iconRect_x - 4,
itemText_y = Math.floor(start_y + rightbar_height * 0.25), // 坐标取整防止模糊
itemClsFontSize = 15,
itemClsFont = core.ui._buildFont(itemClsFontSize),
itemClsColor = "#fff",
itemCls_x = itemText_x - itemClsFontSize / 2,
itemCls_middle = (iconRect_bottom + itemText_y) / 2, //_middle代表文字的中心y坐标
itemNameFontSize = 18,
itemNameColor = "#fff",
itemNameFont = core.ui._buildFont(itemNameFontSize, true);
var itemName_x = iconRect_right + space;
var itemName_middle =
iconRect_y + iconRect_height / 2 + iconRect_lineWidth;
// 修改这里可以编辑未选中道具时的默认值
var defaultItem = {
cls: "constants",
name: "未知道具",
text: "没有道具最永久",
};
var defaultEquip = {
cls: "equips",
name: "未知装备",
text: "一无所有,又何尝不是一种装备",
equip: {
type: "装备",
},
};
///// ***
var originItem = item;
if (core.status.event.id == "equipbox") item = item || defaultEquip;
item = item || defaultItem;
var itemCls = item.cls,
itemName = item.name,
itemText = item.text;
itemText = core.replaceText(itemText);
if (!itemText) itemText = "该道具无描述。";
/* 一个根据道具id修改道具名字(右栏)的例子
* if (item.id == "xxx") itemNameColor = "red";
*/
var itemClsName = core.getItemClsName(item);
var itemNameMaxWidth =
rightbar_width - iconRect_width - iconRect_lineWidth * 2 - space * 2;
core.strokeRoundRect(
ctx,
iconRect_x,
iconRect_y,
iconRect_width,
iconRect_height,
iconRect_radius,
iconRect_style,
iconRect_lineWidth
);
if (item.id)
core.drawIcon(
ctx,
item.id,
iconRect_x + iconRect_lineWidth / 2,
iconRect_y + iconRect_lineWidth / 2,
iconRect_width - iconRect_lineWidth,
iconRect_height - iconRect_lineWidth
);
core.setTextAlign(ctx, "left");
core.setTextBaseline(ctx, "middle");
if (itemCls === "equips" && item.id) {
itemName = "【" + item.equipCls + "】" + itemName;
}
core.fillText(
ctx,
itemName,
itemName_x,
itemName_middle,
itemNameColor,
itemNameFont,
itemNameMaxWidth
);
if (!item.equip)
core.fillText(
ctx,
"【" + itemClsName + "】",
itemCls_x,
itemCls_middle,
itemClsColor,
itemClsFont
);
var statusText = "";
if (core.status.event.id == "equipbox") {
var type = item.equip.type;
if (typeof type == "string") type = core.getEquipTypeByName(type);
var compare = core.compareEquipment(item.id, core.getEquip(type));
var compare2;
if (item.equipCls === "双手剑")
compare2 = core.compareEquipment(null, core.getEquip(1));
if (
item.equipCls === "盾牌" &&
core.material.items[core.getEquip(0)]?.equipCls === "双手剑"
)
compare2 = core.compareEquipment(null, core.getEquip(0));
if (info.select.action == "unload")
compare = core.compareEquipment(null, item.id);
// --- 变化值...
for (var name in core.status.hero) {
if (typeof core.status.hero[name] != "number") continue;
var nowValue = core.getRealStatus(name);
// 查询新值
var newValue = Math.floor(
((core.getStatus(name) +
(compare.value[name] || 0) +
(compare2?.value[name] || 0)) *
(core.getBuff(name) * 100 +
(compare.percentage[name] || 0) +
(compare2?.percentage[name] || 0))) /
100
);
if (name === "mdef") {
var nowValue = core.getRealStatus(name);
var newValue = Math.round(
(core.getStatus(name) -
(compare.value[name] || 0) -
(compare2?.value[name] || 0)) *
(1 -
(1 - core.getBuff(name)) *
(compare.percentage[name] || 1) *
(compare2?.percentage[name] || 1))
);
}
if (nowValue == newValue) continue;
var color = newValue > nowValue ? "#00FF00" : "#FF0000";
nowValue = core.formatBigNumber(nowValue);
newValue = core.formatBigNumber(newValue);
if (name === "mdef") {
nowValue += "%";
newValue += "%";
}
statusText +=
core.getStatusLabel(name) +
" " +
nowValue +
"->\r[" +
color +
"]" +
newValue +
"\r\n";
}
}
itemText = statusText + itemText;
if (item.equip) {
core.drawTextContent(ctx, itemText, {
left: itemText_x,
top: itemCls_middle,
bold: false,
color: "white",
align: "left",
fontSize: itemTextFontSize,
maxWidth: rightbar_width -
(itemText_x - rightbar_x) * 2 +
itemTextFontSize / 2,
});
} else {
core.drawTextContent(ctx, itemText, {
left: itemText_x,
top: itemText_y,
bold: false,
color: "white",
align: "left",
fontSize: itemTextFontSize,
maxWidth: rightbar_width -
(itemText_x - rightbar_x) * 2 +
itemTextFontSize / 2,
});
}
///// *** 退出按钮设置
var btnRadius = 10;
var btnBorderWidth = 2;
var btnRight = toolboxRight - 2;
var btnBottom = toolboxBottom - 2;
var btnBorderStyle = "#fff";
///// ***
// 获取圆心位置
var btn_x = btnRight - btnRadius - btnBorderWidth / 2;
btn_y = btnBottom - btnRadius - btnBorderWidth / 2;
drawToolbox_setExitBtn(
ctx,
btn_x,
btn_y,
btnRadius,
btnBorderStyle,
btnBorderWidth
);
///// *** 使用按钮设置
var useBtnHeight = btnRadius * 2;
// 这里不设置useBtnWidth而是根据各项数据自动得出width
var useBtnRadius = useBtnHeight / 4;
var useBtn_x = rightbar_x + 4,
useBtn_y = btnBottom - useBtnHeight;
var useBtnBorderStyle = "#fff";
var useBtnBorderWidth = btnBorderWidth;
const batchUseBtn_x = useBtn_x + 50; // 个人觉得,搞这么多参数还不如硬编码
const hideBtn_y = useBtn_y - useBtnHeight - 8;
///// ***
drawToolbox_setUseBtn(
ctx,
useBtn_x,
useBtn_y,
useBtnRadius,
useBtnHeight,
useBtnBorderStyle,
useBtnBorderWidth
);
if (core.status.event.id === "toolbox") {
drawToolbox_setBatchUseBtn(
ctx,
batchUseBtn_x,
useBtn_y,
useBtnRadius,
useBtnHeight,
useBtnBorderStyle,
useBtnBorderWidth
);
}
drawToolbox_setHideBtn(
ctx,
useBtn_x,
hideBtn_y,
useBtnRadius,
useBtnHeight,
useBtnBorderStyle,
useBtnBorderWidth
);
drawToolbox_setShowHideBtn(
ctx,
rightbar_x,
useBtn_y,
useBtnHeight,
useBtnBorderStyle
);
}
function drawEquipbox_drawOthers(ctx, obj) {
var info = core.status.thisUIEventInfo;
///// *** 装备格设置
var equipList_lineWidth = 2;
var equipList_boxSize = 32;
var equipList_borderWidth = 2;
var equipList_borderStyle = "#fff";
var equipList_nameColor = "#fff";
///// ***
var equipList_x = obj.x + 4,
equipList_bottom = obj.obj.y - equipList_lineWidth,
equipList_y = equipList_bottom - obj.obj.oneItemHeight * reduceItem - 2,
equipList_height = equipList_bottom - equipList_y;
var equipList_right = obj.leftbar_right,
equipList_width = equipList_right - equipList_x;
core.drawLine(
ctx,
obj.x,
equipList_bottom + equipList_lineWidth / 2,
equipList_right,
equipList_bottom + equipList_lineWidth / 2,
equipList_borderStyle,
equipList_lineWidth
);
var toDrawList = core.status.globalAttribute.equipName,
len = toDrawList.length;
///// *** 装备格设置
var maxItem = 2;
var box_width = 32,
box_height = 32,
box_borderStyle = "#fff",
box_selectBorderStyle = "gold", // 选中的装备格的颜色
box_borderWidth = 2;
var boxName_fontSize = 14,
boxName_space = 2,
boxName_color = "#fff"; // 装备格名称与上面的装备格框的距离
var maxLine = Math.ceil(len / maxItem);
///// ***
var l = Math.sqrt(len);
if (Math.pow(l) == len && len != 4) {
if (l <= maxItem) maxItem = l;
}
maxItem = Math.min(toDrawList.length, maxItem);
info.equips = maxItem;
var boxName_font = core.ui._buildFont(boxName_fontSize);
// 总宽高减去所有装备格宽高得到空隙大小
var oneBoxWidth = box_width + box_borderWidth * 2;
var oneBoxHeight =
box_height + boxName_fontSize + boxName_space + 2 * box_borderWidth;
var space_y = (equipList_height - maxLine * oneBoxHeight) / (1 + maxLine),
space_x = (equipList_width - maxItem * oneBoxWidth) / (1 + maxItem);
var box_x = equipList_x + space_x,
box_y = equipList_y + space_y + 12;
for (var i = 0; i < 2; i++) {
var id = core.getEquip(i),
name = toDrawList[i];
if (i === 0) name = "主手";
if (i === 1) name = "副手";
var selectBorder = false;
if (core.status.thisUIEventInfo.select.type == i) selectBorder = true;
var borderStyle = selectBorder ?
box_selectBorderStyle :
box_borderStyle;
drawEquipbox_drawOne(
ctx,
name,
id,
box_x,
box_y,
box_width,
box_height,
boxName_space,
boxName_font,
boxName_color,
borderStyle,
box_borderWidth
);
var todo = new Function(
"core.clickOneEquipbox('" + id + "'," + i + ")"
);
addUIEventListener(
box_x - box_borderWidth / 2,
box_y - box_borderWidth / 2,
oneBoxWidth,
oneBoxHeight,
todo
);
box_x += space_x + oneBoxWidth;
if ((i + 1) % maxItem == 0) {
box_x = equipList_x + space_x;
box_y += space_y + oneBoxHeight;
}
}
if (core.material.items[core.getEquip(0)]?.equipCls === "双手剑") {
core.drawLine(
ctx,
equipList_x + space_x + space_x + oneBoxWidth,
equipList_y + space_y + 12,
equipList_x +
space_x +
space_x +
oneBoxWidth +
box_width +
box_borderWidth,
equipList_y + space_y + box_height + 12
);
core.drawLine(
ctx,
equipList_x + space_x + space_x + oneBoxWidth,
equipList_y + space_y + box_height + 12,
equipList_x +
space_x +
space_x +
oneBoxWidth +
box_width +
box_borderWidth,
equipList_y + space_y + 12
);
}
///// *** 装备格设置
var maxItem = 3;
var box_width = 32,
box_height = 32,
box_borderStyle = "#fff",
box_selectBorderStyle = "gold", // 选中的装备格的颜色
box_borderWidth = 2;
var boxName_fontSize = 14,
boxName_space = 2,
boxName_color = "#fff"; // 装备格名称与上面的装备格框的距离
var maxLine = Math.ceil(len / maxItem);
///// ***
var l = Math.sqrt(len);
if (Math.pow(l) == len && len != 4) {
if (l <= maxItem) maxItem = l;
}
maxItem = Math.min(toDrawList.length, maxItem);
info.equips = maxItem;
var boxName_font = core.ui._buildFont(boxName_fontSize);
// 总宽高减去所有装备格宽高得到空隙大小
var oneBoxWidth = box_width + box_borderWidth * 2;
var oneBoxHeight =
box_height + boxName_fontSize + boxName_space + 2 * box_borderWidth;
var space_y = (equipList_height - maxLine * oneBoxHeight) / (1 + maxLine),
space_x = (equipList_width - maxItem * oneBoxWidth) / (1 + maxItem);
var box_x = equipList_x + space_x,
box_y = equipList_y + space_y + space_y + oneBoxHeight;
for (var i = 2; i < len; i++) {
var id = core.getEquip(i),
name = toDrawList[i];
var selectBorder = false;
if (core.status.thisUIEventInfo.select.type == i) selectBorder = true;
var borderStyle = selectBorder ?
box_selectBorderStyle :
box_borderStyle;
drawEquipbox_drawOne(
ctx,
name,
id,
box_x,
box_y,
box_width,
box_height,
boxName_space,
boxName_font,
boxName_color,
borderStyle,
box_borderWidth
);
var todo = new Function(
"core.clickOneEquipbox('" + id + "'," + i + ")"
);
addUIEventListener(
box_x - box_borderWidth / 2,
box_y - box_borderWidth / 2,
oneBoxWidth,
oneBoxHeight,
todo
);
box_x += space_x + oneBoxWidth;
}
}
this.drawToolbox = function (ctx) {
ctx = ctx || core.canvas.ui;
core.status.thisEventClickArea = [];
var info = drawBoxBackground(ctx);
info.itemNum = itemNum;
drawItemListbox(ctx, info.obj);
drawToolboxRightbar(ctx, info);
core.setTextBaseline(ctx, "alphabetic");
core.setTextAlign("left");
};
var reduceItem = 4;
this.drawEquipbox = function (ctx) {
ctx = ctx || core.canvas.ui;
core.status.thisEventClickArea = [];
var info = drawBoxBackground(ctx);
info.itemNum = itemNum - reduceItem;
info.obj.y += info.obj.oneItemHeight * reduceItem;
info.obj.height -= info.obj.oneItemHeight * reduceItem;
drawItemListbox(ctx, info.obj);
drawEquipbox_drawOthers(ctx, info);
drawToolboxRightbar(ctx, info);
core.setTextBaseline(ctx, "alphabetic");
core.setTextAlign("left");
};
function drawEquipbox_drawOne(
ctx,
name,
id,
x,
y,
width,
height,
space,
font,
color,
style,
lineWidth
) {
if (id)
core.drawIcon(
ctx,
id,
x + lineWidth / 2,
y + lineWidth / 2,
width,
height
);
core.strokeRect(
ctx,
x,
y,
width + lineWidth,
height + lineWidth,
style,
lineWidth
);
core.setTextAlign(ctx, "center");
core.setTextBaseline(ctx, "top");
var tx = (x + x + lineWidth / 2 + width) / 2,
ty = y + height + (lineWidth / 2) * 3 + space;
core.fillText(ctx, name, tx, ty, color, font);
core.setAlpha(ctx, 1);
core.setTextBaseline(ctx, "alphabetic");
core.setTextAlign("left");
}
function drawItemListbox_drawItem(
ctx,
left,
right,
top,
height,
marginLeft,
marginHeight,
style,
id
) {
var info = core.status.thisUIEventInfo;
var nowClick = info.index;
var item = core.material.items[id] || {};
var name = item.name || "???";
var num = core.itemCount(id) || 0;
var fontSize = Math.floor(height - marginHeight * 2);
core.setTextAlign(ctx, "right");
var numText = "x" + num;
core.fillText(
ctx,
numText,
right - marginLeft,
top + height / 2,
style,
core.ui._buildFont(fontSize)
);
const hideInfo = core.getFlag("hideInfo", {});
if (
item &&
(hideInfo.hasOwnProperty(id) ? hideInfo[id] : item.hideInToolbox)
)
core.setAlpha(ctx, 0.5);
if (name != "???")
core.drawIcon(
ctx,
id,
left + marginLeft,
top + marginHeight,
fontSize,
fontSize
);
var text_x = left + marginLeft + fontSize + 2;
var maxWidth = right - core.calWidth(ctx, numText) - text_x;
core.setTextAlign(ctx, "left");
core.fillText(
ctx,
name,
text_x,
top + height / 2,
style,
core.ui._buildFont(fontSize),
maxWidth
);
core.setAlpha(ctx, 1);
var todo = new Function("core.clickItemFunc('" + id + "');");
addUIEventListener(left, top, right - left, height, todo);
}
function setPageItems(page) {
var num = itemNum;
if (core.status.event.id == "equipbox") num -= reduceItem;
var info = core.status.thisUIEventInfo;
if (!info) return;
page = page || info.page;
var items = core.getToolboxItems(
core.status.event.id == "toolbox" ? "all" : "equips",
core.getFlag("showHideItem", false)
);
info.allItems = items;
var maxPage = Math.ceil(items.length / num);
info.maxPage = maxPage;
var pageItems = items.slice((page - 1) * num, page * num);
info.pageItems = pageItems;
info.maxItem = pageItems.length;
if (items.length == 0 && pageItems.length == 0) info.index = null;
if (pageItems.length == 0 && info.page > 1) {
info.page = Math.max(1, info.page - 1);
return setPageItems(info.page);
}
return pageItems;
}
function drawToolbox_setExitBtn(ctx, x, y, r, style, lineWidth) {
core.strokeCircle(ctx, x, y, r, style, lineWidth);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var textSize = Math.sqrt(2) * r;
core.fillText(
ctx,
"x",
x,
y,
style,
core.ui._buildFont(textSize),
textSize
);
core.setTextAlign(ctx, "start");
core.setTextBaseline(ctx, "top");
var todo = function () {
core.closePanel();
};
addUIEventListener(x - r, y - r, r * 2, r * 2, todo);
}
function drawToolbox_setUseBtn(ctx, x, y, r, h, style, lineWidth) {
core.setTextAlign(ctx, "left");
core.setTextBaseline(ctx, "top");
var fontSize = h - 4;
var font = core.ui._buildFont(fontSize);
var text = core.status.event.id == "toolbox" ? "使用" : "装备";
if (core.status.thisUIEventInfo.select.action == "unload") text = "卸下";
var w = core.calWidth(ctx, text, font) + 2 * r + lineWidth / 2;
core.strokeRoundRect(ctx, x, y, w, h, r, style, lineWidth);
core.fillText(ctx, text, x + r, y + lineWidth / 2 + 2, style, font);
var todo = function () {
core.useSelectItemInBox();
};
addUIEventListener(x, y, w, h, todo);
}
function getSelectedItem() {
var info = core.status.thisUIEventInfo;
if (
!(
info &&
info.select.id && ["toolbox", "equipbox"].includes(core.status.event.id)
)
) {
core.drawFailTip("发生了未知错误!");
return;
}
return info.select.id;
}
function batchUse(item, count) {
try {
const itemCount = core.itemCount(item);
if (count > itemCount) count = itemCount;
core.closePanel();
for (let i = 0; i < count; i++) {
if (core.canUseItem(item)) core.useItem(item);
else return;
}
} catch (e) {
console.error(e);
core.drawFailTip("批量使用时出现未知错误!");
}
}
function drawToolbox_setBatchUseBtn(ctx, x, y, r, h, style, lineWidth) {
try {
const selectedItem = getSelectedItem();
let canBatchUse = eval(core.material.items[selectedItem]?.canBatchUse);
if (!canBatchUse) return;
} catch (error) {
console.error(error);
return;
}
core.setTextAlign(ctx, "left");
core.setTextBaseline(ctx, "top");
var fontSize = h - 4;
var font = core.ui._buildFont(fontSize);
var text = "批量使用";
var w = core.calWidth(ctx, text, font) + 2 * r + lineWidth / 2;
core.strokeRoundRect(ctx, x, y, w, h, r, style, lineWidth);
core.fillText(ctx, text, x + r, y + lineWidth / 2 + 2, style, font);
var todo = function () {
core.utils.myprompt("输入要使用该物品的次数(0~99)。", null, (value) => {
value = parseInt(value);
const id = getSelectedItem();
if (Number.isNaN(value) || value < 0 || value > 99) {
core.drawFailTip("输入不合法!");
return;
}
if (!core.canUseItem(id)) {
core.drawFailTip("当前无法使用该道具!");
return;
}
core.closePanel();
batchUse(id, value);
});
};
addUIEventListener(x, y, w, h, todo);
}
function drawToolbox_setHideBtn(ctx, x, y, r, h, style, lineWidth) {
core.setTextAlign(ctx, "left");
core.setTextBaseline(ctx, "top");
var fontSize = h - 4;
var font = core.ui._buildFont(fontSize);
var text = "显示/隐藏";
var w = core.calWidth(ctx, text, font) + 2 * r + lineWidth / 2;
core.strokeRoundRect(ctx, x, y, w, h, r, style, lineWidth);
core.fillText(ctx, text, x + r, y + lineWidth / 2 + 2, style, font);
var todo = function () {
//debugger;
var id = getSelectedItem();
let hideInfo = core.getFlag("hideInfo", {});
console.log(id);
if (hideInfo.hasOwnProperty(id)) {
hideInfo[id] = !hideInfo[id];
core.setFlag("hideInfo", hideInfo);
} else {
hideInfo[id] = !core.material.items[id].hideInToolbox;
core.setFlag("hideInfo", hideInfo);
}
if (core.status.event.id === "toolbox") core.plugin.drawToolbox();
else if (core.status.event.id === "equipbox")
core.plugin.drawEquipbox();
};
addUIEventListener(x, y, w, h, todo);
}
ui.prototype.getToolboxItems = function (cls, showHide) {
let list = Object.keys(core.status.hero.items[cls] || {});
if (cls === "all") {
for (let name in core.status.hero.items) {
if (name == "equips") continue;
list = list.concat(Object.keys(core.status.hero.items[name]));
}
if (!showHide)
list = list.filter(function (id2) {
const hideInfo = core.getFlag("hideInfo", {});
if (hideInfo.hasOwnProperty(id2)) return !hideInfo[id2];
else return !core.material.items[id2].hideInToolbox;
});
list = list.sort();
return list;
}
if (cls === "equips") {
if (!showHide)
list = list.filter(function (id2) {
const hideInfo = core.getFlag("hideInfo", {});
if (hideInfo.hasOwnProperty(id2)) return !hideInfo[id2];
else return !core.material.items[id2].hideInToolbox;
});
list = list.sort();
return list;
}
if (this.uidata.getToolboxItems) {
return this.uidata.getToolboxItems(cls, showHide);
}
if (!showHide)
list = list.filter(function (id2) {
return !core.material.items[id2].hideInToolbox;
});
list = list.sort();
return list;
};
function drawToolbox_setShowHideBtn(ctx, x, y, h, style) {
core.setTextAlign(ctx, "left");
core.setTextBaseline(ctx, "top");
var fontSize = h - 6;
var font = core.ui._buildFont(fontSize);
var text = "显示隐藏";
var w = core.calWidth(ctx, text, font);
h += 4;
const squareSize = h - 6;
x -= w + squareSize + 26;
const border = 2;
core.fillRect(ctx, x, y, squareSize, squareSize, " #F5F5F5");
if (core.hasFlag("showHideItem")) {
core.fillRect(
ctx,
x + border,
y + border,
squareSize - 2 * border,
squareSize - 2 * border,
"lime"
);
}
core.fillText(ctx, text, x + squareSize + 2, y + 4, style, font);
var todo = function () {
core.setFlag("showHideItem", !core.getFlag("showHideItem", false));
if (core.status.event.id === "toolbox") core.plugin.drawToolbox();
else if (core.status.event.id === "equipbox")
core.plugin.drawEquipbox();
};
addUIEventListener(x, y, w, h, todo);
}
function drawItemListbox_setPageBtn(
ctx,
left,
right,
bottom,
r,
style,
lineWidth
) {
var offset = lineWidth / 2 + r;
var x = left + offset;
var y = bottom - offset;
var pos = (Math.sqrt(2) / 2) * (r - lineWidth / 2);
core.fillPolygon(
ctx,
[
[x - pos, y],
[x + pos - 2, y - pos],
[x + pos - 2, y + pos],
],
style
);
core.strokeCircle(ctx, x, y, r, style, lineWidth);
var todo = function () {
core.addItemListboxPage(-1);
};
addUIEventListener(x - r - 2, y - r - 2, r * 2 + 4, r * 2 + 4, todo);
x = right - offset;
core.fillPolygon(
ctx,
[
[x + pos, y],
[x - pos + 2, y - pos],
[x - pos + 2, y + pos],
],
style
);
core.strokeCircle(ctx, x, y, r, style, lineWidth);
var todo = function () {
core.addItemListboxPage(1);
};
addUIEventListener(x - r - 2, y - r - 2, r * 2 + 4, r * 2 + 4, todo);
}
this.clickItemFunc = function (id) {
var info = core.status.thisUIEventInfo;
if (!info) return;
if (info.select.id == id) return core.useSelectItemInBox();
info.select = {};
info.select.id = id;
core.setIndexAndSelect("index");
refreshBox();
};
this.clickOneEquipbox = function (id, type) {
var info = core.status.thisUIEventInfo;
if (!info) return;
if (info.select.id == id && info.select.type == type)
core.useSelectItemInBox();
else
core.status.thisUIEventInfo.select = {
id: id,
type: type,
action: "unload",
};
return refreshBox();
};
this.useSelectItemInBox = function () {
var info = core.status.thisUIEventInfo;
if (!info) return;
if (!info.select.id) return;
var id = info.select.id;
if (core.status.event.id == "toolbox") {
core.events.tryUseItem(id);
// core.closePanel();
} else if (core.status.event.id == "equipbox") {
var action = info.select.action || "load";
info.index = 1;
if (action == "load") {
var type = core.getEquipTypeById(id);
let equipClsid = core.material.items[id]?.equipCls;
let equipCls0 = core.material.items[core.getEquip(0)]?.equipCls;
let equipCls1 = core.material.items[core.getEquip(1)]?.equipCls;
if (equipClsid === "双手剑") {
core.unloadEquip(0, function () {
core.status.route.push("unEquip:" + 0);
});
core.unloadEquip(1, function () {
core.status.route.push("unEquip:" + 1);
});
}
if (
equipCls0 === "双手剑" &&
!(equipClsid === "饰品" || equipClsid === "护具")
) {
core.unloadEquip(0, function () {
core.status.route.push("unEquip:" + 0);
});
}
core.loadEquip(id, function () {
core.status.route.push("equip:" + id);
info.select.type = type;
core.setIndexAndSelect("select");
core.drawEquipbox();
});
} else {
var type = info.select.type;
core.unloadEquip(type, function () {
core.status.route.push("unEquip:" + type);
info.select.type = type;
info.select.action = "load";
core.setIndexAndSelect("select");
core.drawEquipbox();
});
}
}
core.updateStatusBar();
};
this.setIndexAndSelect = function (toChange) {
var info = core.status.thisUIEventInfo;
if (!info) return;
setPageItems(info.page);
var index = info.index || 1;
var items = info.pageItems;
info.select.action = null;
info.select.type = null;
if (toChange == "index") info.index = items.indexOf(info.select.id) + 1;
info.select.id = items[info.index - 1];
};
this.addItemListboxPage = function (num) {
var info = core.status.thisUIEventInfo;
if (!info) return;
var maxPage = info.maxPage || 1;
info.page = info.page || 1;
info.page += num;
if (info.page <= 0) info.page = maxPage;
if (info.page > maxPage) info.page = 1;
info.index = 1;
setPageItems(info.page);
core.setIndexAndSelect("select");
refreshBox();
};
this.addItemListboxIndex = function (num) {
var info = core.status.thisUIEventInfo;
if (!info) return;
var maxItem = info.maxItem || 0;
info.index = info.index || 0;
info.index += num;
if (info.index <= 0) info.index = 1;
if (info.index > maxItem) info.index = maxItem;
core.setIndexAndSelect("select");
refreshBox();
};
this.addEquipboxType = function (num) {
var info = core.status.thisUIEventInfo;
var type = info.select.type;
if (type == null && num > 0) info.select.type = 0;
else info.select.type = type + num;
var max = core.status.globalAttribute.equipName.length;
if (info.select.type >= max) {
info.select = {};
core.setIndexAndSelect("select");
return core.addItemListboxPage(0);
} else {
var m = Math.abs(info.select.type);
if (info.select.type < 0) info.select.type = max - m;
core.setIndexAndSelect("select");
refreshBox();
return;
}
};
core.actions._keyDownToolbox = function (keycode) {
if (!core.status.thisEventClickArea) return;
if (keycode == 37) {
// left
core.addItemListboxPage(-1);
return;
}
if (keycode == 38) {
// up
core.addItemListboxIndex(-1);
return;
}
if (keycode == 39) {
// right
core.addItemListboxPage(1);
return;
}
if (keycode == 40) {
// down
core.addItemListboxIndex(1);
return;
}
};
////// 工具栏界面时,放开某个键的操作 //////
core.actions._keyUpToolbox = function (keycode) {
if (keycode == 81) {
core.ui.closePanel();
if (core.isReplaying()) core.control._replay_equipbox();
else core.openEquipbox();
return;
}
if (keycode == 84 || keycode == 27 || keycode == 88) {
core.closePanel();
return;
}
if (keycode == 13 || keycode == 32 || keycode == 67) {
var info = core.status.thisUIEventInfo;
if (info.select) {
core.useSelectItemInBox();
}
return;
}
};
core.actions._keyDownEquipbox = function (keycode) {
if (!core.status.thisEventClickArea) return;
if (keycode == 37) {
// left
var info = core.status.thisUIEventInfo;
if (info.index != null) return core.addItemListboxPage(-1);
return core.addEquipboxType(-1);
}
if (keycode == 38) {
// up
var info = core.status.thisUIEventInfo;
if (info.index == 1) {
info.select.type = core.status.globalAttribute.equipName.length - 1;
core.setIndexAndSelect();
return refreshBox();
}
if (info.index) return core.addItemListboxIndex(-1);
return core.addEquipboxType(-1 * info.equips);
}
if (keycode == 39) {
// right
var info = core.status.thisUIEventInfo;
if (info.index != null) return core.addItemListboxPage(1);
return core.addEquipboxType(1);
}
if (keycode == 40) {
// down
var info = core.status.thisUIEventInfo;
if (info.index) return core.addItemListboxIndex(1);
return core.addEquipboxType(info.equips);
}
};
core.actions._keyUpEquipbox = function (keycode, altKey) {
if (altKey && keycode >= 48 && keycode <= 57) {
core.items.quickSaveEquip(keycode - 48);
return;
}
if (keycode == 84) {
core.ui.closePanel();
if (core.isReplaying()) core.control._replay_toolbox();
else core.openToolbox();
return;
}
if (keycode == 81 || keycode == 27 || keycode == 88) {
core.closePanel();
return;
}
if (keycode == 13 || keycode == 32 || keycode == 67) {
var info = core.status.thisUIEventInfo;
if (info.select) core.useSelectItemInBox();
return;
}
};
core.registerAction(
"ondown",
"inEventClickAction",
function (x, y, px, py) {
if (!core.status.thisEventClickArea) return false;
var info = core.status.thisEventClickArea;
for (var i = 0; i < info.length; i++) {
var obj = info[i];
if (
px >= obj.x &&
px <= obj.x + obj.width &&
py > obj.y &&
py < obj.y + obj.height
) {
if (obj.todo) obj.todo();
break;
}
}
return true;
},
51
);
core.registerAction(
"onclick",
"stopClick",
function () {
if (core.status.thisEventClickArea) return true;
},
51
);
function addUIEventListener(x, y, width, height, todo) {
if (!core.status.thisEventClickArea) return;
var obj = {
x: x,
y: y,
width: width,
height: height,
todo: todo,
};
core.status.thisEventClickArea.push(obj);
}
this.initThisEventInfo = function () {
core.status.thisUIEventInfo = {
page: 1,
select: {},
};
core.status.thisEventClickArea = [];
};
function refreshBox() {
if (!core.status.event.id) return;
if (core.status.event.id == "toolbox") core.drawToolbox();
else core.drawEquipbox();
}
core.ui.closePanel = function () {
if (core.status.hero && core.status.hero.flags) {
// 清除全部临时变量
Object.keys(core.status.hero.flags).forEach(function (name) {
if (name.startsWith("@temp@") || /^arg\d+$/.test(name)) {
delete core.status.hero.flags[name];
}
});
}
this.clearUI();
core.maps.generateGroundPattern();
core.updateStatusBar(true);
core.unlockControl();
core.status.event.data = null;
core.status.event.id = null;
core.status.event.selection = null;
core.status.event.ui = null;
core.status.event.interval = null;
core.status.thisUIEventInfo = null;
core.status.thisEventClickArea = null;
};
this.getItemClsName = function (item) {
if (item == null) return itemClsName;
if (item.cls == "equips") {
if (typeof item.equip.type == "string") return item.equip.type;
var type = core.getEquipTypeById(item.id);
return core.status.globalAttribute.equipName[type];
} else return itemClsName[item.cls] || item.cls;
};
core.events.openToolbox = function (fromUserAction) {
if (core.isReplaying()) return;
if (!this._checkStatus("toolbox", fromUserAction)) return;
core.initThisEventInfo();
let info = core.status.thisUIEventInfo;
info.index = 1;
core.setIndexAndSelect("select");
core.drawToolbox();
};
core.events.openEquipbox = function (fromUserAction) {
if (core.isReplaying()) return;
if (!this._checkStatus("equipbox", fromUserAction)) return;
core.initThisEventInfo();
let info = core.status.thisUIEventInfo;
info.select.type = 0;
core.setIndexAndSelect("select");
core.drawEquipbox();
};
core.control._replay_toolbox = function () {
if (!core.isPlaying() || !core.isReplaying()) return;
if (!core.status.replay.pausing) return core.drawTip("请先暂停录像");
if (core.isMoving() || core.status.replay.animate || core.status.event.id)
return core.drawTip("请等待当前事件的处理结束");
core.lockControl();
core.status.event.id = "toolbox";
core.drawToolbox();
};
core.control._replay_equipbox = function () {
if (!core.isPlaying() || !core.isReplaying()) return;
if (!core.status.replay.pausing) return core.drawTip("请先暂停录像");
if (core.isMoving() || core.status.replay.animate || core.status.event.id)
return core.drawTip("请等待当前事件的处理结束");
core.lockControl();
core.status.event.id = "equipbox";
core.drawEquipbox();
};
core.control._replayAction_item = function (action) {
if (action.indexOf("item:") != 0) return false;
var itemId = action.substring(5);
if (!core.canUseItem(itemId)) return false;
if (
core.material.items[itemId].hideInReplay ||
core.status.replay.speed == 24
) {
core.useItem(itemId, false, core.replay);
return true;
}
core.status.event.id = "toolbox";
core.initThisEventInfo();
var info = core.status.thisUIEventInfo;
var items = core.getToolboxItems(
"all",
core.getFlag("showHideItem", false)
);
setPageItems(1);
var index = items.indexOf(itemId) + 1;
info.page = Math.ceil(index / info.maxItem);
info.index = index % info.maxItem || info.maxItem;
core.setIndexAndSelect("select");
setPageItems(info.page);
core.drawToolbox();
setTimeout(function () {
core.ui.closePanel();
core.useItem(itemId, false, core.replay);
}, core.control.__replay_getTimeout());
return true;
};
core.control._replayAction_equip = function (action) {
if (action.indexOf("equip:") != 0) return false;
var itemId = action.substring(6);
var items = core.getToolboxItems(
"equips",
core.getFlag("showHideItem", false)
);
var index = items.indexOf(itemId) + 1;
if (index < 1) {
core.removeFlag("__doNotCheckAutoEvents__");
return false;
}
var cb = function () {
var next = core.status.replay.toReplay[0] || "";
if (!next.startsWith("equip:") && !next.startsWith("unEquip:")) {
core.removeFlag("__doNotCheckAutoEvents__");
core.checkAutoEvents();
}
core.replay();
};
core.setFlag("__doNotCheckAutoEvents__", true);
core.status.route.push(action);
if (
core.material.items[itemId].hideInReplay ||
core.status.replay.speed == 24
) {
core.loadEquip(itemId, cb);
return true;
}
core.status.event.id = "equipbox";
core.initThisEventInfo();
var info = core.status.thisUIEventInfo;
setPageItems(1);
info.page = Math.ceil(index / info.maxItem);
info.index = index % info.maxItem || info.maxItem;
core.setIndexAndSelect("select");
setPageItems(info.page);
core.drawEquipbox();
setTimeout(function () {
core.ui.closePanel();
core.loadEquip(itemId, cb);
}, core.control.__replay_getTimeout());
return true;
};
core.control._replayAction_unEquip = function (action) {
if (action.indexOf("unEquip:") != 0) return false;
var equipType = parseInt(action.substring(8));
if (!core.isset(equipType)) {
core.removeFlag("__doNotCheckAutoEvents__");
return false;
}
var cb = function () {
var next = core.status.replay.toReplay[0] || "";
if (!next.startsWith("equip:") && !next.startsWith("unEquip:")) {
core.removeFlag("__doNotCheckAutoEvents__");
core.checkAutoEvents();
}
core.replay();
};
core.setFlag("__doNotCheckAutoEvents__", true);
core.status.route.push(action);
if (core.status.replay.speed == 24) {
core.unloadEquip(equipType, cb);
return true;
}
core.status.event.id = "equipbox";
core.initThisEventInfo();
var info = core.status.thisUIEventInfo;
setPageItems(1);
info.select.type = equipType;
core.setIndexAndSelect();
core.drawEquipbox();
setTimeout(function () {
core.ui.closePanel();
core.unloadEquip(equipType, cb);
}, core.control.__replay_getTimeout());
return true;
};
core.registerReplayAction("item", core.control._replayAction_item);
core.registerReplayAction("equip", core.control._replayAction_equip);
core.registerReplayAction("unEquip", core.control._replayAction_unEquip);
},
"技能树": function () {
// 在此增加新插件
//
// 已学习的技能等级 flags._hasSkill_
//打开技能树 core.myskilltree()
//不需要自动连线可以在 core.myskilltree_draw_line开头取消return的注释
core.AllStatus = function () {
//////在这个函数填写技能列表
var Skill_1 = {
召唤之书: {
name: "召唤之书",
text: "每种召唤物至多存在一只,当存在召唤物时,伤害以花花、灰太狼、牛牛、蝴蝶、兔兔、勇士顺序进行结算,直到当前序列召唤物死亡前,下一序列不受伤。吸血、反伤、固伤等特殊属性直接作用于勇士",
image: "I393",
maxLv: 1,
pos: [5, 7],
need: null,
exp: 0,
},
冥想: {
name: "冥想",
text: "默认主动技能战斗后恢复技能等级的MP",
image: "I395",
maxLv: 5,
pos: [5, 11],
need: null,
exp: 20 + 30 * flags._hasSkill_["冥想"], //基础+每个等级需求提升
},
召唤回收: {
name: "召唤回收",
text: "献祭召唤物回复20MP召唤物离场会导致对应图腾失效(涅槃图腾除外)",
image: "I405",
maxLv: 1,
pos: [5, 5],
need: [{ 召唤之书: 1 }],
exp: 100,
},
蝴蝶: {
name: "蝴蝶",
text: "40MP召唤50+30*Lv生命的蝴蝶在场时提升0.5*LV点战后回蓝效果向上取整并提升Lv点经验获取",
image: "I388",
maxLv: 5,
pos: [7, 9],
need: [{ 召唤之书: 1 }],
exp: 50 * flags._hasSkill_["蝴蝶"],
},
兔兔: {
name: "兔兔",
text: "40MP召唤50+30*Lv生命的兔兔在场时提勇士10%*LV金币获取",
image: "I389",
maxLv: 5,
pos: [9, 9],
need: [{ 召唤之书: 1 }],
exp: 50 * flags._hasSkill_["兔兔"],
},
狼狼: {
name: "狼狼",
text: "40MP召唤30+50*LV生命的灰太狼在场时提升勇士2+4*LV点攻击",
image: "I390",
maxLv: 5,
pos: [3, 9],
need: [{ 召唤之书: 1 }],
exp: 50 * flags._hasSkill_["狼狼"],
},
牛牛: {
name: "牛牛",
text: "40MP召唤50+40*LV生命的牛牛在场时提升勇士3%+5%*Lv伤害增幅",
image: "I391",
maxLv: 5,
pos: [5, 9],
need: [{ 召唤之书: 1 }],
exp: 50 * flags._hasSkill_["牛牛"],
},
花花: {
name: "花花",
text: "40MP召唤50+100*LV生命上限、具有同等生命的花花战后回复15%已损失生命值",
image: "I392",
maxLv: 5,
pos: [1, 9],
need: [{ 召唤之书: 1 }],
exp: 50 * flags._hasSkill_["花花"],
},
};
var Skill_2 = {
图腾之书: {
name: "图腾之书",
text: "学习后各图腾提升为1级获得图腾之书所有图腾共享20次公共战斗冷却",
image: "I394",
maxLv: 1,
pos: [1, 7],
need: null,
exp: 200,
},
再生: {
name: "再生",
text: "召唤兽:花花在场时可使用生命恢复提升10%*LV召唤兽:花花离场后移除",
image: "I392",
maxLv: 5,
pos: [1, 5],
need: [{ 图腾之书: 1 }],
exp: 50 * flags._hasSkill_["再生"],
},
嗜血: {
name: "嗜血",
text: "召唤兽:灰太狼在场时可使用勇士获得3%*LV吸血召唤兽:灰太狼离场后移除",
image: "I390",
maxLv: 5,
pos: [3, 5],
need: [{ 图腾之书: 1 }],
exp: 50 * flags._hasSkill_["嗜血"],
},
顽强: {
name: "顽强",
text: "召唤兽:牛牛在场时可使用除牛牛外所有召唤物战后增加5*Lv生命花花不可超过生命上限召唤兽:牛牛离场后移除",
image: "I391",
maxLv: 5,
pos: [5, 5],
need: [{ 图腾之书: 1 }],
exp: 50 * flags._hasSkill_["顽强"],
},
涅槃: {
name: "涅槃",
text: "召唤兽:蝴蝶在场时可使用战斗时任意召唤兽死亡时触发召唤兽临时消失该召唤兽生命变更为50*LV并在战斗结束后重新召唤此技能触发后战斗结束后移除",
image: "I388",
maxLv: 5,
pos: [7, 5],
need: [{ 图腾之书: 1 }],
exp: 50 * flags._hasSkill_["涅槃"],
},
贪婪: {
name: "贪婪",
text: "召唤兽:兔兔在场时可使用每次战斗后额外获得0.5*Lv点法力向上取整0和LV点金币召唤兽:兔兔离场后移除",
image: "I389",
maxLv: 5,
pos: [9, 5],
need: [{ 图腾之书: 1 }],
exp: 50 * flags._hasSkill_["贪婪"],
},
攻击: {
name: "攻击",
text: "被动提升勇者2点攻击",
image: "atk",
maxLv: null,
pos: [3, 9],
need: null,
exp: 20 + 5 * flags._hasSkill_["攻击"],
},
防御: {
name: "防御",
text: "被动提升勇者2点防御",
image: "def",
maxLv: null,
pos: [5, 9],
need: null,
exp: 20 + 5 * flags._hasSkill_["防御"],
},
魔力上限: {
name: "魔力上限",
text: "被动提升勇者10点MP上限",
image: "mana",
maxLv: null,
pos: [7, 9],
need: null,
exp: 30 + 10 * flags._hasSkill_["魔力上限"],
},
火球: {
name: "火球",
text: "消耗8MP第一回合不进行普通攻击改为发出一枚100%+LV*20%攻击伤害的火球",
image: "I397",
maxLv: 5,
pos: [3, 11],
need: null,
exp: 30 * flags._hasSkill_["火球"],
},
嘲讽: {
name: "嘲讽",
text: "消耗10MP本次强制怪物以勇士为目标同时勇士获得5%*LV减伤",
image: "I399",
maxLv: 5,
pos: [5, 11],
need: null,
exp: 30 + 30 * flags._hasSkill_["嘲讽"],
},
治疗: {
name: "治疗",
text: "消耗10点MP战斗时勇者第一回合改为为自己施加100%+20%*lv防御数值的治疗术",
image: "I396",
maxLv: 5,
pos: [7, 11],
need: null,
exp: 20 + 20 * flags._hasSkill_["治疗"],
},
};
var AllSkill = [Skill_1, Skill_2]; ///////////把每页技能数组id填入这里比如三页技能要加上Skill3
return AllSkill;
};
core.myskilltree = function () {
//进入
if (!flags._hasSkill_) flags._hasSkill_ = {};
core.clearUI();
core.status.holdingKeys = [];
core.lockControl();
core.status.event.page = 0;
core.status.event.data = 0;
let Skill = core.AllStatus()[core.status.event.page];
core.status.event.pos = Object.values(Skill)[0].pos;
//初始坐标
core.status.event.id = "myskilltree";
core.myskilltree_draw(); ///重绘页面
};
core.quit_myskilltree = function () {
core.clearUI();
core.status.event.id = null;
core.unlockControl();
if (core.isReplaying()) core.replay();
};
core.myskilltree_add = function () {
//加点
let Skill = core.AllStatus()[core.status.event.page];
var Index = core.status.event.data;
var id = Object.keys(Skill)[Index];
var data = Object.values(Skill)[Index];
let ness;
if (data.need) {
if (Array.isArray(data.need)) {
//多前置
for (var need of data.need) {
var need_name = Object.keys(need);
//if (!Object.keys(flags._hasSkill_).includes(need))
if (
!flags._hasSkill_[need_name] ||
flags._hasSkill_[need_name] < need[need_name]
) {
ness = true;
}
}
} else {
//单前置
if (!Object.keys(flags._hasSkill_).includes(data.need)) {
ness = true;
}
}
}
if (ness) {
core.drawTip(id + " 未习得前置");
} else if (hero.exp < data.exp) {
core.drawTip(id + " 经验值不足");
} else if (flags._hasSkill_[id] === data.maxLv) {
core.drawTip(id + " 技能已满级");
} else {
///学习成功
flags._hasSkill_[id] = flags._hasSkill_[id] + 1 || 1;
hero.exp -= data.exp;
core.myskilltree_get(id);
core.status.route.push(
"skill:" +
Index +
":" +
data.pos[0] +
":" +
data.pos[1] +
":" +
core.status.event.page
);
}
core.myskilltree_draw(); ///重绘页面
};
core.myskilltree_get = function (id) {
////技能升级效果
switch (id) {
case "图腾之书":
flags._hasSkill_["再生"] = 1;
flags._hasSkill_["嗜血"] = 1;
flags._hasSkill_["顽强"] = 1;
flags._hasSkill_["涅槃"] = 1;
flags._hasSkill_["贪婪"] = 1;
core.getItem("I394");
break;
case "攻击":
core.status.hero.atk += 2;
break;
case "防御":
core.status.hero.def += 2;
break;
case "魔力上限":
core.status.hero.manamax += 10;
break;
case "嘲讽":
core.getItem("I399");
break;
case "治疗":
core.getItem("I396");
break;
}
};
core.myskilltree_draw = function () {
//绘制 总
core.clearMap("ui");
let Skill = core.AllStatus()[core.status.event.page];
var Index = core.status.event.data;
///推荐在此处 drawImage 绘制背景图
core.setAlpha("ui", 0.8);
core.setFillStyle("ui", "#dddddd");
core.fillRect("ui", 0, 0, core.__PIXELS__, core.__PIXELS__, "#000000");
core.setAlpha("ui", 1);
let name = Object.keys(Skill)[Index];
let text = Object.values(Skill)[Index].text;
let exp = Object.values(Skill)[Index].exp;
core.myskilltree_draw_text(name, text, exp);
core.myskilltree_draw_tree();
core.setTextAlign("ui", "right");
core.fillText(
"ui",
"返回游戏",
342,
342,
"#ffffff",
ui.prototype._buildFont(15, true)
);
core.setTextAlign("ui", "center");
core.fillText(
"ui",
"上页",
352 / 2 - 50,
342,
"#ffffff",
ui.prototype._buildFont(15, true)
);
core.fillText(
"ui",
"下页",
352 / 2 + 50,
342,
"#ffffff",
ui.prototype._buildFont(15, true)
);
core.fillText(
"ui",
core.status.event.page + 1 + "/" + core.AllStatus().length,
352 / 2,
342,
"#ffffff",
ui.prototype._buildFont(15, true)
);
};
core.myskilltree_draw_tree = function () {
//绘制 树
let x0 = 0,
y0 = -80; //相对坐标
let nx = 32,
ny = 32; //间隔
let Skill = core.AllStatus()[core.status.event.page];
for (var value of Object.values(Skill)) {
//先绘制前置线条,否则会遮挡技能图标
let posx = value.pos[0];
let posy = value.pos[1];
//前置技能
if (value.need) {
if (Array.isArray(value.need)) {
//多前置
for (var need of value.need) {
var need_name = Object.keys(need);
//if (!Object.keys(flags._hasSkill_).includes(need))
if (
!flags._hasSkill_[need_name] ||
flags._hasSkill_[need_name] < need[need_name]
) {
core.setFilter("ui", "brightness(50%)grayscale(70%)");
core.setAlpha("ui", 0.7);
}
let need_x = Skill[need_name].pos[0];
let need_y = Skill[need_name].pos[1];
core.myskilltree_draw_line(
x0 + posx * nx + 16,
y0 + posy * ny + 16,
x0 + posx * nx + 16,
y0 + need_y * ny + 16,
1
); //y
core.myskilltree_draw_line(
x0 + posx * nx + 16,
y0 + need_y * ny + 16,
x0 + need_x * nx + 16,
y0 + need_y * ny + 16,
2
); //x
}
} else {
//单前置
if (!Object.keys(flags._hasSkill_).includes(value.need)) {
core.setFilter("ui", "brightness(50%)grayscale(70%)");
core.setAlpha("ui", 0.7);
}
let need_x = Skill[value.need].pos[0];
let need_y = Skill[value.need].pos[1];
core.myskilltree_draw_line(
x0 + posx * nx + 16,
y0 + posy * ny + 16,
x0 + posx * nx + 16,
y0 + need_y * ny + 16,
1
); //y
core.myskilltree_draw_line(
x0 + posx * nx + 16,
y0 + need_y * ny + 16,
x0 + need_x * nx + 16,
y0 + need_y * ny + 16,
2
); //x
}
}
core.setFilter("ui", "");
core.setAlpha("ui", 1);
}
core.setTextAlign("ui", "center");
let Ex = core.status.event.pos[0];
let Ey = core.status.event.pos[1];
//技能图标
for (var value of Object.values(Skill)) {
let posx = value.pos[0];
let posy = value.pos[1];
let image = value.image;
let boxx = x0 + posx * nx;
let boxy = y0 + posy * ny;
let name = value.name;
let lv = flags._hasSkill_[name] || 0;
//图标
core.myskilltree_draw_box(boxx, boxy, image);
//未学习前置的技能 黑色滤镜
let ness;
if (value.need) {
if (Array.isArray(value.need)) {
//多前置
for (var need of value.need) {
var need_name = Object.keys(need);
// if (!Object.keys(flags._hasSkill_).includes(need))
if (
!flags._hasSkill_[need_name] ||
flags._hasSkill_[need_name] < need[need_name]
) {
core.setFilter("ui", "brightness(0%)");
core.setAlpha("ui", 0.7);
ness = true;
}
}
} else {
//单前置
if (!Object.keys(flags._hasSkill_).includes(value.need)) {
core.setFilter("ui", "brightness(0%)");
core.setAlpha("ui", 0.7);
ness = true;
}
}
}
//未学习的技能 灰色滤镜
if (!ness && !flags._hasSkill_[name]) {
core.setFilter("ui", "brightness(50%)grayscale(70%)");
core.setAlpha("ui", 0.7);
}
core.drawIcon("ui", image, boxx, boxy, 32, 32);
//core.drawImage('ui', image, 0, 0, 32, 32, boxx, boxy, 32, 32);
core.setFilter("ui", "");
core.setAlpha("ui", 1);
if (Ex === posx && Ey === posy)
core.strokeRoundRect(
"ui",
boxx - 2,
boxy - 2,
36,
36,
4,
"#ffff80",
2
);
core.setAlpha("ui", 0.6);
core.fillRoundRect("ui", boxx, boxy + 16 + 8, 32, 16, 4, "#000000");
core.setAlpha("ui", 1);
core.fillText(
"ui",
lv + (value.maxLv == null ? "" : "/" + value.maxLv),
boxx + 16,
boxy + 16 + 8 + 15,
lv === value.maxLv ? "#ffff80" : "#ffffff",
ui.prototype._buildFont(15, true)
);
}
};
core.myskilltree_draw_text = function (name, text, exp) {
// 绘制 说明文本
let x0 = 0,
y0 = 50; //相对坐标
core.setTextAlign("ui", "left");
core.fillText(
"ui",
name,
x0 + 10,
y0 - 20,
"#ffffff",
ui.prototype._buildFont(22, true)
);
core.fillText(
"ui",
"升级经验:" + exp + "/" + hero.exp,
x0 + 200,
y0 - 20,
"#ffffff",
ui.prototype._buildFont(15, true)
);
var height = null;
var max_height = 110;
if (text) {
for (var fontSize = 17; fontSize >= 9; fontSize -= 2) {
var config = {
left: x0 + 10,
top: y0 - 12,
fontSize: 12,
maxWidth: 352 - 20,
bold: true,
color: "#E1E1E1",
};
height = 42 + core.getTextContentHeight(text, config);
if (height < max_height || fontSize == 9) {
core.drawTextContent("ui", text, config);
break;
}
}
}
};
core.myskilltree_draw_box = function (x, y, image) {
//盒子包装
let col = "#dddddd";
core.fillRoundRect("ui", x, y, 32, 32, 4, col);
core.strokeRoundRect("ui", x - 2, y - 2, 36, 36, 4, col, 1);
};
core.myskilltree_draw_line = function (x1, y1, x2, y2, type) {
//线 包装
//return;
let col = "#dddddd";
if (type === 1) {
core.drawLine("ui", x1 - 4, y1, x2 - 4, y2, col, 1);
core.drawLine("ui", x1 + 4, y1, x2 + 4, y2, col, 1);
core.drawLine("ui", x1, y1, x2, y2, col, 4);
} else if (type === 2) {
core.drawLine("ui", x1, y1 - 4, x2, y2 - 4, col, 1);
core.drawLine("ui", x1, y1 + 4, x2, y2 + 4, col, 1);
core.drawLine("ui", x1, y1, x2, y2, col, 4);
}
};
core.myChooseOnKey = function (dir) {
//计算方向键应选择的坐标
let Skill = core.AllStatus()[core.status.event.page];
let x = core.status.event.pos[0];
let y = core.status.event.pos[1];
var temp = [];
var temp2 = [];
for (var i in Object.values(Skill)) {
let posx = Object.values(Skill)[i].pos[0];
let posy = Object.values(Skill)[i].pos[1];
let Index = Number(i);
let ok =
(dir === "up" && posy < y) ||
(dir === "down" && posy > y) ||
(dir === "left" && posx < x) ||
(dir === "right" && posx > x);
let ok2 =
((dir === "up" || dir === "down") && x === posx) ||
((dir === "left" || dir === "right") && y === posy);
if (ok) temp.push([posx, posy, Index]);
if (ok && ok2) temp2.push([posx, posy, Index]);
}
if (temp2[0]) {
if (temp2.length === 1) {
return temp2[0];
} else {
let R = 999;
let rx, ry, ri;
for (var value of temp2) {
let posx = value[0];
let posy = value[1];
let Index = value[2];
let r = Math.abs(posx - x + posy - y);
if (r < R) {
R = r;
rx = posx;
ry = posy;
ri = Index;
}
}
return [rx, ry, ri];
}
} else if (temp.length === 1) {
return temp[0];
} else {
let R = 9999;
let rx, ry, ri;
for (var value of temp) {
let posx = value[0];
let posy = value[1];
let Index = value[2];
let r = Math.pow(posx - x, 2) + Math.pow(posy - y, 2);
if (r < R) {
R = r;
rx = posx;
ry = posy;
ri = Index;
}
}
if (rx && ry) return [rx, ry, ri];
}
};
this.myskilltree_keyDown = function (keycode) {
if (core.status.event.id !== "myskilltree") return false;
let Skill = core.AllStatus()[core.status.event.page];
let temp;
if (
keycode === 37 &&
!core.myChooseOnKey("left") &&
core.status.event.page > 0
) {
core.status.event.page = core.status.event.page - 1;
core.status.event.data = 0;
Skill = core.AllStatus()[core.status.event.page];
core.status.event.pos = Object.values(Skill)[0].pos;
} else if (
keycode === 39 &&
!core.myChooseOnKey("right") &&
core.status.event.page < core.AllStatus().length - 1
) {
core.status.event.page = core.status.event.page + 1;
core.status.event.data = 0;
Skill = core.AllStatus()[core.status.event.page];
core.status.event.pos = Object.values(Skill)[0].pos;
} else if (keycode === 38 && core.myChooseOnKey("up")) {
temp = core.myChooseOnKey("up");
} else if (keycode === 40 && core.myChooseOnKey("down")) {
temp = core.myChooseOnKey("down");
} else if (keycode === 37 && core.myChooseOnKey("left")) {
temp = core.myChooseOnKey("left");
} else if (keycode === 39 && core.myChooseOnKey("right")) {
temp = core.myChooseOnKey("right");
}
if (temp) {
core.status.event.pos[0] = temp[0];
core.status.event.pos[1] = temp[1];
core.status.event.data = temp[2];
}
if (keycode === 32 || keycode === 13 || keycode === 67)
core.myskilltree_add();
core.myskilltree_draw(); ///重绘页面
return true;
};
core.registerAction("keyDown", "myskilltree", "myskilltree_keyDown", 100);
var myskilltree = function (keycode) {
if (core.status.event.id !== "myskilltree") return false;
if (keycode == 88 || keycode == 70 || keycode == 27) {
////x、q和Esc退出
core.quit_myskilltree();
}
return true; ///全部拦截
};
core.registerAction("keyUp", "myskilltree", myskilltree, 100);
this.myskilltree_onmove = function (x, y, px, py) {
if (core.status.event.id !== "myskilltree") return false;
//if (core.domStyle.isVertical) return false
let x0 = 0,
y0 = -80; //相对坐标
let nx = 32,
ny = 32; //间隔
if (py >= 65 && py <= 320) {
let Skill = core.AllStatus()[core.status.event.page];
for (var i in Object.values(Skill)) {
let posx = Object.values(Skill)[i].pos[0];
let posy = Object.values(Skill)[i].pos[1];
let boxx = x0 + posx * nx;
let boxy = y0 + posy * ny;
if (px >= boxx && px <= boxx + 32 && py >= boxy && py <= boxy + 32) {
if (core.status.event.data !== Number(i)) {
core.status.event.pos[0] = posx;
core.status.event.pos[1] = posy;
core.status.event.data = Number(i);
core.myskilltree_draw(); ///重绘页面
}
//core.status.event.mouse = Number(i)
//if(core.status.event.mouse)
}
}
}
return true;
};
core.registerAction("onmove", "myskilltree", "myskilltree_onmove", 100);
core.registerAction(
"onclick",
"myskilltree",
function (x, y, px, py) {
if (core.status.event.id !== "myskilltree") return false;
let x0 = 0,
y0 = -80; //相对坐标
let nx = 32,
ny = 32; //间隔
let Skill = core.AllStatus()[core.status.event.page];
//console.log(px + ',' + py);
if (py >= 65 && py <= 320) {
for (var i in Object.values(Skill)) {
let posx = Object.values(Skill)[i].pos[0];
let posy = Object.values(Skill)[i].pos[1];
let boxx = x0 + posx * nx;
let boxy = y0 + posy * ny;
if (
px >= boxx &&
px <= boxx + 32 &&
py >= boxy &&
py <= boxy + 32
) {
if (core.status.event.data === Number(i)) {
core.myskilltree_add();
} else if (core.status.event.data !== Number(i)) {
core.status.event.pos[0] = posx;
core.status.event.pos[1] = posy;
core.status.event.data = Number(i);
core.myskilltree_draw(); ///重绘页面
}
}
}
} else if (px >= 280 && py >= 320) {
core.quit_myskilltree();
} else if (
px >= 90 &&
px <= 165 &&
py >= 320 &&
core.status.event.page > 0
) {
core.status.event.page = core.status.event.page - 1;
core.status.event.data = 0;
core.status.event.pos = Object.values(Skill)[0].pos;
core.myskilltree_draw(); ///重绘页面
} else if (
px >= 190 &&
px <= 280 &&
py >= 320 &&
core.status.event.page < core.AllStatus().length - 1
) {
core.status.event.page = core.status.event.page + 1;
core.status.event.data = 0;
core.status.event.pos = Object.values(Skill)[0].pos;
core.myskilltree_draw(); ///重绘页面
}
return true;
},
100
);
control.prototype._replayAction_skill = function (action) {
if (action.indexOf("skill:") != 0) return false;
core.myskilltree();
var pos = action.substring(6).split(":");
core.status.event.data = Number(pos[0]);
core.status.event.pos[0] = Number(pos[1]);
core.status.event.pos[1] = Number(pos[2]);
core.status.event.page = Number(pos[3]);
core.myskilltree_add();
//core.myskilltree_draw();
core.replay();
if (core.status.replay.speed == 24) {
core.quit_myskilltree();
return true;
}
setTimeout(function () {
core.quit_myskilltree();
//core.ui.closePanel();
}, core.control.__replay_getTimeout());
return true;
};
core.registerReplayAction("skill", control.prototype._replayAction_skill);
},
"animate": function () {
// -------------------- 插件说明 -------------------- //
// github仓库https://github.com/unanmed/animate
// npm包名mutate-animate
// npm地址https://www.npmjs.com/package/mutate-animate
// 不要去尝试读这个插件,这个插件是经过了打包的,不是人类可读的(
// 想读的话可以去github读
// 该插件是一个轻量型多功能动画插件,可以允许你使用内置或自定义的速率曲线或轨迹等
// 除此之外,你还可以自定义绘制函数,来让你的动画可视化
// -------------------- 安装说明 -------------------- //
// 直接复制到插件中即可注意所有插件中不能出现插件名为animate的插件
// 该插件分为动画和渐变两部分,教程分开,动画在前,渐变在后
// -------------------- 动画使用教程 -------------------- //
// 1. 首先创建一个异步函数
// async function ani() { }
// 2. 引入插件中的类和函数,引入内容要看个人需求,所有可用的函数在本插件末尾可以看到
// const { Animation, linear, bezier, circle, hyper, trigo, power, inverseTrigo, shake, sleep } = core.plugin.animate
// 3. 在函数内部创建一个动画
// const animate = new Animation();
// 4. 为动画创建一个绘制函数这里以绘制一个矩形为例当然也可以使用core.fillRect替代ctx.fillRect来绘制矩形
// const ctx = core.createCanvas('animate', 0, 0, 416, 416, 100);
// ctx.save();
// const fn = () => {
// ctx.restore();
// ctx.save();
// ctx.clearRect(0, 0, 800, 800);
// ctx.translate(animate.x, animate.y);
// ctx.rotate(animate.angle * Math.PI / 180);
// const size = animate.size;
// ctx.fillRect(-30 * size, -30 * size, 60 * size, 60 * size);
// }
// animate.ticker.add(fn);
// 5. 执行动画
// 下面先对一些概念进行解释
// 动画分为很多种内置的有move(移动至某一点) rotate(旋转) scale(放缩) moveAs(以指定路径移动) shake(震动)
// 对于不同的动画种类其所对应的属性也不同move moveAs shake均对应x和y这两个属性
// rotate对应anglescale对应size。你也可以自定义属性这个之后会提到
// 除了执行动画之外,这里还提供了三个等待函数,可以等待某个动画执行完毕,以及一个等待指定时长的函数
// 分别是animate.n(等待指定数量的动画执行完毕)
// animate.w(等待指定类型的动画执行完毕,也可以是自定义类型)
// animate.all(等待所有动画执行完毕)
// sleep(等待指定时长)
// 执行动画时,要求一个渐变函数,当然这个插件内置了非常丰富的渐变函数,也就是速率曲线。
// 线性渐变函数 linear(),该函数返回一个线性变化函数
// 三角渐变函数 trigo('sin' | 'sec', EaseMode),该函数返回一个指定属性的三角函数变化函数
// 其中EaseMode可以填'in' 'out' 'in-out' 'center'
// 分别表示 慢-快 快-慢 慢-快-慢 快-慢-快
// 幂函数渐变 power(n, EaseMode)该函数返回一个以x^n变化的函数n是指数
// 双曲渐变函数 hyper('sin' | 'tan' | 'sec', EaseMode),该函数返回一个双曲函数,分别是双曲正弦、双曲正切、双曲正割
// 反三角渐变函数 inverseTrigo('sin' | 'tan', EaseMode),该函数返回一个反三角函数
// 贝塞尔曲线渐变函数 bezier(...cps),参数为贝塞尔曲线的控制点纵坐标(横坐标不能自定义,毕竟一个时刻不能对应多个速率)
// 示例bezier(0.4, 0.2, 0.7); // 三个控制点的四次贝塞尔曲线渐变函数
// 了解完渐变函数以后,这里还有一个特殊的渐变函数-shake
// shake(power, timing),这个函数是一个震荡函数,会让一个值来回变化,实现震动的效果
// 其中power是震动的最大值timing是渐变函数描述了power在震动时大小的变化
// 下面,我们就可以进行动画的执行了,我们以 运动 + 旋转 + 放缩为例
// animate.mode(hyper('sin', 'out')) // 设置渐变函数为 双曲正弦 快 -> 慢,注意不能加分号
// .time(1000) // 设置动画的执行时间为1000毫秒
// .move(300, 300) // 移动至[300, 300]的位置
// .relative() // 设置相对模式为相对之前,与之前为相加的关系
// .mode(power(3, 'center')) // 设置为 x^3 快-慢-快 的渐变函数
// .time(3000)
// .rotate(720) // 旋转720度
// .absolute() // 设置相对模式为绝对
// .mode(trigo('sin', 'in')) // 设置渐变函数为 正弦 慢 -> 快
// .time(1500)
// .scale(3); // 放缩大小至3倍
// 这样,我们就把三种基础动画都执行了一遍,同时,这种写法非常直观,出现问题时也可以很快地找到问题所在
// 下面,我们需要等待动画执行完毕,因为同一种动画不可能同时执行两个
// await animate.n(1); // 等待任意一个动画执行完毕别把await忘了
// await animate.w('scale'); // 等待放缩动画执行完毕
// await animate.all(); // 等待所有动画执行完毕
// await sleep(1000); // 等待1000毫秒
// 下面,还有一个特殊的动画函数-moveAs
// 这是一个非常强大的函数,它允许你让你的物体按照指定路线运动
// 说到这,我们需要先了解一下运动函数。
// 该插件内置了两个运动函数,分别是圆形运动和贝塞尔曲线运动
// 圆形运动 circle(r, n, timing, inverse)r是圆的半径n是圈数timing描述半径大小的变化inverse说明了是否翻转timing函数后面三个可以不填
// 贝塞尔曲线 bezierPath(start, end, ...cps)
// 其中start和end是起点和结束点应当填入[x, y]数组cps是控制点也是[x, y]数组
// 示例bezierPath([0, 0], [200, 200], [100, 50], [300, 150], [200, 180]);
// 这是一个起点为 [0, 0],终点为[200, 200],有三个控制点的四次贝塞尔曲线
// 下面,我们就可以使用路径函数了
// animate.mode(hyper('sin', 'in-out')) // 设置渐变曲线
// .time(5000)
// .relative() // 设置为相对模式,这个比较必要,不然的话很可能出现瞬移
// .moveAs(circle(100, 5, linear())) // 创建一个5圈的半径从0至100逐渐变大的圆轨迹是个螺旋线并让物体沿着它运动
//
// 最后,还有一个震动函数 shake(x, y)x和y表示了在横向上和纵向上的震动幅度1表示为震动幅度的100%
// 示例:
// animate.mode(shake(5, hyper('sin', 'in')), true) // 这里第二个参数说明是震动函数
// .time(2000)
// .shake(1, 0.5)
// 这样,所有内置动画就已经介绍完毕
// 6. 自定义动画属性
// 本插件允许你自定义一个动画属性,但功能可能不会像自带的属性那么强大
// 你可以在创建动画之后使用animate.register(key, init)来注册一个自定义属性
// 其中key是自定义属性的名称init是自定义属性的初始值这个值应当在0-1之间变化
// 你可以通过animate.value[key]来获取你注册的自定义属性
// 对于自定义属性的动画你应当使用animate.apply(key, n, first)
// 其中key是你的自定义属性的名称n是其目标值first是一个布尔值说明了是否将该动画插入到目前所有的动画之前即每帧会优先执行该动画
// 下面是一个不透明度的示例
// animate.register('opacity', 1); // 这句话应该放到刚创建动画之后
// ctx.globalAlpha = animate.value.opacity; // 这句话应当放到每帧绘制的函数里面,放在绘制之前
// animate.mode(bezier(0.9, 0.1, 0.05)) // 设置渐变函数
// .time(2000)
// .absolute()
// .apply('opacity', 0.3); // 将不透明度按照渐变曲线更改为0.3
// 7. 运行动画
// 还记得刚开始定义的async function 吗,直接调用它就能执行动画了!
// 示例ani(); // 执行刚刚写的所有动画
// 8. 自定义速率曲线和路径
// 该插件中,速率曲线和路径均可自定义
// 对于速率曲线,其类型为 (input: number) => number
// 它接受一个范围在 0-1 的值,输出一个 0-1 的值表示了动画的完成度1表示动画已完成0表示动画刚开始当前大于1小于0也不会报错也会执行相应的动画
// 对于路径,其类型为 (input: number) => [number, number]
// 它与速率曲线类似,接收一个 0-1 的值,输出一个坐标数组
// 9. 多个属性绑定
// 该插件中你可以绑定多个动画属性你可以使用ani.bind(...attr)来绑定。
// 绑定之后这三个动画属性可以被一个返回了长度为3的数组的渐变函数执行。
// 绑定使用ani.bind设置渐变函数仍然使用ani.mode注意它与单个动画属性是分开的也就是它不会影响正常的渐变函数。
// 然后使用ani.applyMulti即可执行动画
// 例如:
// // 自定义的一个三属性渐变函数
// function b(input) {
// return [input * 100, input ** 2 * 100, input ** 3 * 100];
// }
// ani.bind('a', 'b', 'c') // 这样会绑定abc这三个动画属性
// .mode(b) // 自定义的一个返回了长度为3的数组的函数
// .time(5000)
// .absolute()
// .applyMulti(); // 执行这个动画
// 9. 监听 动画的生命周期钩子
// 这个插件还允许你去监听动画的状态,可以监听动画的开始、结束、运行
// 你可以使用 animate.listen(type, fn)来监听fn的类型是 (a: Animation, type: string) => void
// 当然,一般情况下你不会用到这个功能,插件中已经帮你包装了三个等待函数,他们就是以这些监听为基础的
// 10. 自定义时间获取函数
// 你可以修改ani.getTime来修改动画的时间获取函数例如想让动画速度变成一半可以写ani.getTime = () => Date.now() / 2
// 这样可以允许你随意控制动画的运行速度,暂停,甚至是倒退。该值默认为`Date.now`
// -------------------- 渐变使用教程 -------------------- //
// 相比于动画,渐变属于一种较为简便的动画,它可以让你在设置一个属性后使属性缓慢变化值目标值而不是突变至目标值
// 现在假设你已经了解了动画的使用,下面我们来了解渐变。
// 1. 创建一个渐变实例
// 与动画类似你需要使用new来实例化一个渐变当然别忘了引入
// const { Transition } = core.plugin.animate;
// const tran = new Transition();
// 2. 绘制
// const ctx = core.createCanvas('transition', 0, 0, 416, 416, 100);
// ctx.save();
// const fn = () => {
// ctx.restore();
// ctx.save();
// ctx.clearRect(0, 0, 800, 800);
// ctx.beginPath();
// ctx.arc(tran.value.x, tran.value.y, 50, 0, Math.PI * 2); // 使用tran.value.xxx获取当前的属性
// ctx.fill();
// // 当然也可以用样板的api例如core.fillCircle();等
// }
// animate.ticker.add(fn);
// 3. 设置渐变
// 同样与动画类似你可以使用tran.time()设置渐变时间使用tran.mode()设置渐变函数使用tran.absolute()和tran.relative()设置相对模式
// 例如:
// tran.time(1000)
// .mode(hyper('sin', 'out'))
// .absolute();
// 4. 初始化渐变属性
// 与动画不同的是动画在执行一个自定义属性前都需要register而渐变不需要。
// 你可以通过tran.value.xxx = yyy来设置动画属性或使用tran.transition('xxx', yyy)来设置
// 你的首次赋值即是初始化了渐变属性,这时是不会执行渐变的,例如:
// tran.value.x = 200;
// tran.transition('y', 200);
// 上述例子便是将 x 和 y 初始化成了200
// 5. 执行渐变
// 初始化完成后,便可以直接执行渐变了,有两种方法
// tran.value.x = 400; // 将 x 缓慢移动至400
// tran.transition('y', 400); // 将 y 缓慢移动至400
// 6. 自定义时间获取函数
// 与动画类似你依然可以通过修改tran.getTime来修改时间获取函数
if (main.replayChecking) return (core.plugin.animate = {});
var M = Object.defineProperty;
var E = (n, i, t) =>
i in n
? M(n, i, { enumerable: !0, configurable: !0, writable: !0, value: t })
: (n[i] = t);
var o = (n, i, t) => (E(n, typeof i != "symbol" ? i + "" : i, t), t);
let w = [];
const k = (n) => {
for (const i of w)
if (i.status === "running")
try {
for (const t of i.funcs) t(n - i.startTime);
} catch (t) {
i.destroy(), console.error(t);
}
requestAnimationFrame(k);
};
requestAnimationFrame(k);
class I {
constructor() {
o(this, "funcs", /* @__PURE__ */ new Set());
o(this, "status", "stop");
o(this, "startTime", 0);
(this.status = "running"),
w.push(this),
requestAnimationFrame((i) => (this.startTime = i));
}
add(i) {
return this.funcs.add(i), this;
}
remove(i) {
return this.funcs.delete(i), this;
}
clear() {
this.funcs.clear();
}
destroy() {
this.clear(), this.stop();
}
stop() {
(this.status = "stop"), (w = w.filter((i) => i !== this));
}
}
class F {
constructor() {
o(this, "timing");
o(this, "relation", "absolute");
o(this, "easeTime", 0);
o(this, "applying", {});
o(this, "getTime", Date.now);
o(this, "ticker", new I());
o(this, "value", {});
o(this, "listener", {});
this.timing = (i) => i;
}
async all() {
if (Object.values(this.applying).every((i) => i === !0))
throw new ReferenceError("There is no animates to be waited.");
await new Promise((i) => {
const t = () => {
Object.values(this.applying).every((e) => e === !1) &&
(this.unlisten("end", t), i("all animated."));
};
this.listen("end", t);
});
}
async n(i) {
const t = Object.values(this.applying).filter((s) => s === !0).length;
if (t < i)
throw new ReferenceError(
`You are trying to wait ${i} animate, but there are only ${t} animate animating.`
);
let e = 0;
await new Promise((s) => {
const r = () => {
e++, e === i && (this.unlisten("end", r), s(`${i} animated.`));
};
this.listen("end", r);
});
}
async w(i) {
if (this.applying[i] === !1)
throw new ReferenceError(`The ${i} animate is not animating.`);
await new Promise((t) => {
const e = () => {
this.applying[i] === !1 &&
(this.unlisten("end", e), t(`${i} animated.`));
};
this.listen("end", e);
});
}
listen(i, t) {
var e, s;
(s = (e = this.listener)[i]) != null || (e[i] = []),
this.listener[i].push(t);
}
unlisten(i, t) {
const e = this.listener[i].findIndex((s) => s === t);
if (e === -1)
throw new ReferenceError(
"You are trying to remove a nonexistent listener."
);
this.listener[i].splice(e, 1);
}
hook(...i) {
const t = Object.entries(this.listener).filter((e) => i.includes(e[0]));
for (const [e, s] of t) for (const r of s) r(this, e);
}
}
function y(n) {
return n != null;
}
async function R(n) {
return new Promise((i) => setTimeout(i, n));
}
class j extends F {
constructor() {
super();
o(this, "shakeTiming");
o(this, "path");
o(this, "multiTiming");
o(this, "value", {});
o(this, "size", 1);
o(this, "angle", 0);
o(this, "targetValue", {
system: {
move: [0, 0],
moveAs: [0, 0],
resize: 0,
rotate: 0,
shake: 0,
"@@bind": [],
},
custom: {},
});
o(this, "animateFn", {
system: {
move: [() => 0, () => 0],
moveAs: () => 0,
resize: () => 0,
rotate: () => 0,
shake: () => 0,
"@@bind": () => 0,
},
custom: {},
});
o(this, "ox", 0);
o(this, "oy", 0);
o(this, "sx", 0);
o(this, "sy", 0);
o(this, "bindInfo", []);
(this.timing = (t) => t),
(this.shakeTiming = (t) => t),
(this.multiTiming = (t) => [t, t]),
(this.path = (t) => [t, t]),
(this.applying = {
move: !1,
scale: !1,
rotate: !1,
shake: !1,
}),
this.ticker.add(() => {
const { running: t } = this.listener;
if (y(t)) for (const e of t) e(this, "running");
});
}
get x() {
return this.ox + this.sx;
}
get y() {
return this.oy + this.sy;
}
mode(t, e = !1) {
return (
typeof t(0) == "number"
? e
? (this.shakeTiming = t)
: (this.timing = t)
: (this.multiTiming = t),
this
);
}
time(t) {
return (this.easeTime = t), this;
}
relative() {
return (this.relation = "relative"), this;
}
absolute() {
return (this.relation = "absolute"), this;
}
bind(...t) {
return (
this.applying["@@bind"] === !0 && this.end(!1, "@@bind"),
(this.bindInfo = t),
this
);
}
unbind() {
return (
this.applying["@@bind"] === !0 && this.end(!1, "@@bind"),
(this.bindInfo = []),
this
);
}
move(t, e) {
return (
this.applying.move && this.end(!0, "move"),
this.applySys("ox", t, "move"),
this.applySys("oy", e, "move"),
this
);
}
rotate(t) {
return this.applySys("angle", t, "rotate"), this;
}
scale(t) {
return this.applySys("size", t, "resize"), this;
}
shake(t, e) {
this.applying.shake === !0 && this.end(!0, "shake"),
(this.applying.shake = !0);
const { easeTime: s, shakeTiming: r } = this,
l = this.getTime();
if ((this.hook("start", "shakestart"), s <= 0))
return this.end(!1, "shake"), this;
const a = () => {
const c = this.getTime() - l;
if (c > s) {
this.ticker.remove(a),
(this.applying.shake = !1),
(this.sx = 0),
(this.sy = 0),
this.hook("end", "shakeend");
return;
}
const h = c / s,
m = r(h);
(this.sx = m * t), (this.sy = m * e);
};
return this.ticker.add(a), (this.animateFn.system.shake = a), this;
}
moveAs(t) {
this.applying.moveAs && this.end(!0, "moveAs"),
(this.applying.moveAs = !0),
(this.path = t);
const { easeTime: e, relation: s, timing: r } = this,
l = this.getTime(),
[a, u] = [this.x, this.y],
[c, h] = (() => {
if (s === "absolute") return t(1);
{
const [d, f] = t(1);
return [a + d, u + f];
}
})();
if ((this.hook("start", "movestart"), e <= 0))
return this.end(!1, "moveAs"), this;
const m = () => {
const f = this.getTime() - l;
if (f > e) {
this.end(!0, "moveAs");
return;
}
const g = f / e,
[v, x] = t(r(g));
s === "absolute"
? ((this.ox = v), (this.oy = x))
: ((this.ox = a + v), (this.oy = u + x));
};
return (
this.ticker.add(m),
(this.animateFn.system.moveAs = m),
(this.targetValue.system.moveAs = [c, h]),
this
);
}
register(t, e) {
if (typeof this.value[t] == "number")
return this.error(
`Property ${t} has been regietered twice.`,
"reregister"
);
(this.value[t] = e), (this.applying[t] = !1);
}
apply(t, e) {
this.applying[t] === !0 && this.end(!1, t),
t in this.value ||
this.error(`You are trying to execute nonexistent property ${t}.`),
(this.applying[t] = !0);
const s = this.value[t],
r = this.getTime(),
{ timing: l, relation: a, easeTime: u } = this,
c = a === "absolute" ? e - s : e;
if ((this.hook("start"), u <= 0)) return this.end(!1, t), this;
const h = () => {
const d = this.getTime() - r;
if (d > u) {
this.end(!1, t);
return;
}
const f = d / u,
g = l(f);
this.value[t] = s + g * c;
};
return (
this.ticker.add(h),
(this.animateFn.custom[t] = h),
(this.targetValue.custom[t] = c + s),
this
);
}
applyMulti() {
this.applying["@@bind"] === !0 && this.end(!1, "@@bind"),
(this.applying["@@bind"] = !0);
const t = this.bindInfo,
e = t.map((h) => this.value[h]),
s = this.getTime(),
{ multiTiming: r, relation: l, easeTime: a } = this,
u = r(1);
if (u.length !== e.length)
throw new TypeError(
`The number of binded animate attributes and timing function returns's length does not match. binded: ${t.length}, timing: ${u.length}`
);
if ((this.hook("start"), a <= 0)) return this.end(!1, "@@bind"), this;
const c = () => {
const m = this.getTime() - s;
if (m > a) {
this.end(!1, "@@bind");
return;
}
const d = m / a,
f = r(d);
t.forEach((g, v) => {
l === "absolute"
? (this.value[g] = f[v])
: (this.value[g] = e[v] + f[v]);
});
};
return (
this.ticker.add(c),
(this.animateFn.custom["@@bind"] = c),
(this.targetValue.system["@@bind"] = u),
this
);
}
applySys(t, e, s) {
s !== "move" && this.applying[s] === !0 && this.end(!0, s),
(this.applying[s] = !0);
const r = this[t],
l = this.getTime(),
a = this.timing,
u = this.relation,
c = this.easeTime,
h = u === "absolute" ? e - r : e;
if ((this.hook("start", `${s}start`), c <= 0)) return this.end(!0, s);
const m = () => {
const f = this.getTime() - l;
if (f > c) {
this.end(!0, s);
return;
}
const g = f / c,
v = a(g);
(this[t] = r + h * v), t !== "oy" && this.hook(s);
};
this.ticker.add(m),
t === "ox"
? (this.animateFn.system.move[0] = m)
: t === "oy"
? (this.animateFn.system.move[1] = m)
: (this.animateFn.system[s] = m),
s === "move"
? (t === "ox" && (this.targetValue.system.move[0] = h + r),
t === "oy" && (this.targetValue.system.move[1] = h + r))
: s !== "shake" && (this.targetValue.system[s] = h + r);
}
error(t, e) {
throw e === "repeat"
? new Error(`Cannot execute the same animation twice. Info: ${t}`)
: e === "reregister"
? new Error(`Cannot register an animated property twice. Info: ${t}`)
: new Error(t);
}
end(t, e) {
if (t === !0)
if (
((this.applying[e] = !1),
e === "move"
? (this.ticker.remove(this.animateFn.system.move[0]),
this.ticker.remove(this.animateFn.system.move[1]))
: e === "moveAs"
? this.ticker.remove(this.animateFn.system.moveAs)
: e === "@@bind"
? this.ticker.remove(this.animateFn.system["@@bind"])
: this.ticker.remove(this.animateFn.system[e]),
e === "move")
) {
const [s, r] = this.targetValue.system.move;
(this.ox = s), (this.oy = r), this.hook("moveend", "end");
} else if (e === "moveAs") {
const [s, r] = this.targetValue.system.moveAs;
(this.ox = s), (this.oy = r), this.hook("moveend", "end");
} else
e === "rotate"
? ((this.angle = this.targetValue.system.rotate),
this.hook("rotateend", "end"))
: e === "resize"
? ((this.size = this.targetValue.system.resize),
this.hook("resizeend", "end"))
: e === "@@bind"
? this.bindInfo.forEach((r, l) => {
this.value[r] = this.targetValue.system["@@bind"][l];
})
: ((this.sx = 0), (this.sy = 0), this.hook("shakeend", "end"));
else
(this.applying[e] = !1),
this.ticker.remove(this.animateFn.custom[e]),
(this.value[e] = this.targetValue.custom[e]),
this.hook("end");
}
}
class O extends F {
constructor() {
super();
o(this, "now", {});
o(this, "target", {});
o(this, "transitionFn", {});
o(this, "value");
o(this, "handleSet", (t, e, s) => (this.transition(e, s), !0));
o(this, "handleGet", (t, e) => this.now[e]);
(this.timing = (t) => t),
(this.value = new Proxy(this.target, {
set: this.handleSet,
get: this.handleGet,
}));
}
mode(t) {
return (this.timing = t), this;
}
time(t) {
return (this.easeTime = t), this;
}
relative() {
return (this.relation = "relative"), this;
}
absolute() {
return (this.relation = "absolute"), this;
}
transition(t, e) {
if (e === this.target[t]) return this;
if (!y(this.now[t])) return (this.now[t] = e), this;
this.applying[t] && this.end(t, !0),
(this.applying[t] = !0),
this.hook("start");
const s = this.getTime(),
r = this.easeTime,
l = this.timing,
a = this.now[t],
u = e + (this.relation === "absolute" ? 0 : a),
c = u - a;
this.target[t] = u;
const h = () => {
const d = this.getTime() - s;
if (d >= r) {
this.end(t);
return;
}
const f = d / r;
(this.now[t] = l(f) * c + a), this.hook("running");
};
return (
(this.transitionFn[t] = h),
this.ticker.add(h),
r <= 0 ? (this.end(t), this) : this
);
}
end(t, e = !1) {
const s = this.transitionFn[t];
if (!y(s))
throw new ReferenceError(
`You are trying to end an ended transition: ${t}`
);
this.ticker.remove(this.transitionFn[t]),
delete this.transitionFn[t],
(this.applying[t] = !1),
this.hook("end"),
e || (this.now[t] = this.target[t]);
}
}
const T = (...n) => n.reduce((i, t) => i + t, 0),
b = (n) => {
if (n === 0) return 1;
let i = n;
for (; n > 1; ) n--, (i *= n);
return i;
},
A = (n, i) => Math.round(b(i) / (b(n) * b(i - n))),
p = (n, i, t = (e) => 1 - i(1 - e)) =>
n === "in"
? i
: n === "out"
? t
: n === "in-out"
? (e) => (e < 0.5 ? i(e * 2) / 2 : 0.5 + t((e - 0.5) * 2) / 2)
: (e) => (e < 0.5 ? t(e * 2) / 2 : 0.5 + i((e - 0.5) * 2) / 2),
$ = Math.cosh(2),
z = Math.acosh(2),
V = Math.tanh(3),
P = Math.atan(5);
function Y() {
return (n) => n;
}
function q(...n) {
const i = [0].concat(n);
i.push(1);
const t = i.length,
e = Array(t)
.fill(0)
.map((s, r) => A(r, t - 1));
return (s) => {
const r = e.map((l, a) => l * i[a] * (1 - s) ** (t - a - 1) * s ** a);
return T(...r);
};
}
function U(n, i) {
if (n === "sin") {
const t = (s) => Math.sin((s * Math.PI) / 2);
return p(i, (s) => 1 - t(1 - s), t);
}
if (n === "sec") {
const t = (s) => 1 / Math.cos(s);
return p(i, (s) => t((s * Math.PI) / 3) - 1);
}
throw new TypeError(
"Unexpected parameters are delivered in trigo timing function."
);
}
function C(n, i) {
if (!Number.isInteger(n))
throw new TypeError(
"The first parameter of power timing function only allow integer."
);
return p(i, (e) => e ** n);
}
function G(n, i) {
if (n === "sin") return p(i, (e) => (Math.cosh(e * 2) - 1) / ($ - 1));
if (n === "tan") {
const t = (s) => (Math.tanh(s * 3) * 1) / V;
return p(i, (s) => 1 - t(1 - s), t);
}
if (n === "sec") {
const t = (s) => 1 / Math.cosh(s);
return p(i, (s) => 1 - (t(s * z) - 0.5) * 2);
}
throw new TypeError(
"Unexpected parameters are delivered in hyper timing function."
);
}
function N(n, i) {
if (n === "sin") {
const t = (s) => (Math.asin(s) / Math.PI) * 2;
return p(i, (s) => 1 - t(1 - s), t);
}
if (n === "tan") {
const t = (s) => Math.atan(s * 5) / P;
return p(i, (s) => 1 - t(1 - s), t);
}
throw new TypeError(
"Unexpected parameters are delivered in inverse trigo timing function."
);
}
function B(n, i = () => 1) {
let t = -1;
return (e) => (
(t *= -1), e < 0.5 ? n * i(e * 2) * t : n * i((1 - e) * 2) * t
);
}
function D(n, i = 1, t = [0, 0], e = 0, s = (l) => 1, r = !1) {
return (l) => {
const a = i * l * Math.PI * 2 + (e * Math.PI) / 180,
u = Math.cos(a),
c = Math.sin(a),
h = n * s(s(r ? 1 - l : l));
return [h * u + t[0], h * c + t[1]];
};
}
function H(n, i, ...t) {
const e = [n].concat(t);
e.push(i);
const s = e.length,
r = Array(s)
.fill(0)
.map((l, a) => A(a, s - 1));
return (l) => {
const a = r.map(
(c, h) => c * e[h][0] * (1 - l) ** (s - h - 1) * l ** h
),
u = r.map((c, h) => c * e[h][1] * (1 - l) ** (s - h - 1) * l ** h);
return [T(...a), T(...u)];
};
}
if ("animate" in core.plugin)
throw new ReferenceError(`插件中已存在名为animate的属性`);
core.plugin.animate = {
Animation: j,
AnimationBase: F,
Ticker: I,
Transition: O,
bezier: q,
bezierPath: H,
circle: D,
hyper: G,
linear: Y,
power: C,
shake: B,
sleep: R,
trigo: U,
inverseTrigo: N,
};
},
"func": function () {
// 功能函数集,具体有哪些函数看每个函数前的注释即可
// 安装方式:直接复制到插件里面,注意新建插件自带的 function () { } 不能删
// 使用方式:可以直接使用对象解构按需引入
// 例如const { has, slide } = core.plugin.utils;
// slide([1, 2, 3], -1); // [2, 3, 1]
/**
* 滑动数组,使数组元素平移若干项
* @example slide([1, 2, 3], -1); // [2, 3, 1]
* @example slide([1, 3, 5], 10); // [5, 3, 1];
* @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;
}
}
/**
* 图片叠加滤镜
* @param {img} image 需要叠加的图片,
* @param {color} style 需要叠加的颜色
*/
function imagelighter(image, style) {
// 创建一个canvas元素
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// 设置canvas的尺寸与图片相同
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
// 创建一个临时canvas用于红色滤镜
const tempCanvas = document.createElement("canvas");
const tempCtx = tempCanvas.getContext("2d");
tempCanvas.width = image.width;
tempCanvas.height = image.height;
// 在临时canvas上绘制红色滤镜
tempCtx.fillStyle = style ?? "rgba(255, 0, 0, 0.5)"; // 半透明红色
tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
// 使用lighter混合模式叠加红色滤镜
ctx.globalCompositeOperation = "lighter";
ctx.drawImage(tempCanvas, 0, 0);
// 使用destination-in混合模式保留原始图片的透明度
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(image, 0, 0);
// 恢复默认混合模式
ctx.globalCompositeOperation = "source-over";
// 返回处理后的canvas
return canvas;
}
/**
* 获取一个方向的反方向
* @example backDir('up'); // 'down'
* @example backDir('leftup'); // 'rightdown'
* @param {string} dir 方向
*/
function backDir(dir) {
const map = {
up: "down",
down: "up",
left: "right",
right: "left",
leftup: "rightdown",
leftdown: "rightup",
rightdown: "leftup",
rightup: "leftdown",
};
if (!dir in map) {
throw new TypeError(
`Wrong dir is delivered when getting back direction.`
);
}
return map[dir];
}
/**
* 判断一个值是否不是undefined和null
* @example has(0); // true
* @example has(false); // true
* @example has(NaN); // true
* @example has(null); // false
* @param {any} v 要判断的值
*/
function has(v) {
return v !== null && v !== void 0;
}
/**
* 解析css字符串为CSSStyleDeclaration对象
* @example
* parseCss('background-color: cyan; cursor: pointer; user-select: none');
* // 输出 { backgroundColor: 'cyan', cursor: 'pointer', userSelect: 'none' }
* @param {string} css 要解析的css字符串
*/
function parseCss(css) {
const str = css.replace(/[\n\s\t]*/g, "").replace(/;*/g, ";");
const styles = str.split(";");
const res = {};
for (const one of styles) {
const [key, data] = one.split(":");
const cssKey = key.replace(/\-([a-z])/g, (str, $1) => $1.toUpperCase());
res[cssKey] = data;
}
return res;
}
/**
* 等待一段时间需在async function中使用否则报错
* @example await sleep(500); // 等待500毫秒
* @param {number} time 等待的毫秒数
*/
async function sleep(time) {
return new Promise((res) => setTimeout(res, time));
}
/**
* 在下一帧的下一帧执行一个函数
* @example nextFrame(() => console.log(1)); // 两帧后在控制台输出1
* @param cb 执行的函数
*/
function nextFrame(cb) {
requestAnimationFrame(() => {
requestAnimationFrame(cb);
});
}
/**
* 将一个css颜色解析成一个rgba数组
* 目前仅支持 #RGB #RGBA #RRGGBB #RRGGBBAA rgb() rgba() hsl() hsla() css自带颜色 这几种的转换
* @exmaple parseColor('#fff'); // [255, 255, 255]
* @example parseColor('#abcd'); // [170, 187, 204, 0.8666666666666667]
* @example parseColor('rgba(170, 230, 13, 0.2)'); // [170, 230, 13, 0.2]
* @example parseColor('cyan'); // [0, 255, 255]
* @example parseColor('lightcoral'); // [240, 128, 128]
* @example parseColor('hsla(0.2, 0.3, 0.4, 0.2)'); // [120, 133, 71, 0.2]
* @example parseColor('rgba(20%, 50, 33%, 0.2)'); // [51, 50, 84.15, 0.2]
* @param color 要解析的颜色字符串
*/
function parseColor(color) {
if (color.startsWith("rgb")) {
// rgb
const match = color.match(/rgba?\([\d\,\s\.%]+\)/);
if (!has(match)) throw new Error(`Invalid color is delivered!`);
const l = color.includes("a");
return match[0]
.slice(l ? 5 : 4, -1)
.split(",")
.map((v, i) => {
const vv = v.trim();
if (vv.endsWith("%")) {
if (i === 3) {
return parseInt(vv) / 100;
} else {
return (parseInt(vv) * 255) / 100;
}
} else return parseFloat(vv);
})
.slice(0, l ? 4 : 3);
} else if (color.startsWith("#")) {
// 十六进制
const content = color.slice(1);
if (![3, 4, 6, 8].includes(content.length)) {
throw new Error(`Invalid color is delivered!`);
}
if (content.length <= 4) {
const res = content.split("").map((v) => Number(`0x${v}${v}`));
if (res.length === 4) res[3] /= 255;
return res;
} else {
const res = Array(content.length / 2)
.fill(1)
.map((v, i) => Number(`0x${content[i * 2]}${content[i * 2 + 1]}`));
if (res.length === 4) res[3] /= 255;
return res;
}
} else if (color.startsWith("hsl")) {
// hsl转成rgb后输出
const match = color.match(/hsla?\([\d\,\s\.%]+\)/);
if (!has(match)) throw new Error(`Invalid color is delivered!`);
const l = color.includes("a");
const hsl = match[0]
.slice(l ? 5 : 4, -1)
.split(",")
.map((v) => {
const vv = v.trim();
if (vv.endsWith("%")) return parseInt(vv) / 100;
else return parseFloat(vv);
});
const rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
return l ? rgb.concat([hsl[3]]) : rgb;
} else {
// 单词
const rgb = cssColors[color];
if (!has(rgb)) {
throw new Error(`Invalid color is delivered!`);
}
return parseColor(rgb);
}
}
/**
* hsl转rgb
* @param h 色相
* @param s 饱和度
* @param l 亮度
*/
function hslToRgb(h, s, l) {
if (s == 0) {
return [0, 0, 0];
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
const r = hue2rgb(p, q, h + 1 / 3);
const g = hue2rgb(p, q, h);
const b = hue2rgb(p, q, h - 1 / 3);
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
}
/**
* 确保一个变量是一个数组,不是的话转为数组并返回,是的话直接返回传入的数组
* @param arr 要判断的变量
* @example ensureArray(1); // [1]
* @example ensureArray([1, 2]); // [1, 2]
* @example ensureArray('test'); // ['test']
*/
function ensureArray(arr) {
// @ts-ignore
return arr instanceof Array ? arr : [arr];
}
/**
* 返回一个坐标在某个方向上移动 d 格后的坐标
* @param d 移动多少格默认为1
* @example ofDir(7, 7, 'left'); // [6, 7]
* @example ofDir(10, 8, 'leftup', 5); // [5, 3]
*/
function ofDir(x, y, dir, d = 1) {
const { x: dx, y: dy } = core.utils.scan2[dir];
return [x + dx * d, y + dy * d];
}
const cssColors = {
black: "#000000",
silver: "#c0c0c0",
gray: "#808080",
white: "#ffffff",
maroon: "#800000",
red: "#ff0000",
purple: "#800080",
fuchsia: "#ff00ff",
green: "#008000",
lime: "#00ff00",
olive: "#808000",
yellow: "#ffff00",
navy: "#000080",
blue: "#0000ff",
teal: "#008080",
aqua: "#00ffff",
orange: "#ffa500",
aliceblue: "#f0f8ff",
antiquewhite: "#faebd7",
aquamarine: "#7fffd4",
azure: "#f0ffff",
beige: "#f5f5dc",
bisque: "#ffe4c4",
blanchedalmond: "#ffebcd",
blueviolet: "#8a2be2",
brown: "#a52a2a",
burlywood: "#deb887",
cadetblue: "#5f9ea0",
chartreuse: "#7fff00",
chocolate: "#d2691e",
coral: "#ff7f50",
cornflowerblue: "#6495ed",
cornsilk: "#fff8dc",
crimson: "#dc143c",
cyan: "#00ffff",
darkblue: "#00008b",
darkcyan: "#008b8b",
darkgoldenrod: "#b8860b",
darkgray: "#a9a9a9",
darkgreen: "#006400",
darkgrey: "#a9a9a9",
darkkhaki: "#bdb76b",
darkmagenta: "#8b008b",
darkolivegreen: "#556b2f",
darkorange: "#ff8c00",
darkorchid: "#9932cc",
darkred: "#8b0000",
darksalmon: "#e9967a",
darkseagreen: "#8fbc8f",
darkslateblue: "#483d8b",
darkslategray: "#2f4f4f",
darkslategrey: "#2f4f4f",
darkturquoise: "#00ced1",
darkviolet: "#9400d3",
deeppink: "#ff1493",
deepskyblue: "#00bfff",
dimgray: "#696969",
dimgrey: "#696969",
dodgerblue: "#1e90ff",
firebrick: "#b22222",
floralwhite: "#fffaf0",
forestgreen: "#228b22",
gainsboro: "#dcdcdc",
ghostwhite: "#f8f8ff",
gold: "#ffd700",
goldenrod: "#daa520",
greenyellow: "#adff2f",
grey: "#808080",
honeydew: "#f0fff0",
hotpink: "#ff69b4",
indianred: "#cd5c5c",
indigo: "#4b0082",
ivory: "#fffff0",
khaki: "#f0e68c",
lavender: "#e6e6fa",
lavenderblush: "#fff0f5",
lawngreen: "#7cfc00",
lemonchiffon: "#fffacd",
lightblue: "#add8e6",
lightcoral: "#f08080",
lightcyan: "#e0ffff",
lightgoldenrodyellow: "#fafad2",
lightgray: "#d3d3d3",
lightgreen: "#90ee90",
lightgrey: "#d3d3d3",
lightpink: "#ffb6c1",
lightsalmon: "#ffa07a",
lightseagreen: "#20b2aa",
lightskyblue: "#87cefa",
lightslategray: "#778899",
lightslategrey: "#778899",
lightsteelblue: "#b0c4de",
lightyellow: "#ffffe0",
limegreen: "#32cd32",
linen: "#faf0e6",
magenta: "#ff00ff",
mediumaquamarine: "#66cdaa",
mediumblue: "#0000cd",
mediumorchid: "#ba55d3",
mediumpurple: "#9370db",
mediumseagreen: "#3cb371",
mediumslateblue: "#7b68ee",
mediumspringgreen: "#00fa9a",
mediumturquoise: "#48d1cc",
mediumvioletred: "#c71585",
midnightblue: "#191970",
mintcream: "#f5fffa",
mistyrose: "#ffe4e1",
moccasin: "#ffe4b5",
navajowhite: "#ffdead",
oldlace: "#fdf5e6",
olivedrab: "#6b8e23",
orangered: "#ff4500",
orchid: "#da70d6",
palegoldenrod: "#eee8aa",
palegreen: "#98fb98",
paleturquoise: "#afeeee",
palevioletred: "#db7093",
papayawhip: "#ffefd5",
peachpuff: "#ffdab9",
peru: "#cd853f",
pink: "#ffc0cb",
plum: "#dda0dd",
powderblue: "#b0e0e6",
rosybrown: "#bc8f8f",
royalblue: "#4169e1",
saddlebrown: "#8b4513",
salmon: "#fa8072",
sandybrown: "#f4a460",
seagreen: "#2e8b57",
seashell: "#fff5ee",
sienna: "#a0522d",
skyblue: "#87ceeb",
slateblue: "#6a5acd",
slategray: "#708090",
slategrey: "#708090",
snow: "#fffafa",
springgreen: "#00ff7f",
steelblue: "#4682b4",
tan: "#d2b48c",
thistle: "#d8bfd8",
tomato: "#ff6347",
turquoise: "#40e0d0",
violet: "#ee82ee",
wheat: "#f5deb3",
whitesmoke: "#f5f5f5",
yellowgreen: "#9acd32",
transparent: "#0000",
};
// 计算两个数的最大公约数
function gcdOfTwo(a, b) {
while (b !== 0) {
let temp = b;
b = a % b;
a = temp;
}
return a;
}
// 计算任意项整数的最大公约数
function gcd(...numbers) {
if (numbers.length < 2) {
throw new Error("至少需要两个数");
}
return numbers.reduce((a, b) => gcdOfTwo(a, b));
}
// 计算两个数的最小公倍数
function lcmOfTwo(a, b) {
return (a * b) / gcdOfTwo(a, b);
}
// 计算任意项整数的最小公倍数
function lcm(...numbers) {
if (numbers.length < 2) {
throw new Error("至少需要两个数");
}
return numbers.reduce((a, b) => lcmOfTwo(a, b));
}
if (has(core.plugin.utils)) {
throw new ReferenceError(
`core.plugin上已经有'utils'属性,因此功能函数插件将无法使用!`
);
}
core.plugin.utils = {
imagelighter,
gcdOfTwo,
lcmOfTwo,
gcd,
lcm,
has,
slide,
backDir,
parseCss,
sleep,
nextFrame,
parseColor,
hslToRgb,
ensureArray,
ofDir,
};
// Utility.js
// 通用函數插件
// 本插件與古祠發佈的《功能插件 --- 实用功能函数集》不同,函數是定義在全域的。
// 自訂常見事件模板插件(editorBlocklyconfigPlus.js)的前置插件
/**
* 使js暫停指定時間
* async環境下await Sleep(500)
* @param {number} millisecond 暫停毫秒數
*/
self.Sleep = async function (millisecond) {
return new Promise((resolve) => setTimeout(resolve, millisecond));
};
/**
* 使js暫停一幀
* async環境下await SleepFrame()
*/
self.SleepFrame = async function () {
return new Promise((resolve) => requestAnimationFrame(resolve));
};
/**
* editor_file的isset函數
*/
self.isset = function (val) {
if (val == undefined || val == null) {
return false;
}
return true;
};
/**
* editor_file的checkCallback函數
*/
self.checkCallback = function (callback) {
if (!isset(callback)) {
printe("未设置callback");
throw "未设置callback";
}
};
},
"音频系统": function () {
// 在此增加新插件
/*首先,在造塔群下载所需的库文件,然后放置在塔目录下的 libs/thirdparty 或其他目录下,之后在 index.html 的最后加上下面这几行:
<script src="libs/thirdparty/ogg-vorbis-decoder.min.js"></script>
<script src="libs/thirdparty/ogg-opus-decoder.min.js"></script>
<script src="libs/thirdparty/codec-parser.min.js"></script>
*/
// 将__enable置为false将关闭插件
let __enable = true;
if (!__enable || main.mode === "editor") return;
const { OggOpusDecoderWebWorker } = window["ogg-opus-decoder"];
const { OggVorbisDecoderWebWorker } = window["ogg-vorbis-decoder"];
const { CodecParser } = window.CodecParser;
const { Transition, linear } = core.plugin.animate;
const audio = new Audio();
const AudioStatus = {
Playing: 0,
Pausing: 1,
Paused: 2,
Stoping: 3,
Stoped: 4,
};
const supportMap = new Map();
const AudioType = {
Mp3: "audio/mpeg",
Wav: 'audio/wav; codecs="1"',
Flac: "audio/flac",
Opus: 'audio/ogg; codecs="opus"',
Ogg: 'audio/ogg; codecs="vorbis"',
Aac: "audio/aac",
};
/**
* 检查一种音频类型是否能被播放
* @param type 音频类型 AudioType
*/
function isAudioSupport(type) {
if (supportMap.has(type)) return supportMap.get(type);
else {
const support = audio.canPlayType(type);
const canPlay = support === "maybe" || support === "probably";
supportMap.set(type, canPlay);
return canPlay;
}
}
const typeMap = new Map([
["ogg", AudioType.Ogg],
["mp3", AudioType.Mp3],
["wav", AudioType.Wav],
["flac", AudioType.Flac],
["opus", AudioType.Opus],
["aac", AudioType.Aac],
]);
/**
* 根据文件名拓展猜测其类型
* @param file 文件名 string
*/
function guessTypeByExt(file) {
const ext = /\.[a-zA-Z\d]+$/.exec(file);
if (!ext?.[0]) return "";
const type = ext[0].slice(1);
return typeMap.get(type.toLocaleLowerCase()) ?? "";
}
isAudioSupport(AudioType.Ogg);
isAudioSupport(AudioType.Mp3);
isAudioSupport(AudioType.Wav);
isAudioSupport(AudioType.Flac);
isAudioSupport(AudioType.Opus);
isAudioSupport(AudioType.Aac);
function isNil(value) {
return value === void 0 || value === null;
}
function sleep(time) {
return new Promise((res) => setTimeout(res, time));
}
class AudioEffect {
constructor(ac) {}
/**
* 连接至其他效果器
* @param target 目标输入 IAudioInput
* @param output 当前效果器输出通道 Number
* @param input 目标效果器的输入通道 Number
*/
connect(target, output, input) {
this.output.connect(target.input, output, input);
}
/**
* 与其他效果器取消连接
* @param target 目标输入 IAudioInput
* @param output 当前效果器输出通道 Number
* @param input 目标效果器的输入通道 Number
*/
disconnect(target, output, input) {
if (!target) {
if (!isNil(output)) {
this.output.disconnect(output);
} else {
this.output.disconnect();
}
} else {
if (!isNil(output)) {
if (!isNil(input)) {
this.output.disconnect(target.input, output, input);
} else {
this.output.disconnect(target.input, output);
}
} else {
this.output.disconnect(target.input);
}
}
}
}
class StereoEffect extends AudioEffect {
constructor(ac) {
super(ac);
const panner = ac.createPanner();
this.input = panner;
this.output = panner;
}
/**
* 设置音频朝向x正方形水平向右y正方形垂直于地面向上z正方向垂直屏幕远离用户
* @param x 朝向x坐标 Number
* @param y 朝向y坐标 Number
* @param z 朝向z坐标 Number
*/
setOrientation(x, y, z) {
this.output.orientationX.value = x;
this.output.orientationY.value = y;
this.output.orientationZ.value = z;
}
/**
* 设置音频位置x正方形水平向右y正方形垂直于地面向上z正方向垂直屏幕远离用户
* @param x 位置x坐标 Number
* @param y 位置y坐标 Number
* @param z 位置z坐标 Number
*/
setPosition(x, y, z) {
this.output.positionX.value = x;
this.output.positionY.value = y;
this.output.positionZ.value = z;
}
end() {}
start() {}
}
class VolumeEffect extends AudioEffect {
constructor(ac) {
super(ac);
const gain = ac.createGain();
this.input = gain;
this.output = gain;
}
/**
* 设置音量大小
* @param volume 音量大小 Number
*/
setVolume(volume) {
this.output.gain.value = volume;
}
/**
* 获取音量大小 Number
*/
getVolume() {
return this.output.gain.value;
}
end() {}
start() {}
}
class ChannelVolumeEffect extends AudioEffect {
/** 所有的音量控制节点 */
constructor(ac) {
super(ac);
/** 所有的音量控制节点 */
this.gain = [];
const splitter = ac.createChannelSplitter();
const merger = ac.createChannelMerger();
this.output = merger;
this.input = splitter;
for (let i = 0; i < 6; i++) {
const gain = ac.createGain();
splitter.connect(gain, i);
gain.connect(merger, 0, i);
this.gain.push(gain);
}
}
/**
* 设置某个声道的音量大小
* @param channel 要设置的声道可填0-5 Number
* @param volume 这个声道的音量大小 Number
*/
setVolume(channel, volume) {
if (!this.gain[channel]) return;
this.gain[channel].gain.value = volume;
}
/**
* 获取某个声道的音量大小可填0-5
* @param channel 要获取的声道 Number
*/
getVolume(channel) {
if (!this.gain[channel]) return 0;
return this.gain[channel].gain.value;
}
end() {}
start() {}
}
class DelayEffect extends AudioEffect {
constructor(ac) {
super(ac);
const delay = ac.createDelay();
this.input = delay;
this.output = delay;
}
/**
* 设置延迟时长
* @param delay 延迟时长,单位秒 Number
*/
setDelay(delay) {
this.output.delayTime.value = delay;
}
/**
* 获取延迟时长
*/
getDelay() {
return this.output.delayTime.value;
}
end() {}
start() {}
}
class EchoEffect extends AudioEffect {
constructor(ac) {
super(ac);
/** 当前增益 */
this.gain = 0.5;
/** 是否正在播放 */
this.playing = false;
const delay = ac.createDelay();
const gain = ac.createGain();
gain.gain.value = 0.5;
delay.delayTime.value = 0.05;
delay.connect(gain);
gain.connect(delay);
/** 延迟节点 */
this.delay = delay;
/** 反馈增益节点 */
this.gainNode = gain;
this.input = gain;
this.output = gain;
}
/**
* 设置回声反馈增益大小
* @param gain 增益大小,范围 0-1大于等于1的视为0.5小于0的视为0 Number
*/
setFeedbackGain(gain) {
const resolved = gain >= 1 ? 0.5 : gain < 0 ? 0 : gain;
this.gain = resolved;
if (this.playing) this.gainNode.gain.value = resolved;
}
/**
* 设置回声间隔时长
* @param delay 回声时长,范围 0.01-Infinity小于0.01的视为0.01 Number
*/
setEchoDelay(delay) {
const resolved = delay < 0.01 ? 0.01 : delay;
this.delay.delayTime.value = resolved;
}
/**
* 获取反馈节点增益
*/
getFeedbackGain() {
return this.gain;
}
/**
* 获取回声间隔时长
*/
getEchoDelay() {
return this.delay.delayTime.value;
}
end() {
this.playing = false;
const echoTime = Math.ceil(Math.log(0.001) / Math.log(this.gain)) + 10;
sleep(this.delay.delayTime.value * echoTime).then(() => {
if (!this.playing) this.gainNode.gain.value = 0;
});
}
start() {
this.playing = true;
this.gainNode.gain.value = this.gain;
}
}
class StreamLoader {
constructor(url) {
/** 传输目标 Set<IStreamReader>*/
this.target = new Set();
this.loading = false;
}
/**
* 将加载流传递给字节流读取对象
* @param reader 字节流读取对象 IStreamReader
*/
pipe(reader) {
if (this.loading) {
console.warn(
"Cannot pipe new StreamReader object when stream is loading."
);
return;
}
this.target.add(reader);
reader.piped(this);
return this;
}
async start() {
if (this.loading) return;
this.loading = true;
const response = await window.fetch(this.url);
const stream = response.body;
if (!stream) {
console.error("Cannot get reader when fetching '" + this.url + "'.");
return;
}
// 获取读取器
this.stream = stream;
const reader = response.body?.getReader();
const targets = [...this.target];
await Promise.all(targets.map((v) => v.start(stream, this, response)));
if (reader && reader.read) {
// 开始流传输
while (true) {
const { value, done } = await reader.read();
await Promise.all(
targets.map((v) => v.pump(value, done, response))
);
if (done) break;
}
} else {
// 如果不支持流传输
const buffer = await response.arrayBuffer();
const data = new Uint8Array(buffer);
await Promise.all(targets.map((v) => v.pump(data, true, response)));
}
this.loading = false;
targets.forEach((v) => v.end(true));
//
}
cancel(reason) {
if (!this.stream) return;
this.stream.cancel(reason);
this.loading = false;
this.target.forEach((v) => v.end(false, reason));
}
}
const fileSignatures = [
[AudioType.Mp3, [0x49, 0x44, 0x33]],
[AudioType.Ogg, [0x4f, 0x67, 0x67, 0x53]],
[AudioType.Wav, [0x52, 0x49, 0x46, 0x46]],
[AudioType.Flac, [0x66, 0x4c, 0x61, 0x43]],
[AudioType.Aac, [0xff, 0xf1]],
[AudioType.Aac, [0xff, 0xf9]],
];
const oggHeaders = [
[AudioType.Opus, [0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64]],
];
function checkAudioType(data) {
let audioType = "";
// 检查头文件获取音频类型仅检查前256个字节
const toCheck = data.slice(0, 256);
for (const [type, value] of fileSignatures) {
if (value.every((v, i) => toCheck[i] === v)) {
audioType = type;
break;
}
}
if (audioType === AudioType.Ogg) {
// 如果是ogg的话进一步判断是不是opus
for (const [key, value] of oggHeaders) {
const has = toCheck.some((_, i) => {
return value.every((v, ii) => toCheck[i + ii] === v);
});
if (has) {
audioType = key;
break;
}
}
}
return audioType;
}
class AudioDecoder {
/**
* 注册一个解码器
* @param type 要注册的解码器允许解码的类型
* @param decoder 解码器对象
*/
static registerDecoder(type, decoder) {
if (!this.decoderMap) this.decoderMap = new Map();
if (this.decoderMap.has(type)) {
console.warn(
"Audio stream decoder for audio type '" +
type +
"' has already existed."
);
return;
}
this.decoderMap.set(type, decoder);
}
/**
* 解码音频数据
* @param data 音频文件数据
* @param player AudioPlayer实例
*/
static async decodeAudioData(data, player) {
// 检查头文件获取音频类型仅检查前256个字节
const toCheck = data.slice(0, 256);
const type = checkAudioType(data);
if (type === "") {
console.error(
"Unknown audio type. Header: '" +
[...toCheck]
.map((v) => v.toString().padStart(2, "0"))
.join(" ")
.toUpperCase() +
"'"
);
return null;
}
if (isAudioSupport(type)) {
if (data.buffer instanceof ArrayBuffer) {
return player.ac.decodeAudioData(data.buffer);
} else {
return null;
}
} else {
const Decoder = this.decoderMap.get(type);
if (!Decoder) {
return null;
} else {
const decoder = new Decoder();
await decoder.create();
const decodedData = await decoder.decode(data);
if (!decodedData) return null;
const buffer = player.ac.createBuffer(
decodedData.channelData.length,
decodedData.channelData[0].length,
decodedData.sampleRate
);
decodedData.channelData.forEach((v, i) => {
buffer.copyToChannel(v, i);
});
decoder.destroy();
return buffer;
}
}
}
}
class VorbisDecoder {
/**
* 创建音频解码器
*/
async create() {
this.decoder = new OggVorbisDecoderWebWorker();
await this.decoder.ready;
}
/**
* 摧毁这个解码器
*/
destroy() {
this.decoder?.free();
}
/**
* 解码流数据
* @param data 流数据
*/
async decode(data) {
return this.decoder?.decode(data);
}
/**
* 解码整个文件
* @param data 文件数据
*/
async decodeAll(data) {
return this.decoder?.decodeFile(data);
}
/**
* 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用
*/
async flush() {
return this.decoder?.flush();
}
}
class OpusDecoder {
/**
* 创建音频解码器
*/
async create() {
this.decoder = new OggOpusDecoderWebWorker();
await this.decoder.ready;
}
/**
* 摧毁这个解码器
*/
destroy() {
this.decoder?.free();
}
/**
* 解码流数据
* @param data 流数据
*/
async decode(data) {
return this.decoder?.decode(data);
}
/**
* 解码整个文件
* @param data 文件数据
*/
async decodeAll(data) {
return this.decoder?.decodeFile(data);
}
/**
* 当音频解码完成后,会调用此函数,需要返回之前还未解析或未返回的音频数据。调用后,该解码器将不会被再次使用
*/
async flush() {
return await this.decoder?.flush();
}
}
const mimeTypeMap = {
[AudioType.Aac]: "audio/aac",
[AudioType.Flac]: "audio/flac",
[AudioType.Mp3]: "audio/mpeg",
[AudioType.Ogg]: "application/ogg",
[AudioType.Opus]: "application/ogg",
[AudioType.Wav]: "application/ogg",
};
function isOggPage(data) {
return !isNil(data.isFirstPage);
}
class AudioStreamSource {
constructor(context) {
this.output = context.createBufferSource();
/** 是否已经完全加载完毕 */
this.loaded = false;
/** 是否正在播放 */
this.playing = false;
/** 已经缓冲了多长时间,如果缓冲完那么跟歌曲时长一致 */
this.buffered = 0;
/** 已经缓冲的采样点数量 */
this.bufferedSamples = 0;
/** 歌曲时长,加载完毕之前保持为 0 */
this.duration = 0;
/** 在流传输阶段,至少缓冲多长时间的音频之后才开始播放,单位秒 */
this.bufferPlayDuration = 1;
/** 音频的采样率,未成功解析出之前保持为 0 */
this.sampleRate = 0;
//是否循环播放
this.loop = false;
/** 上一次播放是从何时开始的 */
this.lastStartWhen = 0;
/** 开始播放时刻 */
this.lastStartTime = 0;
/** 上一次播放的缓存长度 */
this.lastBufferSamples = 0;
/** 是否已经获取到头文件 */
this.headerRecieved = false;
/** 音频类型 */
this.audioType = "";
/** 每多长时间组成一个缓存 Float32Array */
this.bufferChunkSize = 10;
/** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array用于流式解码 */
this.audioData = [];
this.errored = false;
this.ac = context;
}
/** 当前已经播放了多长时间 */
get currentTime() {
return this.ac.currentTime - this.lastStartTime + this.lastStartWhen;
}
/**
* 设置每个缓存数据的大小默认为10秒钟一个缓存数据
* @param size 每个缓存数据的时长,单位秒
*/
setChunkSize(size) {
if (this.controller?.loading || this.loaded) return;
this.bufferChunkSize = size;
}
piped(controller) {
this.controller = controller;
}
async pump(data, done) {
if (!data || this.errored) return;
if (!this.headerRecieved) {
// 检查头文件获取音频类型仅检查前256个字节
const toCheck = data.slice(0, 256);
this.audioType = checkAudioType(data);
if (!this.audioType) {
console.error(
"Unknown audio type. Header: '" +
[...toCheck]
.map((v) => v.toString(16).padStart(2, "0"))
.join(" ")
.toUpperCase() +
"'"
);
return;
}
// 创建解码器
const Decoder = AudioDecoder.decoderMap.get(this.audioType);
if (!Decoder) {
this.errored = true;
console.error(
"Cannot decode stream source type of '" +
this.audioType +
"', since there is no registered decoder for that type."
);
return Promise.reject(
`Cannot decode stream source type of '${this.audioType}', since there is no registered decoder for that type.`
);
}
this.decoder = new Decoder();
// 创建数据解析器
const mime = mimeTypeMap[this.audioType];
const parser = new CodecParser(mime);
this.parser = parser;
await this.decoder.create();
this.headerRecieved = true;
}
const decoder = this.decoder;
const parser = this.parser;
if (!decoder || !parser) {
this.errored = true;
return Promise.reject(
"No parser or decoder attached in this AudioStreamSource"
);
}
await this.decodeData(data, decoder, parser);
if (done) await this.decodeFlushData(decoder, parser);
this.checkBufferedPlay();
}
/**
* 检查采样率,如果还未解析出采样率,那么将设置采样率,如果当前采样率与之前不同,那么发出警告
*/
checkSampleRate(info) {
for (const one of info) {
const frame = isOggPage(one) ? one.codecFrames[0] : one;
if (frame) {
const rate = frame.header.sampleRate;
if (this.sampleRate === 0) {
this.sampleRate = rate;
break;
} else {
if (rate !== this.sampleRate) {
console.warn("Sample rate in stream audio must be constant.");
}
}
}
}
}
/**
* 解析音频数据
*/
async decodeData(data, decoder, parser) {
// 解析音频数据
const audioData = await decoder.decode(data);
if (!audioData) return;
// @ts-expect-error 库类型声明错误
const audioInfo = [...parser.parseChunk(data)];
// 检查采样率
this.checkSampleRate(audioInfo);
// 追加音频数据
this.appendDecodedData(audioData, audioInfo);
}
/**
* 解码剩余数据
*/
async decodeFlushData(decoder, parser) {
const audioData = await decoder.flush();
if (!audioData) return;
// @ts-expect-error 库类型声明错误
const audioInfo = [...parser.flush()];
this.checkSampleRate(audioInfo);
this.appendDecodedData(audioData, audioInfo);
}
/**
* 追加音频数据
*/
appendDecodedData(data, info) {
const channels = data.channelData.length;
if (channels === 0) return;
if (this.audioData.length !== channels) {
this.audioData = [];
for (let i = 0; i < channels; i++) {
this.audioData.push([]);
}
}
// 计算出应该放在哪
const chunk = this.sampleRate * this.bufferChunkSize;
const sampled = this.bufferedSamples;
const pushIndex = Math.floor(sampled / chunk);
const bufferIndex = sampled % chunk;
const dataLength = data.channelData[0].length;
let buffered = 0;
let nowIndex = pushIndex;
let toBuffer = bufferIndex;
while (buffered < dataLength) {
const rest = toBuffer !== 0 ? chunk - bufferIndex : chunk;
for (let i = 0; i < channels; i++) {
const audioData = this.audioData[i];
if (!audioData[nowIndex]) {
audioData.push(new Float32Array(chunk));
}
const toPush = data.channelData[i].slice(buffered, buffered + rest);
audioData[nowIndex].set(toPush, toBuffer);
}
buffered += rest;
nowIndex++;
toBuffer = 0;
}
this.buffered +=
info.reduce((prev, curr) => prev + curr.duration, 0) / 1000;
this.bufferedSamples += info.reduce(
(prev, curr) => prev + curr.samples,
0
);
}
/**
* 检查已缓冲内容,并在未开始播放时播放
*/
checkBufferedPlay() {
if (this.playing || this.sampleRate === 0) return;
const played = this.lastBufferSamples / this.sampleRate;
const dt = this.buffered - played;
if (this.loaded) {
this.playAudio(played);
return;
}
if (dt < this.bufferPlayDuration) return;
this.lastBufferSamples = this.bufferedSamples;
// 需要播放
this.mergeBuffers();
if (!this.buffer) return;
if (this.playing) this.output.stop();
this.createSourceNode(this.buffer);
this.output.loop = false;
this.output.start(0, played);
this.lastStartTime = this.ac.currentTime;
this.playing = true;
this.output.addEventListener("ended", () => {
this.playing = false;
this.checkBufferedPlay();
});
}
mergeBuffers() {
const buffer = this.ac.createBuffer(
this.audioData.length,
this.bufferedSamples,
this.sampleRate
);
const chunk = this.sampleRate * this.bufferChunkSize;
const bufferedChunks = Math.floor(this.bufferedSamples / chunk);
const restLength = this.bufferedSamples % chunk;
for (let i = 0; i < this.audioData.length; i++) {
const audio = this.audioData[i];
const data = new Float32Array(this.bufferedSamples);
for (let j = 0; j < bufferedChunks; j++) {
data.set(audio[j], chunk * j);
}
if (restLength !== 0) {
data.set(
audio[bufferedChunks].slice(0, restLength),
chunk * bufferedChunks
);
}
buffer.copyToChannel(data, i, 0);
}
this.buffer = buffer;
}
async start() {
delete this.buffer;
this.headerRecieved = false;
this.audioType = "";
this.errored = false;
this.buffered = 0;
this.sampleRate = 0;
this.bufferedSamples = 0;
this.duration = 0;
this.loaded = false;
if (this.playing) this.output.stop();
this.playing = false;
this.lastStartTime = this.ac.currentTime;
}
end(done, reason) {
if (done && this.buffer) {
this.loaded = true;
delete this.controller;
this.mergeBuffers();
this.duration = this.buffered;
this.audioData = [];
this.decoder?.destroy();
delete this.decoder;
delete this.parser;
} else {
console.warn(
"Unexpected end when loading stream audio, reason: '" +
(reason ?? "") +
"'"
);
}
}
playAudio(when) {
if (!this.buffer) return;
this.lastStartTime = this.ac.currentTime;
if (this.playing) this.output.stop();
if (this.route.status !== AudioStatus.Playing) {
this.route.status = AudioStatus.Playing;
}
this.createSourceNode(this.buffer);
this.output.start(0, when);
this.playing = true;
this.output.addEventListener("ended", () => {
this.playing = false;
if (this.route.status === AudioStatus.Playing) {
this.route.status = AudioStatus.Stoped;
}
if (this.loop && !this.output.loop) this.play(0);
});
}
/**
* 开始播放这个音频源
*/
play(when) {
if (this.playing || this.errored) return;
if (this.loaded && this.buffer) {
this.playing = true;
this.playAudio(when);
} else {
this.controller?.start();
}
}
createSourceNode(buffer) {
if (!this.target) return;
const node = this.ac.createBufferSource();
node.buffer = buffer;
if (this.playing) this.output.stop();
this.playing = false;
this.output = node;
node.connect(this.target.input);
node.loop = this.loop;
}
/**
* 停止播放这个音频源
* @returns 音频暂停的时刻 number
*/
stop() {
if (this.playing) this.output.stop();
this.playing = false;
return this.ac.currentTime - this.lastStartTime;
}
/**
* 连接到音频路由图上,每次调用播放的时候都会执行一次
* @param target 连接至的目标 IAudioInput
*/
connect(target) {
this.target = target;
}
/**
* 设置是否循环播放
* @param loop 是否循环 boolean)
*/
setLoop(loop) {
this.loop = loop;
}
}
class AudioElementSource {
constructor(context) {
const audio = new Audio();
audio.preload = "none";
this.output = context.createMediaElementSource(audio);
this.audio = audio;
this.ac = context;
audio.addEventListener("play", () => {
this.playing = true;
if (this.route.status !== AudioStatus.Playing) {
this.route.status = AudioStatus.Playing;
}
});
audio.addEventListener("ended", () => {
this.playing = false;
if (this.route.status === AudioStatus.Playing) {
this.route.status = AudioStatus.Stoped;
}
});
}
get duration() {
return this.audio.duration;
}
get currentTime() {
return this.audio.currentTime;
}
/**
* 设置音频源的路径
* @param url 音频路径
*/
setSource(url) {
this.audio.src = url;
}
play(when = 0) {
if (this.playing) return;
this.audio.currentTime = when;
this.audio.play();
}
stop() {
this.audio.pause();
this.playing = false;
if (this.route.status === AudioStatus.Playing) {
this.route.status = AudioStatus.Stoped;
}
return this.audio.currentTime;
}
connect(target) {
this.output.connect(target.input);
}
setLoop(loop) {
this.audio.loop = loop;
}
}
class AudioBufferSource {
constructor(context) {
this.output = context.createBufferSource();
/** 是否循环 */
this.loop = false;
/** 上一次播放是从何时开始的 */
this.lastStartWhen = 0;
/** 播放开始时刻 */
this.lastStartTime = 0;
this.duration = 0;
this.ac = context;
}
get currentTime() {
return this.ac.currentTime - this.lastStartTime + this.lastStartWhen;
}
/**
* 设置音频源数据
* @param buffer 音频源,可以是未解析的 ArrayBuffer也可以是已解析的 AudioBuffer
*/
async setBuffer(buffer) {
if (buffer instanceof ArrayBuffer) {
this.buffer = await this.ac.decodeAudioData(buffer);
} else {
this.buffer = buffer;
}
this.duration = this.buffer.duration;
}
play(when) {
if (this.playing || !this.buffer) return;
this.playing = true;
this.lastStartTime = this.ac.currentTime;
if (this.route.status !== AudioStatus.Playing) {
this.route.status = AudioStatus.Playing;
}
this.createSourceNode(this.buffer);
this.output.start(0, when);
this.output.addEventListener("ended", () => {
this.playing = false;
if (this.route.status === AudioStatus.Playing) {
this.route.status = AudioStatus.Stoped;
}
if (this.loop && !this.output.loop) this.play(0);
});
}
createSourceNode(buffer) {
if (!this.target) return;
const node = this.ac.createBufferSource();
node.buffer = buffer;
this.output = node;
node.connect(this.target.input);
node.loop = this.loop;
}
stop() {
this.output.stop();
return this.ac.currentTime - this.lastStartTime;
}
connect(target) {
this.target = target;
}
setLoop(loop) {
this.loop = loop;
}
}
class AudioPlayer {
constructor() {
/** 音频播放上下文 */
this.ac = new AudioContext();
/** 音量节点 */
this.gain = this.ac.createGain();
this.gain.connect(this.ac.destination);
this.audioRoutes = new Map();
}
/**
* 解码音频数据
* @param data 音频数据
*/
decodeAudioData(data) {
return AudioDecoder.decodeAudioData(data, this);
}
/**
* 设置音量
* @param volume 音量
*/
setVolume(volume) {
this.gain.gain.value = volume;
}
/**
* 获取音量
*/
getVolume() {
return this.gain.gain.value;
}
/**
* 创建一个音频源
* @param Source 音频源类
*/
createSource(Source) {
return new Source(this.ac);
}
/**
* 创建一个兼容流式音频源,可以与流式加载相结合,主要用于处理 opus ogg 不兼容的情况
*/
createStreamSource() {
return new AudioStreamSource(this.ac);
}
/**
* 创建一个通过 audio 元素播放的音频源
*/
createElementSource() {
return new AudioElementSource(this.ac);
}
/**
* 创建一个通过 AudioBuffer 播放的音频源
*/
createBufferSource() {
return new AudioBufferSource(this.ac);
}
/**
* 获取音频目的地
*/
getDestination() {
return this.gain;
}
/**
* 创建一个音频效果器
* @param Effect 效果器类
*/
createEffect(Effect) {
return new Effect(this.ac);
}
/**
* 创建一个修改音量的效果器
* ```txt
* |----------|
* Input ----> | GainNode | ----> Output
* |----------|
* ```
*/
createVolumeEffect() {
return new VolumeEffect(this.ac);
}
/**
* 创建一个立体声效果器
* ```txt
* |------------|
* Input ----> | PannerNode | ----> Output
* |------------|
* ```
*/
createStereoEffect() {
return new StereoEffect(this.ac);
}
/**
* 创建一个修改单个声道音量的效果器
* ```txt
* |----------|
* -> | GainNode | \
* |--------------| / |----------| -> |------------|
* Input ----> | SplitterNode | ...... | MergerNode | ----> Output
* |--------------| \ |----------| -> |------------|
* -> | GainNode | /
* |----------|
* ```
*/
createChannelVolumeEffect() {
return new ChannelVolumeEffect(this.ac);
}
/**
* 创建一个延迟效果器
* |-----------|
* Input ----> | DelayNode | ----> Output
* |-----------|
*/
createDelay() {
return new DelayEffect(this.ac);
}
/**
* 创建一个回声效果器
* ```txt
* |----------|
* Input ----> | GainNode | ----> Output
* ^ |----------| |
* | |
* | |------------| ↓
* |-- | Delay Node | <--
* |------------|
* ```
*/
createEchoEffect() {
return new EchoEffect(this.ac);
}
/**
* 创建一个音频播放路由
* @param source 音频源
*/
createRoute(source) {
return new AudioRoute(source, this);
}
/**
* 添加一个音频播放路由,可以直接被播放
* @param id 这个音频播放路由的名称
* @param route 音频播放路由对象
*/
addRoute(id, route) {
if (!this.audioRoutes) this.audioRoutes = new Map();
if (this.audioRoutes.has(id)) {
console.warn(
"Audio route with id of '" +
id +
"' has already existed. New route will override old route."
);
}
this.audioRoutes.set(id, route);
}
/**
* 根据名称获取音频播放路由对象
* @param id 音频播放路由的名称
*/
getRoute(id) {
return this.audioRoutes.get(id);
}
/**
* 移除一个音频播放路由
* @param id 要移除的播放路由的名称
*/
removeRoute(id) {
this.audioRoutes.delete(id);
}
/**
* 播放音频
* @param id 音频名称
* @param when 从音频的哪个位置开始播放,单位秒
*/
play(id, when) {
const route = this.getRoute(id);
if (!route) {
console.warn(
"Cannot play audio route '" +
id +
"', since there is not added route named it."
);
return;
}
route.play(when);
}
/**
* 暂停音频播放
* @param id 音频名称
* @returns 当音乐真正停止时兑现
*/
pause(id) {
const route = this.getRoute(id);
if (!route) {
console.warn(
"Cannot pause audio route '" +
id +
"', since there is not added route named it."
);
return;
}
return route.pause();
}
/**
* 停止音频播放
* @param id 音频名称
* @returns 当音乐真正停止时兑现
*/
stop(id) {
const route = this.getRoute(id);
if (!route) {
console.warn(
"Cannot stop audio route '" +
id +
"', since there is not added route named it."
);
return;
}
return route.stop();
}
/**
* 继续音频播放
* @param id 音频名称
*/
resume(id) {
const route = this.getRoute(id);
if (!route) {
console.warn(
"Cannot pause audio route '" +
id +
"', since there is not added route named it."
);
return;
}
route.resume();
}
/**
* 设置听者位置x正方向水平向右y正方向垂直于地面向上z正方向垂直屏幕远离用户
* @param x 位置x坐标
* @param y 位置y坐标
* @param z 位置z坐标
*/
setListenerPosition(x, y, z) {
const listener = this.ac.listener;
listener.positionX.value = x;
listener.positionY.value = y;
listener.positionZ.value = z;
}
/**
* 设置听者朝向x正方向水平向右y正方向垂直于地面向上z正方向垂直屏幕远离用户
* @param x 朝向x坐标
* @param y 朝向y坐标
* @param z 朝向z坐标
*/
setListenerOrientation(x, y, z) {
const listener = this.ac.listener;
listener.forwardX.value = x;
listener.forwardY.value = y;
listener.forwardZ.value = z;
}
/**
* 设置听者头顶朝向x正方向水平向右y正方向垂直于地面向上z正方向垂直屏幕远离用户
* @param x 头顶朝向x坐标
* @param y 头顶朝向y坐标
* @param z 头顶朝向z坐标
*/
setListenerUp(x, y, z) {
const listener = this.ac.listener;
listener.upX.value = x;
listener.upY.value = y;
listener.upZ.value = z;
}
}
class AudioRoute {
constructor(source, player) {
source.route = this;
this.output = source.output;
/** 效果器路由图 */
this.effectRoute = [];
/** 结束时长,当音频暂停或停止时,会经过这么长时间之后才真正终止播放,期间可以做音频淡入淡出等效果 */
this.endTime = 0;
/** 暂停时播放了多长时间 */
this.pauseCurrentTime = 0;
/** 当前播放状态 */
this.player = player;
this.status = AudioStatus.Stoped;
this.shouldStop = false;
/**
* 每次暂停或停止时自增,用于判断当前正在处理的情况。
* 假如暂停后很快播放,然后很快暂停,那么需要根据这个来判断实际是否应该执行暂停后操作
*/
this.stopIdentifier = 0;
/** 暂停时刻 */
this.pauseTime = 0;
this.source = source;
this.source.player = player;
}
/** 音频时长,单位秒 */
get duration() {
return this.source.duration;
}
/** 当前播放了多长时间,单位秒 */
get currentTime() {
if (this.status === AudioStatus.Paused) {
return this.pauseCurrentTime;
} else {
return this.source.currentTime;
}
}
set currentTime(time) {
this.source.stop();
this.source.play(time);
}
/**
* 设置结束时间,暂停或停止时,会经过这么长时间才终止音频的播放,这期间可以做一下音频淡出的效果。
* @param time 暂停或停止时,经过多长时间之后才会结束音频的播放
*/
setEndTime(time) {
this.endTime = time;
}
/**
* 当音频播放时执行的函数,可以用于音频淡入效果
* @param fn 音频开始播放时执行的函数
*/
onStart(fn) {
this.audioStartHook = fn;
}
/**
* 当音频暂停或停止时执行的函数,可以用于音频淡出效果
* @param fn 音频在暂停或停止时执行的函数,不填时表示取消这个钩子。
* 包含两个参数,第一个参数是结束时长,第二个参数是当前音频播放路由对象
*/
onEnd(fn) {
this.audioEndHook = fn;
}
/**
* 开始播放这个音频
* @param when 从音频的什么时候开始播放,单位秒
*/
async play(when = 0) {
if (this.status === AudioStatus.Playing) return;
this.link();
await this.player.ac.resume();
if (this.effectRoute.length > 0) {
const first = this.effectRoute[0];
this.source.connect(first);
const last = this.effectRoute.at(-1);
last.connect({ input: this.player.getDestination() });
} else {
this.source.connect({ input: this.player.getDestination() });
}
this.source.play(when);
this.status = AudioStatus.Playing;
this.pauseTime = 0;
this.audioStartHook?.(this);
this.startAllEffect();
if (this.status !== AudioStatus.Playing) {
this.status = AudioStatus.Playing;
}
}
/**
* 暂停音频播放
*/
async pause() {
if (this.status !== AudioStatus.Playing) return;
this.status = AudioStatus.Pausing;
this.stopIdentifier++;
const identifier = this.stopIdentifier;
if (this.audioEndHook) {
this.audioEndHook(this.endTime, this);
await sleep(this.endTime);
}
if (
this.status !== AudioStatus.Pausing ||
this.stopIdentifier !== identifier
) {
return;
}
this.pauseCurrentTime = this.source.currentTime;
const time = this.source.stop();
this.pauseTime = time;
if (this.shouldStop) {
this.status = AudioStatus.Stoped;
this.endAllEffect();
this.shouldStop = false;
} else {
this.status = AudioStatus.Paused;
this.endAllEffect();
}
this.endAllEffect();
}
/**
* 继续音频播放
*/
resume() {
if (this.status === AudioStatus.Playing) return;
if (
this.status === AudioStatus.Pausing ||
this.status === AudioStatus.Stoping
) {
this.audioStartHook?.(this);
return;
}
if (this.status === AudioStatus.Paused) {
this.play(this.pauseTime);
} else {
this.play(0);
}
this.status = AudioStatus.Playing;
this.pauseTime = 0;
this.audioStartHook?.(this);
this.startAllEffect();
}
/**
* 停止音频播放
*/
async stop() {
if (this.status !== AudioStatus.Playing) {
if (this.status === AudioStatus.Pausing) {
this.shouldStop = true;
}
return;
}
this.status = AudioStatus.Stoping;
this.stopIdentifier++;
const identifier = this.stopIdentifier;
if (this.audioEndHook) {
this.audioEndHook(this.endTime, this);
await sleep(this.endTime);
}
if (
this.status !== AudioStatus.Stoping ||
this.stopIdentifier !== identifier
) {
return;
}
this.source.stop();
this.status = AudioStatus.Stoped;
this.pauseTime = 0;
this.endAllEffect();
}
/**
* 添加效果器
* @param effect 要添加的效果,可以是数组,表示一次添加多个
* @param index 从哪个位置开始添加如果大于数组长度那么加到末尾如果小于0那么将会从后面往前数。默认添加到末尾
*/
addEffect(effect, index) {
if (isNil(index)) {
if (effect instanceof Array) {
this.effectRoute.push(...effect);
} else {
this.effectRoute.push(effect);
}
} else {
if (effect instanceof Array) {
this.effectRoute.splice(index, 0, ...effect);
} else {
this.effectRoute.splice(index, 0, effect);
}
}
this.setOutput();
if (this.source.playing) this.link();
}
/**
* 移除一个效果器
* @param effect 要移除的效果
*/
removeEffect(effect) {
const index = this.effectRoute.indexOf(effect);
if (index === -1) return;
this.effectRoute.splice(index, 1);
effect.disconnect();
this.setOutput();
if (this.source.playing) this.link();
}
setOutput() {
const effect = this.effectRoute.at(-1);
if (!effect) this.output = this.source.output;
else this.output = effect.output;
}
/**
* 连接音频路由图
*/
link() {
this.effectRoute.forEach((v) => v.disconnect());
this.effectRoute.forEach((v, i) => {
const next = this.effectRoute[i + 1];
if (next) {
v.connect(next);
}
});
}
startAllEffect() {
this.effectRoute.forEach((v) => v.start());
}
endAllEffect() {
this.effectRoute.forEach((v) => v.end());
}
}
const audioPlayer = new AudioPlayer();
class BgmController {
constructor(player) {
this.mainGain = player.createVolumeEffect();
this.player = player;
/** bgm音频名称的前缀 */
this.prefix = "bgms.";
/** 每个 bgm 的音量控制器 */
this.gain = new Map();
/** 正在播放的 bgm */
this.playingBgm = "";
/** 是否正在播放 */
this.playing = false;
/** 是否已经启用 */
this.enabled = true;
/** 是否屏蔽所有的音乐切换 */
this.blocking = false;
/** 渐变时长 */
this.transitionTime = 2000;
}
/**
* 设置音频渐变时长
* @param time 渐变时长
*/
setTransitionTime(time) {
this.transitionTime = time;
for (const [, value] of this.gain) {
value.transition.time(time);
}
}
/**
* 屏蔽音乐切换
*/
blockChange() {
this.blocking = true;
}
/**
* 取消屏蔽音乐切换
*/
unblockChange() {
this.blocking = false;
}
/**
* 设置总音量大小
* @param volume 音量大小
*/
setVolume(volume) {
this.mainGain.setVolume(volume);
this._volume = volume;
}
/**
* 获取总音量大小
*/
getVolume() {
return this.mainGain.getVolume();
}
/**
* 设置是否启用
* @param enabled 是否启用
*/
setEnabled(enabled) {
if (enabled) this.resume();
else this.stop();
this.enabled = enabled;
}
/**
* 设置 bgm 音频名称的前缀
*/
setPrefix(prefix) {
this.prefix = prefix;
}
getId(name) {
return `${this.prefix}${name}`;
}
/**
* 根据 bgm 名称获取其 AudioRoute 实例
* @param id 音频名称
*/
get(id) {
return this.player.getRoute(this.getId(id));
}
/**
* 添加一个 bgm
* @param id 要添加的 bgm 的名称
* @param url 指定 bgm 的加载地址
*/
addBgm(id, url = `project/bgms/${id}`) {
const type = guessTypeByExt(id);
if (!type) {
console.warn(
"Unknown audio extension name: '" +
id.split(".").slice(0, -1).join(".") +
"'"
);
return;
}
const gain = this.player.createVolumeEffect();
if (isAudioSupport(type)) {
const source = audioPlayer.createElementSource();
source.setSource(url);
source.setLoop(true);
const route = new AudioRoute(source, audioPlayer);
route.addEffect([gain, this.mainGain]);
audioPlayer.addRoute(this.getId(id), route);
this.setTransition(id, route, gain);
} else {
const source = audioPlayer.createStreamSource();
const stream = new StreamLoader(url);
stream.pipe(source);
source.setLoop(true);
const route = new AudioRoute(source, audioPlayer);
route.addEffect([gain, this.mainGain]);
audioPlayer.addRoute(this.getId(id), route);
this.setTransition(id, route, gain);
}
}
/**
* 移除一个 bgm
* @param id 要移除的 bgm 的名称
*/
removeBgm(id) {
this.player.removeRoute(this.getId(id));
const gain = this.gain.get(id);
gain?.transition.ticker.destroy();
this.gain.delete(id);
}
setTransition(id, route, gain) {
const transition = new Transition();
transition
.time(this.transitionTime)
.mode(linear())
.transition("volume", 0);
const tick = () => {
gain.setVolume(transition.value.volume);
};
/**
* @param expect 在结束时应该是正在播放还是停止
*/
const setTick = async (expect) => {
transition.ticker.remove(tick);
transition.ticker.add(tick);
const identifier = route.stopIdentifier;
await sleep(this.transitionTime + 500);
if (route.status === expect && identifier === route.stopIdentifier) {
transition.ticker.remove(tick);
if (route.status === AudioStatus.Playing) {
gain.setVolume(1);
} else {
gain.setVolume(0);
}
}
};
route.onStart(async () => {
transition.transition("volume", 1);
setTick(AudioStatus.Playing);
});
route.onEnd(() => {
transition.transition("volume", 0);
setTick(AudioStatus.Paused);
});
route.setEndTime(this.transitionTime);
this.gain.set(id, { effect: gain, transition });
}
/**
* 播放一个 bgm
* @param id 要播放的 bgm 名称
*/
play(id, when) {
if (this.blocking) return;
if (id !== this.playingBgm && this.playingBgm) {
this.player.pause(this.getId(this.playingBgm));
}
this.playingBgm = id;
if (!this.enabled) return;
this.player.play(this.getId(id), when);
this.playing = true;
}
/**
* 继续当前的 bgm
*/
resume() {
if (this.blocking || !this.enabled || this.playing) return;
if (this.playingBgm) {
this.player.resume(this.getId(this.playingBgm));
}
this.playing = true;
}
/**
* 暂停当前的 bgm
*/
pause() {
if (this.blocking || !this.enabled) return;
if (this.playingBgm) {
this.player.pause(this.getId(this.playingBgm));
}
this.playing = false;
}
/**
* 停止当前的 bgm
*/
stop() {
if (this.blocking || !this.enabled) return;
if (this.playingBgm) {
this.player.stop(this.getId(this.playingBgm));
}
this.playing = false;
}
}
const bgmController = new BgmController(audioPlayer);
class SoundPlayer {
constructor(player) {
/** 每个音效的唯一标识符 */
this.num = 0;
this.enabled = true;
this.gain = player.createVolumeEffect();
/** 每个音效的数据 */
this.buffer = new Map();
/** 所有正在播放的音乐 */
this.playing = new Set();
this.player = player;
}
/**
* 设置是否启用音效
* @param enabled 是否启用音效
*/
setEnabled(enabled) {
if (!enabled) this.stopAllSounds();
this.enabled = enabled;
}
/**
* 设置音量大小
* @param volume 音量大小
*/
setVolume(volume) {
this.gain.setVolume(volume);
}
/**
* 获取音量大小
*/
getVolume() {
return this.gain.getVolume();
}
/**
* 添加一个音效
* @param id 音效名称
* @param data 音效的Uint8Array数据
*/
async add(id, data) {
const buffer = await this.player.decodeAudioData(data);
if (!buffer) {
console.warn(
"Cannot decode sound '" +
id +
"', since audio file may not supported by 2.b."
);
return;
}
this.buffer.set(id, buffer);
}
/**
* 播放一个音效
* @param id 音效名称
* @param position 音频位置,[0, 0, 0]表示正中心x轴指向水平向右y轴指向水平向上z轴指向竖直向上
* @param orientation 音频朝向,[0, 1, 0]表示朝向前方
*/
play(id, position = [0, 0, 0], orientation = [1, 0, 0]) {
if (!this.enabled || !id) return -1;
const buffer = this.buffer.get(id);
if (!buffer) {
console.warn(
"Cannot play sound '" +
id +
"', since there is no added data named it."
);
return -1;
}
const soundNum = this.num++;
const source = this.player.createBufferSource();
source.setBuffer(buffer);
const route = this.player.createRoute(source);
const stereo = this.player.createStereoEffect();
stereo.setPosition(position[0], position[1], position[2]);
stereo.setOrientation(orientation[0], orientation[1], orientation[2]);
route.addEffect([stereo, this.gain]);
this.player.addRoute(`sounds.${soundNum}`, route);
route.play();
source.output.addEventListener("ended", () => {
this.playing.delete(soundNum);
});
this.playing.add(soundNum);
return soundNum;
}
/**
* 停止一个音效
* @param num 音效的唯一 id
*/
stop(num) {
const id = `sounds.${num}`;
const route = this.player.getRoute(id);
if (route) {
route.stop();
this.player.removeRoute(id);
this.playing.delete(num);
}
}
/**
* 停止播放所有音效
*/
stopAllSounds() {
this.playing.forEach((v) => {
const id = `sounds.${v}`;
const route = this.player.getRoute(id);
if (route) {
route.stop();
this.player.removeRoute(id);
}
});
this.playing.clear();
}
}
const soundPlayer = new SoundPlayer(audioPlayer);
function loadAllBgm() {
const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
for (const bgm of data.main.bgms) {
bgmController.addBgm(bgm);
}
}
loadAllBgm();
AudioDecoder.registerDecoder(AudioType.Ogg, VorbisDecoder);
AudioDecoder.registerDecoder(AudioType.Opus, OpusDecoder);
core.plugin.audioSystem = {
AudioType,
AudioDecoder,
AudioStatus,
checkAudioType,
isAudioSupport,
audioPlayer,
soundPlayer,
bgmController,
guessTypeByExt,
BgmController,
SoundPlayer,
EchoEffect,
DelayEffect,
ChannelVolumeEffect,
VolumeEffect,
StereoEffect,
AudioEffect,
AudioPlayer,
AudioRoute,
AudioStreamSource,
AudioElementSource,
AudioBufferSource,
loadAllBgm,
StreamLoader,
};
//bgm相关复写
control.prototype.playBgm = (bgm, when) => {
bgm = core.getMappedName(bgm);
bgmController.play(bgm, when);
core.setMusicBtn();
};
control.prototype.pauseBgm = () => {
bgmController.pause();
core.setMusicBtn();
};
control.prototype.resumeBgm = function () {
bgmController.resume();
core.setMusicBtn();
};
control.prototype.checkBgm = function () {
core.playBgm(bgmController.playingBgm || main.startBgm);
};
control.prototype.triggerBgm = function () {
core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus;
if (bgmController.playing) bgmController.pause();
else bgmController.resume();
core.setMusicBtn();
core.setLocalStorage("bgmStatus", core.musicStatus.bgmStatus);
};
//sound相关复写
control.prototype.playSound = function (
sound,
_pitch,
callback,
position,
orientation
) {
if (main.mode != "play" || !core.musicStatus.soundStatus) return;
const name = core.getMappedName(sound);
const num = soundPlayer.play(name, position, orientation);
const route = audioPlayer.getRoute(`sounds.${num}`);
if (!route) {
callback?.();
return -1;
} else {
sleep(route.duration * 1000).then(() => callback?.());
return num;
}
};
control.prototype.stopSound = function (id) {
if (isNil(id)) {
soundPlayer.stopAllSounds();
} else {
soundPlayer.stop(id);
}
};
control.prototype.getPlayingSounds = function () {
return [...soundPlayer.playing];
};
//sound加载复写
loader.prototype._loadOneSound_decodeData = function (name, data) {
if (data instanceof Blob) {
var blobReader = new zip.BlobReader(data);
blobReader.init(function () {
blobReader.readUint8Array(0, blobReader.size, function (uint8) {
//core.loader._loadOneSound_decodeData(name, uint8.buffer);
soundPlayer.add(name, uint8);
});
});
return;
}
if (data instanceof ArrayBuffer) {
const uint8 = new Uint8Array(data);
soundPlayer.add(name, uint8);
}
};
//音量控制复写
soundPlayer.setVolume(
core.musicStatus.userVolume * core.musicStatus.designVolume
);
bgmController.setVolume(
core.musicStatus.userVolume * core.musicStatus.designVolume
);
actions.prototype._clickSwitchs_sounds_userVolume = function (delta) {
var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume));
if (value == 0 && delta < 0) return;
core.musicStatus.userVolume = core.clamp(
Math.pow(value + delta, 2) / 100,
0,
1
);
//audioContext 音效 不受designVolume 影响
if (core.musicStatus.gainNode != null)
core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume;
soundPlayer.setVolume(
core.musicStatus.userVolume * core.musicStatus.designVolume
);
bgmController.setVolume(
core.musicStatus.userVolume * core.musicStatus.designVolume
);
core.setLocalStorage("userVolume", core.musicStatus.userVolume);
core.playSound("确定");
core.ui._drawSwitchs_sounds();
};
},
"自定义常用事件": function () {
// editorBlocklyconfigPlus.js
// 自訂常見事件模板插件
// 本插件引用了通用函數插件(Utility.js)
// 適用樣板2.10.3
// 請注意:
// 此插件對事件編輯器(editor_blocklyconfig)進行複寫,若還有其它針對事件編輯器做複寫的插件,請謹慎使用!
// 此插件對表格操作行為(editor_mode.doActionList)進行複寫,若還有其它對表格操作行為做複寫的插件,請謹慎使用!
// 使用方法:
// 現在在主頁下拉選單多了個常用事件模版,在那邊可以自由設定常用事件模板。
// 設定完後按F5刷新再到事件編輯器看就有你設定好的常用事件模板了。
if (main.mode == "editor") {
//#region 配置表格初始化
let TableFileName = "project/table/CommonEventTemplate_comment.js";
let TableRow = `
var CommonEventTemplate_comment = {"_type": "object",
"_data": {
"CommonEventTemplate": {
"_type": "object",
"_data": function (key) {
var obj = {
"检测音乐如果没有开启则系统提示开启": {
"_leaf": true,
"_type": "event",
"_event": "commonEvent",
"_data": "检测音乐如果没有开启则系统提示开启"
},
"仿新新魔塔一次性商人": {
"_leaf": true,
"_type": "event",
"_event": "commonEvent",
"_data": "仿新新魔塔一次性商人"
},
"全地图选中一个点": {
"_leaf": true,
"_type": "event",
"_event": "commonEvent",
"_data": "全地图选中一个点"
},
"多阶段Boss战斗": {
"_leaf": true,
"_type": "event",
"_event": "commonEvent",
"_data": "多阶段Boss战斗"
},
}
if (obj[key]) return obj[key];
return {
"_leaf": true,
"_type": "event",
"_event": "commonEvent",
"_data": "常見事件模板"
}
}
}
}}
`;
if (!events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate) {
/**
* @type {{[EvnetName:actionParserJson]}}
*/
events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate = {
检测音乐如果没有开启则系统提示开启: [
{
type: "if",
condition: "!core.musicStatus.bgmStatus",
true: [
"\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳",
],
false: [],
},
],
仿新新魔塔一次性商人: [
{
type: "if",
condition: "switch:A",
true: [
"\t[行商,trader]\b[this]这是购买我的道具后我给玩家的提示。",
{
type: "comment",
text: "下一条指令可视情况使用或不使用",
},
{
type: "hide",
remove: true,
time: 250,
},
],
false: [
{
type: "confirm",
text: "我有3把黄钥匙\n你出50金币就卖给你。",
yes: [
{
type: "if",
condition: "status:money>=50",
true: [
{
type: "setValue",
name: "status:money",
operator: "-=",
value: "50",
},
{
type: "setValue",
name: "item:yellowKey",
operator: "+=",
value: "3",
},
{
type: "playSound",
name: "确定",
stop: true,
},
{
type: "setValue",
name: "switch:A",
value: "true",
},
],
false: [
{
type: "playSound",
name: "操作失败",
},
"\t[行商,trader]\b[this]你的金币不足!",
],
},
],
no: [],
},
],
},
],
全地图选中一个点: [
{
type: "comment",
text: "全地图选中一个点,需要用鼠标或触屏操作",
},
{
type: "setValue",
name: "temp:X",
value: "status:x",
},
{
type: "setValue",
name: "temp:Y",
value: "status:y",
},
{
type: "tip",
text: "再次点击闪烁位置确认",
},
{
type: "while",
condition: "true",
data: [
{
type: "drawSelector",
image: "winskin.webp",
code: 1,
x: "32*temp:X",
y: "32*temp:Y",
width: 32,
height: 32,
},
{
type: "wait",
},
{
type: "if",
condition: "(flag:type === 1)",
true: [
{
type: "if",
condition: "((temp:X===flag:x)&&(temp:Y===flag:y))",
true: [
{
type: "break",
n: 1,
},
],
},
{
type: "setValue",
name: "temp:X",
value: "flag:x",
},
{
type: "setValue",
name: "temp:Y",
value: "flag:y",
},
],
},
],
},
{
type: "drawSelector",
code: 1,
},
{
type: "comment",
text: "流程进行到这里可以对[X,Y]点进行处理,比如",
},
{
type: "closeDoor",
id: "yellowDoor",
loc: ["temp:X", "temp:Y"],
},
],
多阶段Boss战斗: [
{
type: "comment",
text: "多阶段boss请直接作为战后事件使用",
},
{
type: "setValue",
name: "switch:A",
operator: "+=",
value: "1",
},
{
type: "switch",
condition: "switch:A",
caseList: [
{
case: "1",
action: [
{
type: "setBlock",
number: "redSlime",
},
"\t[2阶段boss,redSlime]\b[this]你以为你已经打败我了吗?没听说过史莱姆有九条命吗?",
],
},
{
case: "2",
action: [
{
type: "setBlock",
number: "blackSlime",
},
"\t[3阶段boss,blackSlime]\b[this]不能消灭我的,只会让我更强大!",
],
},
{
case: "3",
action: [
{
type: "setBlock",
number: "slimelord",
},
"\t[4阶段boss,slimelord]\b[this]我还能打!",
],
},
{
case: "4",
action: ["\t[4阶段boss,slimelord]我一定会回来的!"],
},
],
},
],
};
}
//#endregion
// 新增模板選項
let editModeSelect = document.getElementById("editModeSelect");
let newEditModeOption = document.createElement("option");
newEditModeOption.value = "CommonEventTemplate";
newEditModeOption.text = "常見事件模板";
editModeSelect.add(newEditModeOption);
//檢查可用的編輯模板ID
let leftIDNumber = 11 - 1;
let ExistLeftElement = document.querySelector(".main");
while (ExistLeftElement) {
leftIDNumber++;
ExistLeftElement = document.getElementById(`left${leftIDNumber}`);
}
//新增編輯模板
let MainDiv = document.querySelector(".main");
let CommonEventTemplateMainDiv = document.createElement("div");
CommonEventTemplateMainDiv.id = `left${leftIDNumber}`;
CommonEventTemplateMainDiv.className = "leftTab";
CommonEventTemplateMainDiv.style.zIndex = "-1";
CommonEventTemplateMainDiv.style.opacity = "0";
CommonEventTemplateMainDiv.innerHTML = `
<!-- CommonEventTemplate -->
<h3 class="leftTabHeader">
常見事件模板  
<button onclick="editor.mode.onmode('save')">保存</button>  
<button onclick="editor.table.CommonEventTemplateAddFunc()">添加</button>  
<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>  
<button onclick="editor_multi.CommonEventTemplateEditCommentJs('CommonEventTemplate')">配置表格</button>
</h3>
<div class="leftTabContent">
<div class='etable'>
<table>
<tbody id='table_298572d8-93dd-4c6e-a278-6a7d49831e3a'>
<tr>
<td>条目</td>
<td>注释</td>
<td>值</td>
</tr>
</tbody>
</table>
</div>
</div>
`;
MainDiv.appendChild(CommonEventTemplateMainDiv);
(async function () {
//等待編輯器初始化
while (!editor_mode.ids) {
await Sleep(100);
}
//新增編輯模板ID
editor_mode.ids["CommonEventTemplate"] = `left${leftIDNumber}`;
editor_mode.init_dom_ids();
//切換至常見事件模板
editor_mode.CommonEventTemplate = function (callback) {
var objs = [];
editor.file.editCommonEventTemplate([], function (objs_) {
objs = objs_;
//console.log(objs_)
});
//只查询不修改时,内部实现不是异步的,所以可以这么写
var tableinfo = editor.table.objToTable(objs[0], objs[1]);
document.getElementById(
"table_298572d8-93dd-4c6e-a278-6a7d49831e3a"
).innerHTML = tableinfo.HTML;
tableinfo.listen(tableinfo.guids);
if (Boolean(callback)) callback();
};
//檢查配置表格存在
let TableRowExist = null;
fs.readFile(TableFileName, "base64", function (err, data) {
if (err) {
console.log(`察覺常見事件模板配置表格不存在,原因:${err}`);
console.log("新建一個常見事件模板配置表格。");
TableRowExist = false;
} else {
TableRowExist = true;
}
});
//等待配置表格載入完畢(最多0.3秒,超過則視為失敗)
for (let i = 0; i < 3; i++) {
if (TableRowExist == null) {
await Sleep(100);
}
}
//配置表格初始化
if (TableRowExist != true) {
fs.mkdir("project/table", function (err, data) {
if (err) throw `常見事件模板配置表格目錄初始化失敗,原因:${err}`;
});
fs.writeFile(
TableFileName,
editor.util.encode64(TableRow || ""),
"base64",
function (err, data) {
if (err) throw `常見事件模板配置表格文件初始化失敗,原因:${err}`;
}
);
}
//載入配置表格
//editor.file.loadCommentjs(callback);
(function () {
var key = "CommonEventTemplate_comment";
var script = document.createElement("script");
script.src = "project/table/" + key + ".js";
document.body.appendChild(script);
script.onload = function () {
editor.file[key] = eval(key.replace(".", "_"));
var loaded = Boolean(editor.file[key]);
};
})();
//按下配置表格
editor_multi.CommonEventTemplateEditCommentJs = function (mod) {
editor_multi.lintAutocomplete = true;
editor_multi.setLint();
editor_multi.importFile(TableFileName);
};
//定義表格操作行為
editor_mode.OriginDoActionList = editor_mode.doActionList;
editor_mode.doActionList = function (mode, actionList, callback) {
if (editor_mode.mode == "CommonEventTemplate") {
if (actionList.length == 0) return;
printf("修改中...");
var cb = function (objs_) {
if (objs_.slice(-1)[0] != null) {
printe(objs_.slice(-1)[0]);
throw objs_.slice(-1)[0];
}
var str = "修改成功!";
if (
data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.name ==
"template"
)
str += "<br/>请注意全塔属性的name尚未修改请及时予以设置。";
printf(str);
if (callback) callback();
};
editor.file.editCommonEventTemplate(actionList, cb);
} else {
editor_mode.OriginDoActionList(mode, actionList, callback);
}
};
//添加表格列
editor.table.CommonEventTemplateAddFunc = function () {
let obj = events_c12a15a8_c380_4b28_8144_256cba95f760;
// 1.输入id
let newid = prompt("请输入新项的ID支持中文");
if (newid == null || newid.length == 0) {
return;
}
// 2.检查id是否符合规范或与已有id重复
var conflict = true;
var basefield = "".replace(/\[[^\[]*\]$/, "");
try {
var baseobj = eval("obj" + basefield);
conflict = newid in baseobj;
} catch (ee) {
// 理论上这里不会发生错误
printe(ee);
throw ee;
}
if (conflict) {
printe("id已存在, 请直接修改该项的值");
return;
}
// 3.添加
editor_mode.addAction(["add", basefield + "['" + newid + "']", null]);
editor_mode.onmode("save", function () {
printf("添加成功,刷新后生效;也可以继续新增其他项目。");
}); //自动保存 删掉此行的话点保存按钮才会保存
};
//對表格的存讀
editor.file.editCommonEventTemplate = function (actionList, callback) {
/*actionList:[
["change","['test']",['123']],
]
为[]时只查询不修改
*/
var data_obj =
events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate;
checkCallback(callback);
if (isset(actionList) && actionList.length > 0) {
actionList.forEach(function (value) {
value[1] = "['CommonEventTemplate']" + value[1];
});
editor.file.saveSetting("events", actionList, function (err) {
callback([err]);
});
} else {
callback([
Object.assign({}, data_obj),
editor.file.CommonEventTemplate_comment._data.CommonEventTemplate,
null,
]);
}
};
})();
//複寫事件編輯器(editor_blocklyconfig)
editor_blocklyconfig = function () {
// start mark sfergsvae
(function () {
var getCategory = function (name, custom) {
for (var node of document.getElementById("toolbox").children) {
if (node.getAttribute("name") == name) return node;
}
var node = document.createElement("category");
node.setAttribute("name", name);
if (custom) node.setAttribute("custom", custom);
document.getElementById("toolbox").appendChild(node);
return node;
};
var toolboxObj = {
入口方块: [
'<label text="入口方块会根据当前类型在此数组中筛选,具体控制在editor_blockly.entranceCategoryCallback中"></label>',
MotaActionFunctions.actionParser.parse(
[
"欢迎使用事件编辑器",
"本事件触发一次后会消失",
{ type: "hide", time: 500 },
],
"event"
),
MotaActionFunctions.actionParser.parse(
{
condition: "flag:__door__===2",
currentFloor: true,
priority: 0,
delayExecute: false,
multiExecute: false,
data: [{ type: "openDoor", loc: [10, 5] }],
},
"autoEvent"
),
MotaActionBlocks["changeFloor_m"].xmlText(),
MotaActionFunctions.actionParser.parse(
[
{
id: "shop1",
text: "\t[贪婪之神,moneyShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:",
textInList: "1F金币商店",
choices: [
{
text: "生命+800",
need: "status:money>=20+2*flag:shop1",
action: [
{
type: "comment",
text: "新版商店中需要手动扣减金币和增加访问次数",
},
{
type: "setValue",
name: "status:money",
operator: "-=",
value: "20+2*flag:shop1",
},
{
type: "setValue",
name: "flag:shop1",
operator: "+=",
value: "1",
},
{
type: "setValue",
name: "status:hp",
operator: "+=",
value: "800",
},
],
},
],
},
{
id: "itemShop",
item: true,
textInList: "道具商店",
choices: [{ id: "yellowKey", number: 10, money: 10 }],
},
{
id: "keyShop1",
textInList: "回收钥匙商店",
commonEvent: "回收钥匙商店",
args: "",
},
],
"shop"
),
MotaActionBlocks["common_m"].xmlText(),
MotaActionBlocks["beforeBattle_m"].xmlText(),
MotaActionBlocks["afterBattle_m"].xmlText(),
MotaActionBlocks["afterGetItem_m"].xmlText(),
MotaActionBlocks["afterOpenDoor_m"].xmlText(),
MotaActionBlocks["firstArrive_m"].xmlText(),
MotaActionBlocks["eachArrive_m"].xmlText(),
MotaActionBlocks["level_m"].xmlText(),
MotaActionFunctions.actionParser.parse(
[["MTx", ""]],
"floorPartition"
),
MotaActionBlocks["commonEvent_m"].xmlText(),
MotaActionBlocks["item_m"].xmlText(),
MotaActionFunctions.actionParser.parse(
[
{
title: "简单",
name: "Easy",
hard: 1,
action: [
{ type: "comment", text: "在这里写该难度需执行的事件" },
],
},
],
"levelChoose"
),
MotaActionFunctions.actionParser.parse(
{
type: 0,
value: { atk: 10 },
percentage: { speed: 10 },
},
"equip"
),
MotaActionFunctions.actionParser.parse(
[
{
name: "bg.webp",
x: 0,
y: 0,
canvas: "bg",
},
],
"floorImage"
),
MotaActionFunctions.actionParser.parse(
{
time: 160,
openSound: "door.opus",
closeSound: "door.opus",
keys: { yellowKey: 1, orangeKey: 1 },
},
"doorInfo"
),
MotaActionBlocks["faceIds_m"].xmlText(),
MotaActionBlocks["mainStyle_m"].xmlText(),
MotaActionFunctions.actionParser.parse(
{
背景音乐: "bgm.opus",
确定: "confirm.opus",
攻击: "attack.opus",
背景图: "bg.webp",
领域: "zone",
文件名: "file.jpg",
},
"nameMap"
),
MotaActionFunctions.actionParser.parse(
[{ name: "hero.webp", width: 32, height: 32, prefix: "hero_" }],
"splitImages"
),
],
显示文字: [
MotaActionBlocks["text_0_s"].xmlText(),
MotaActionBlocks["text_1_s"].xmlText(),
MotaActionFunctions.actionParser.parseList(
"\t[小妖精,fairy]\f[fairy.webp,0,0]欢迎使用事件编辑器(双击方块可直接预览)"
),
MotaActionBlocks["over_s"].xmlText(),
MotaActionFunctions.actionParser.parseList([
{
type: "overlist",
image: "bg_5043.webp",
memory: false,
hidetime: 30,
list: [
{
text: "",
sound: "",
time: 50,
textColor: "255,255,255,1",
boldColor: "0,0,0,1",
font: "bold 48px Verdana",
frame: 0,
},
],
},
]),
MotaActionBlocks["cgtextList_s"].xmlText(),
MotaActionFunctions.actionParser.parseList([
{
type: "cgtext",
bg: "bg_5043.webp",
memory: false,
WindowSkin: false,
index: 0,
head: { name: "face_050445.webp", px: -300 },
time: 0,
wait: 2000,
sound: "",
bodyList: [
{ name: "tati_050145a.webp", px: 100, filter: false },
],
},
]),
MotaActionBlocks["moveTextBox_s"].xmlText(),
MotaActionBlocks["clearTextBox_s"].xmlText(),
MotaActionBlocks["comment_s"].xmlText(),
MotaActionBlocks["autoText_s"].xmlText(),
MotaActionBlocks["scrollText_s"].xmlText(),
MotaActionBlocks["setText_s"].xmlText(),
MotaActionBlocks["tip_s"].xmlText(),
MotaActionBlocks["addPop_s"].xmlText(),
MotaActionBlocks["confirm_s"].xmlText(),
MotaActionBlocks["choices_s"].xmlText([
"选择剑或者盾",
"流浪者",
"man",
0,
"",
MotaActionBlocks["choicesContext"].xmlText([
"剑",
"",
"",
null,
"",
"",
MotaActionFunctions.actionParser.parseList([
{ type: "openDoor", loc: [3, 3] },
]),
]),
]),
MotaActionBlocks["win_s"].xmlText(),
MotaActionBlocks["lose_s"].xmlText(),
MotaActionBlocks["restart_s"].xmlText(),
],
数据相关: [
MotaActionBlocks["setValue_s"].xmlText([
MotaActionBlocks["idIdList_e"].xmlText(["status", "生命"]),
"=",
"",
false,
]),
MotaActionBlocks["setEnemy_s"].xmlText(),
MotaActionBlocks["setEnemyOnPoint_s"].xmlText(),
MotaActionBlocks["resetEnemyOnPoint_s"].xmlText(),
MotaActionBlocks["moveEnemyOnPoint_s"].xmlText(),
MotaActionBlocks["moveEnemyOnPoint_1_s"].xmlText(),
MotaActionBlocks["setEquip_s"].xmlText(),
MotaActionBlocks["setFloor_s"].xmlText(),
MotaActionBlocks["setGlobalAttribute_s"].xmlText(),
MotaActionBlocks["setGlobalValue_s"].xmlText(),
MotaActionBlocks["setGlobalFlag_s"].xmlText(),
MotaActionBlocks["setNameMap_s"].xmlText(),
MotaActionBlocks["input_s"].xmlText(),
MotaActionBlocks["input2_s"].xmlText(),
MotaActionBlocks["update_s"].xmlText(),
MotaActionBlocks["moveAction_s"].xmlText(),
MotaActionBlocks["changeFloor_s"].xmlText(),
MotaActionBlocks["changePos_s"].xmlText(),
MotaActionBlocks["battle_s"].xmlText(),
MotaActionBlocks["useItem_s"].xmlText(),
MotaActionBlocks["loadEquip_s"].xmlText(),
MotaActionBlocks["unloadEquip_s"].xmlText(),
MotaActionBlocks["openShop_s"].xmlText(),
MotaActionBlocks["disableShop_s"].xmlText(),
MotaActionBlocks["setHeroIcon_s"].xmlText(),
MotaActionBlocks["follow_s"].xmlText(),
MotaActionBlocks["unfollow_s"].xmlText(),
],
地图处理: [
MotaActionBlocks["battle_1_s"].xmlText(),
MotaActionBlocks["openDoor_s"].xmlText(),
MotaActionBlocks["closeDoor_s"].xmlText(),
MotaActionBlocks["show_s"].xmlText(),
MotaActionBlocks["hide_s"].xmlText(),
MotaActionBlocks["setBlock_s"].xmlText(),
MotaActionBlocks["setBlockOpacity_s"].xmlText(),
MotaActionBlocks["setBlockFilter_s"].xmlText(),
MotaActionBlocks["turnBlock_s"].xmlText(),
MotaActionBlocks["moveHero_s"].xmlText(),
MotaActionBlocks["move_s"].xmlText(),
MotaActionBlocks["jumpHero_s"].xmlText(),
MotaActionBlocks["jumpHero_1_s"].xmlText(),
MotaActionBlocks["jump_s"].xmlText(),
MotaActionBlocks["jump_1_s"].xmlText(),
MotaActionBlocks["showBgFgMap_s"].xmlText(),
MotaActionBlocks["hideBgFgMap_s"].xmlText(),
MotaActionBlocks["setBgFgBlock_s"].xmlText(),
MotaActionBlocks["showFloorImg_s"].xmlText(),
MotaActionBlocks["hideFloorImg_s"].xmlText(),
],
事件控制: [
MotaActionBlocks["if_1_s"].xmlText(),
MotaActionBlocks["if_s"].xmlText(),
MotaActionFunctions.actionParser.parseList({
type: "switch",
condition: "判别值",
caseList: [
{
action: [
{ type: "comment", text: "当判别值是值的场合执行此事件" },
],
},
{
case: "default",
action: [
{
type: "comment",
text: "当没有符合的值的场合执行default事件",
},
],
},
],
}),
MotaActionFunctions.actionParser.parseList({
type: "for",
name: "temp:A",
from: "0",
to: "12",
step: "1",
data: [],
}),
MotaActionFunctions.actionParser.parseList({
type: "forEach",
name: "temp:A",
list: ["status:atk", "status:def"],
data: [],
}),
MotaActionBlocks["while_s"].xmlText(),
MotaActionBlocks["dowhile_s"].xmlText(),
MotaActionBlocks["break_s"].xmlText(),
MotaActionBlocks["continue_s"].xmlText(),
MotaActionBlocks["exit_s"].xmlText(),
MotaActionBlocks["trigger_s"].xmlText(),
MotaActionBlocks["insert_1_s"].xmlText(),
MotaActionBlocks["insert_2_s"].xmlText(),
],
特效表现: [
MotaActionBlocks["sleep_s"].xmlText(),
MotaActionBlocks["setq_s"].xmlText(),
MotaActionBlocks["setcgs_s"].xmlText(),
MotaActionBlocks["setmusics_s"].xmlText(),
MotaActionBlocks["changebg_s"].xmlText(),
MotaActionFunctions.actionParser.parseList({
type: "wait",
timeout: 0,
data: [
{
case: "keyboard",
keycode: "13,32",
action: [
{
type: "comment",
text: "当按下回车(keycode=13)或空格(keycode=32)时执行此事件\n超时剩余时间会写入flag:timeout",
},
],
},
{
case: "mouse",
px: [0, 32],
py: [0, 32],
action: [
{
type: "comment",
text: "当点击地图左上角时执行此事件\n超时剩余时间会写入flag:timeout",
},
],
},
{
case: "condition",
condition: "flag:type==0\n&&flag:keycode==13",
action: [
{
type: "comment",
text: "当满足自定义条件时会执行此事件\n超时剩余时间会写入flag:timeout",
},
],
},
{
case: "timeout",
action: [
{ type: "comment", text: "当超时未操作时执行此事件" },
],
},
],
}),
MotaActionBlocks["waitAsync_s"].xmlText(),
MotaActionBlocks["stopAsync_s"].xmlText(),
MotaActionBlocks["op_s"].xmlText(),
MotaActionBlocks["drawWarning_s"].xmlText(),
MotaActionBlocks["changeMouse_s"].xmlText(),
MotaActionBlocks["removeMouse_s"].xmlText(),
MotaActionBlocks["vibrate_s"].xmlText(),
MotaActionBlocks["animate_s"].xmlText(),
MotaActionBlocks["animate_1_s"].xmlText(),
MotaActionBlocks["stopAnimate_s"].xmlText(),
MotaActionBlocks["setViewport_s"].xmlText(),
MotaActionBlocks["setViewport_1_s"].xmlText(),
MotaActionBlocks["lockViewport_s"].xmlText(),
MotaActionBlocks["showStatusBar_s"].xmlText(),
MotaActionBlocks["hideStatusBar_s"].xmlText(),
MotaActionBlocks["setHeroOpacity_s"].xmlText(),
MotaActionBlocks["setCurtain_0_s"].xmlText(),
MotaActionBlocks["setCurtain_1_s"].xmlText(),
MotaActionBlocks["screenFlash_s"].xmlText(),
MotaActionBlocks["setWeather_s"].xmlText(),
MotaActionBlocks["callBook_s"].xmlText(),
MotaActionBlocks["callSave_s"].xmlText(),
MotaActionBlocks["autoSave_s"].xmlText(),
MotaActionBlocks["forbidSave_s"].xmlText(),
MotaActionBlocks["callLoad_s"].xmlText(),
],
音像处理: [
MotaActionBlocks["animationDrawable_s"].xmlText(),
MotaActionBlocks["introAndLoop_s"].xmlText(),
MotaActionBlocks["setanimate_s"].xmlText(),
MotaActionBlocks["deleteanimate_s"].xmlText(),
MotaActionBlocks["playanimate_s"].xmlText(),
MotaActionBlocks["clearanimate_s"].xmlText(),
MotaActionBlocks["showImage_s"].xmlText(),
MotaActionBlocks["showImage_1_s"].xmlText(),
MotaActionBlocks["hideImage_s"].xmlText(),
MotaActionBlocks["showTextImage_s"].xmlText(),
MotaActionBlocks["moveImage_s"].xmlText(),
MotaActionBlocks["rotateImage_s"].xmlText(),
MotaActionBlocks["scaleImage_s"].xmlText(),
MotaActionBlocks["showGif_s"].xmlText(),
MotaActionBlocks["playBgm_s"].xmlText(),
MotaActionBlocks["pauseBgm_s"].xmlText(),
MotaActionBlocks["resumeBgm_s"].xmlText(),
MotaActionBlocks["loadBgm_s"].xmlText(),
MotaActionBlocks["freeBgm_s"].xmlText(),
MotaActionBlocks["playSound_s"].xmlText(),
MotaActionBlocks["playSound_1_s"].xmlText(),
MotaActionBlocks["stopSound_s"].xmlText(),
MotaActionBlocks["setVolume_s"].xmlText(),
MotaActionBlocks["setBgmSpeed_s"].xmlText(),
],
UI绘制: [
MotaActionBlocks["previewUI_s"].xmlText(),
MotaActionBlocks["clearMap_s"].xmlText(),
MotaActionBlocks["setAttribute_s"].xmlText(),
MotaActionBlocks["setFilter_s"].xmlText(),
MotaActionBlocks["fillText_s"].xmlText(),
MotaActionBlocks["fillBoldText_s"].xmlText(),
MotaActionBlocks["drawTextContent_s"].xmlText(),
MotaActionBlocks["fillRect_s"].xmlText(),
MotaActionBlocks["strokeRect_s"].xmlText(),
MotaActionBlocks["drawLine_s"].xmlText(),
MotaActionBlocks["drawArrow_s"].xmlText(),
MotaActionBlocks["fillPolygon_s"].xmlText(),
MotaActionBlocks["strokePolygon_s"].xmlText(),
MotaActionBlocks["fillEllipse_s"].xmlText(),
MotaActionBlocks["strokeEllipse_s"].xmlText(),
MotaActionBlocks["fillArc_s"].xmlText(),
MotaActionBlocks["strokeArc_s"].xmlText(),
MotaActionBlocks["drawImage_s"].xmlText(),
MotaActionBlocks["drawImage_1_s"].xmlText(),
MotaActionBlocks["drawIcon_s"].xmlText(),
MotaActionBlocks["drawBackground_s"].xmlText(),
MotaActionBlocks["drawSelector_s"].xmlText(),
MotaActionBlocks["drawSelector_1_s"].xmlText(),
],
原生脚本: [
MotaActionBlocks["function_s"].xmlText(),
MotaActionBlocks["unknown_s"].xmlText(),
],
值块: [
MotaActionBlocks["setValue_s"].xmlText([
MotaActionBlocks["idIdList_e"].xmlText(["status", "生命"]),
"=",
"",
false,
]),
MotaActionBlocks["expression_arithmetic_0"].xmlText(),
MotaActionBlocks["idFlag_e"].xmlText(),
MotaActionBlocks["idTemp_e"].xmlText(),
MotaActionBlocks["negate_e"].xmlText(),
MotaActionBlocks["unaryOperation_e"].xmlText(),
MotaActionBlocks["bool_e"].xmlText(),
MotaActionBlocks["idString_e"].xmlText(),
MotaActionBlocks["idIdList_e"].xmlText(),
MotaActionBlocks["idFixedList_e"].xmlText(),
MotaActionBlocks["enemyattr_e"].xmlText(),
MotaActionBlocks["blockId_e"].xmlText(),
MotaActionBlocks["blockNumber_e"].xmlText(),
MotaActionBlocks["blockCls_e"].xmlText(),
MotaActionBlocks["hasEquip_e"].xmlText(),
MotaActionBlocks["equip_e"].xmlText(),
MotaActionBlocks["nextXY_e"].xmlText(),
MotaActionBlocks["isReplaying_e"].xmlText(),
MotaActionBlocks["hasVisitedFloor_e"].xmlText(),
MotaActionBlocks["isShopVisited_e"].xmlText(),
MotaActionBlocks["canBattle_e"].xmlText(),
MotaActionBlocks["damage_e"].xmlText(),
MotaActionBlocks["damage_1_e"].xmlText(),
MotaActionBlocks["rand_e"].xmlText(),
MotaActionBlocks["evalString_e"].xmlText(),
],
常见事件模板: [
'<label text="此处只是占位符,实际定义在#region 動態常見事件模板"></label>',
],
最近使用事件: [
'<label text="此处只是占位符,实际定义在editor_blockly.searchBlockCategoryCallback中"></label>',
],
};
var toolboxgap = '<sep gap="5"></sep>';
//xml_text = MotaActionFunctions.actionParser.parse(obj,type||'event')
//MotaActionBlocks['idString_e'].xmlText()
//#region 動態常見事件模板
let CommonEventTemplateHTML = [];
for (let commonEventName in events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate) {
if (
events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate.hasOwnProperty(
commonEventName
)
) {
let actionParserJson = Array.from(
events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate[
commonEventName
] ?? []
);
let labelHTML = "";
let blockHTML = "";
labelHTML = `<label text="${commonEventName}"></label>`;
if (actionParserJson.length > 1) {
actionParserJson = {
type: "if",
condition: "true",
true: actionParserJson,
};
} else if (actionParserJson.length < 1) {
actionParserJson = [
"空的常用事件模板。\n請在主頁下拉菜單中選擇常用事件模板進行編輯。\n編輯後需按F5刷新事件編輯器。",
];
}
blockHTML =
MotaActionFunctions.actionParser.parseList(actionParserJson);
CommonEventTemplateHTML.push(labelHTML);
CommonEventTemplateHTML.push(blockHTML);
}
}
toolboxObj["常见事件模板"] = CommonEventTemplateHTML;
//#endregion
for (var name in toolboxObj) {
var custom = null;
if (name == "最近使用事件") custom = "searchBlockCategory";
if (name == "入口方块") custom = "entranceCategory";
getCategory(name, custom).innerHTML =
toolboxObj[name].join(toolboxgap);
}
var blocklyArea = document.getElementById("blocklyArea");
var blocklyDiv = document.getElementById("blocklyDiv");
var workspace = Blockly.inject(blocklyDiv, {
media: "_server/blockly/media/",
toolbox: document.getElementById("toolbox"),
zoom: {
controls: true,
wheel: false, //滚轮改为上下(shift:左右)翻滚
startScale: 1.0,
maxScale: 3,
minScale: 0.3,
scaleSpeed: 1.08,
},
trashcan: false,
});
editor_blockly.isCommonEntry = function () {
var commonEntries = [
"beforeBattle",
"afterBattle",
"afterOpenDoor",
"firstArrive",
"eachArrive",
"commonEvent",
"item",
];
return commonEntries.indexOf(editor_blockly.entryType) >= 0;
};
editor_blockly.entranceCategoryCallback = function (workspace) {
var list = toolboxObj["入口方块"];
var xmlList = [];
var eventType =
(editor_blockly.isCommonEntry()
? "common"
: editor_blockly.entryType) + "_m";
for (var ii = 0, blockText; (blockText = list[ii]); ii++) {
if (
new RegExp('<block type="' + eventType + '">').exec(blockText)
) {
var block = Blockly.Xml.textToDom(
"<xml>" + blockText + "</xml>"
).firstChild;
block.setAttribute("gap", 5);
xmlList.push(block);
}
}
return xmlList;
};
workspace.registerToolboxCategoryCallback(
"entranceCategory",
editor_blockly.entranceCategoryCallback
);
editor_blockly.searchBlockCategoryCallback = function (workspace) {
var xmlList = [];
var labels = editor_blockly.searchBlock();
for (var i = 0; i < labels.length; i++) {
var blockText =
"<xml>" + MotaActionBlocks[labels[i]].xmlText() + "</xml>";
var block = Blockly.Xml.textToDom(blockText).firstChild;
block.setAttribute("gap", 5);
xmlList.push(block);
}
return xmlList;
};
workspace.registerToolboxCategoryCallback(
"searchBlockCategory",
editor_blockly.searchBlockCategoryCallback
);
var onresize = function (e) {
blocklyDiv.style.width = blocklyArea.offsetWidth + "px";
blocklyDiv.style.height = blocklyArea.offsetHeight + "px";
Blockly.svgResize(workspace);
};
if (typeof editor !== "undefined" && !editor.isMobile)
window.addEventListener("resize", onresize, false);
onresize();
//Blockly.svgResize(workspace);
//Blockly.bindEventWithChecks_(workspace.svgGroup_,"wheel",workspace,function(e){});
document.getElementById("blocklyDiv").onmousewheel = function (e) {
//console.log(e);
e.preventDefault();
var hvScroll = e.shiftKey ? "hScroll" : "vScroll";
var mousewheelOffsetValue =
(20 / 380) * workspace.scrollbar[hvScroll].handleLength_ * 3;
workspace.scrollbar[hvScroll].handlePosition_ +=
(e.deltaY || 0) + (e.detail || 0) > 0
? mousewheelOffsetValue
: -mousewheelOffsetValue;
workspace.scrollbar[hvScroll].onScroll_();
// workspace.setScale(workspace.scale);
};
var doubleClickCheck = [[0, "abc"]];
function omitedcheckUpdateFunction(event) {
if (event.type === "create") {
editor_blockly.addIntoLastUsedType(event.blockId);
}
if (event.type === "ui" && event.element == "click") {
var newClick = [new Date().getTime(), event.blockId];
var lastClick = doubleClickCheck.shift();
doubleClickCheck.push(newClick);
if (newClick[0] - lastClick[0] < 500) {
if (newClick[1] === lastClick[1]) {
editor_blockly.doubleClickBlock(newClick[1]);
}
}
}
// Only handle these events
if (["create", "move", "change", "delete"].indexOf(event.type) < 0)
return;
if (editor_blockly.workspace.topBlocks_.length >= 2) {
editor_blockly.setValue("入口方块只能有一个");
return;
}
var eventType = editor_blockly.entryType;
if (editor_blockly.workspace.topBlocks_.length == 1) {
var blockType = editor_blockly.workspace.topBlocks_[0].type;
if (
blockType !== eventType + "_m" &&
!(editor_blockly.isCommonEntry() && blockType == "common_m")
) {
editor_blockly.setValue("入口方块类型错误");
return;
}
}
try {
var code = Blockly.JavaScript.workspaceToCode(workspace).replace(
/\\(i|c|d|e|g|z)/g,
"\\\\$1"
);
editor_blockly.setValue(code);
} catch (error) {
editor_blockly.setValue(String(error));
if (error instanceof OmitedError) {
var blockName = error.blockName;
var varName = error.varName;
var block = error.block;
}
// console.log(error);
}
}
workspace.addChangeListener(omitedcheckUpdateFunction);
workspace.addChangeListener(Blockly.Events.disableOrphans);
editor_blockly.workspace = workspace;
MotaActionFunctions.workspace = function () {
return editor_blockly.workspace;
};
// 因为在editor_blockly.parse里已经HTML转义过一次了,所以这里要覆盖掉以避免在注释中出现<等
MotaActionFunctions.xmlText = function (
ruleName,
inputs,
isShadow,
comment,
collapsed,
disabled
) {
var rule = MotaActionBlocks[ruleName];
var blocktext = isShadow ? "shadow" : "block";
var xmlText = [];
xmlText.push(
"<" +
blocktext +
' type="' +
ruleName +
'"' +
(collapsed ? ' collapsed="true"' : "") +
(disabled ? ' disabled="true"' : "") +
">"
);
if (!inputs) inputs = [];
for (var ii = 0, inputType; (inputType = rule.argsType[ii]); ii++) {
var input = inputs[ii];
var _input = "";
var noinput = input === null || input === undefined;
if (
noinput &&
inputType === "field" &&
MotaActionBlocks[rule.argsGrammarName[ii]].type !==
"field_dropdown"
)
continue;
if (noinput && inputType === "field") {
noinput = false;
input = rule.fieldDefault(rule.args[ii]);
}
if (noinput) input = "";
if (
inputType === "field" &&
MotaActionBlocks[rule.argsGrammarName[ii]].type ===
"field_checkbox"
)
input = input ? "TRUE" : "FALSE";
if (inputType !== "field") {
var subList = false;
var subrulename = rule.argsGrammarName[ii];
var subrule = MotaActionBlocks[subrulename];
if (subrule instanceof Array) {
subrulename = subrule[subrule.length - 1];
subrule = MotaActionBlocks[subrulename];
subList = true;
}
_input = subrule.xmlText([], true);
if (noinput && !subList && !isShadow) {
//无输入的默认行为是: 如果语句块的备选方块只有一个,直接代入方块
input = subrule.xmlText();
}
}
xmlText.push("<" + inputType + ' name="' + rule.args[ii] + '">');
xmlText.push(_input + input);
xmlText.push("</" + inputType + ">");
}
if (comment) {
xmlText.push("<comment>");
xmlText.push(comment);
xmlText.push("</comment>");
}
var next = inputs[rule.args.length];
if (next) {
//next
xmlText.push("<next>");
xmlText.push(next);
xmlText.push("</next>");
}
xmlText.push("</" + blocktext + ">");
return xmlText.join("");
};
})();
// end mark sfergsvae
}
.toString()
.split("// start mark sfergsvae")[1]
.split("// end mark sfergsvae")[0];
}
},
"夹击激光动画": function () {
function createCanvas(name, zIndex) {
if (!name) return;
var canvas = document.createElement("canvas");
canvas.id = name;
canvas.className = "gameCanvas";
// 将图层插入进游戏内容
document.getElementById("gameDraw").appendChild(canvas);
canvas.style.zIndex = zIndex || 0;
var ctx = canvas.getContext("2d");
core.canvas[name] = ctx;
canvas.width = core.__PIXELS__;
canvas.height = core.__PIXELS__;
return canvas;
}
var bg3Canvas = createCanvas("bg3", 25);
if (main.mode == "editor") {
// 与编辑器显伤的神秘联动(((
editor.dom.mapEdit.insertBefore(bg3Canvas, core.canvas.event.canvas);
bg3Canvas.style.zIndex = null;
}
core.bigmap.canvas = ["bg", "bg3", "event", "event2", "fg", "damage"];
core.plugin._betCanvas = "bg3";
this._drawBetweenAttack = function (x, y, pos, frame) {
var ctx = core.plugin._betCanvas;
var w = 32,
h = 68,
ix = x * 32,
iy = y * 32;
// 左右夹击
if (pos[0])
core.drawImage(
ctx,
"light.webp",
32 * (frame - 1),
0,
32,
68,
ix,
iy - 18,
w,
h,
(90 * Math.PI) / 180
);
// 上下夹击
if (pos[1])
core.drawImage(
ctx,
"light.webp",
32 * (frame - 1),
0,
32,
68,
ix,
iy - 18,
w,
h
);
};
core.registerAnimationFrame("betweenAttack", true, function (timestamp) {
if (!flags.betweenAttackData) {
core.clearMap(
core.plugin._betCanvas,
0,
0,
core.__PIXELS__,
core.__PIXELS__
);
return;
}
var time = core.events._timestamp;
if (time && timestamp - time < 400) return;
core.clearMap(
core.plugin._betCanvas,
0,
0,
core.__PIXELS__,
core.__PIXELS__
);
core.events._timestamp = timestamp;
var data = flags.betweenAttackData || {};
flags._frame = flags._frame || 1;
var frame = flags._frame;
for (var loc in data) {
var l = loc.split(",");
var x = parseInt(l[0]),
y = parseInt(l[1]);
core.plugin._drawBetweenAttack(x, y, data[loc], frame);
}
flags._frame = frame + 1;
if (flags._frame > 4) flags._frame = 1;
});
var origin_extraDamage = core.control._updateDamage_extraDamage;
core.control._updateDamage_extraDamage = function (floorId, onMap) {
flags.betweenAttackData = null;
if (!flags.useBetweenLight)
return origin_extraDamage.call(core.control, floorId, onMap);
else {
core.status.damage.extraData = [];
if (!core.flags.displayExtraDamage) return;
var width = core.floors[floorId].width,
height = core.floors[floorId].height;
var startX =
onMap && core.bigmap.v2
? Math.max(0, core.bigmap.posX - core.bigmap.extend)
: 0;
var endX =
onMap && core.bigmap.v2
? Math.min(
width,
core.bigmap.posX + core.__SIZE__ + core.bigmap.extend + 1
)
: width;
var startY =
onMap && core.bigmap.v2
? Math.max(0, core.bigmap.posY - core.bigmap.extend)
: 0;
var endY =
onMap && core.bigmap.v2
? Math.min(
height,
core.bigmap.posY + core.__SIZE__ + core.bigmap.extend + 1
)
: height;
const find = function (x, y) {
return core.status.damage.extraData.find(function (data) {
return data.x == x && data.y == y;
});
};
for (var x = startX; x < endX; x++) {
for (var y = startY; y < endY; y++) {
var alpha = 1;
if (core.noPass(x, y, floorId)) {
if (core.flags.extraDamageType == 2) alpha = 0;
else if (core.flags.extraDamageType == 1) alpha = 0.6;
}
var loc = x + "," + y;
var damage = core.status.checkBlock.damage[loc] || 0;
var getEnemy = function (x, y) {
var id = core.getBlockId(x, y, floorId);
var e = core.material.enemys[id];
if (main.mode == "editor") e = core.enemys.enemys[id];
return e;
};
if (damage > 0) {
// 该点伤害
damage = core.formatBigNumber(damage, true);
var left = false,
top = false;
var e_left = getEnemy(x - 1, y),
e_right = getEnemy(x + 1, y);
var e_bottom = getEnemy(x, y - 1),
e_top = getEnemy(x, y + 1);
if (
core.hasSpecial(e_left, 16) &&
core.hasSpecial(e_right, 16) &&
e_left.id == e_right.id
)
left = true;
if (
core.hasSpecial(e_bottom, 16) &&
core.hasSpecial(e_top, 16) &&
e_bottom.id == e_top.id
)
top = true;
flags.betweenAttackData = flags.betweenAttackData || {};
if (flags.betweenAttackData[x + "," + y]) continue;
var data = [left, top];
let [px, py] = [32 * x + 16, 32 * (y + 1) - 14];
if (left || top) {
px += 15;
py -= 10;
flags.betweenAttackData[x + "," + y] = data;
}
core.plugin._drawBetweenAttack(x, y, data, 1);
if (left) {
if (!find(x - 1, y))
core.status.damage.extraData.push({
x: x,
y: y,
text: damage,
px: 32 * x + 16,
py: 32 * (y + 1) - 14,
color: "#ffaa33",
alpha: alpha,
});
} else if (top) {
if (!find(x, y - 1))
core.status.damage.extraData.push({
x: x,
y: y,
text: damage,
px: 32 * x + 16,
py: 32 * (y + 1) - 14,
color: "#ffaa33",
alpha: alpha,
});
} else {
core.status.damage.extraData.push({
text: damage,
px: 32 * x + 16,
py: 32 * (y + 1) - 14,
color: "#ffaa33",
alpha: alpha,
});
}
} else {
// 检查捕捉
if (core.status.checkBlock.ambush[x + "," + y]) {
core.status.damage.extraData.push({
text: "!",
px: 32 * x + 16,
py: 32 * (y + 1) - 14,
color: "#ffaa33",
alpha: alpha,
});
}
}
}
}
}
};
},
"瞬移轨迹": function () {
// 在此增加新插件
function popMove() {
var ctx = core.getContextByName("popMove");
if (!ctx)
ctx = core.createCanvas(
"popMove",
0,
0,
core.__PIXELS__,
core.__PIXELS__,
71
);
ctx.canvas.classList.add("gameCanvas", "anti-aliasing");
core.clearMap(ctx);
if (core.status.replay.speed <= 3 && !flags.stopPop) {
var list = core.status.popMove || [];
var count = 0;
list.forEach(function (one) {
// 由frame计算出dy
one.frame++;
// 绘制
if (one.frame >= 0) core.setAlpha(ctx, 1 - one.frame / 30);
else core.setAlpha(ctx, 1);
var length = Math.sqrt(
Math.pow(one.px2 - one.px, 2) + Math.pow(one.py2 - one.py, 2)
);
//console.log(length)
var li = length / 16;
var lx = (one.px2 - one.px) / li;
var ly = (one.py2 - one.py) / li;
for (var i = 0; i < li; i += 1) {
core.setAlpha(ctx, (1 - one.frame / 30) * ((i / li) * 0.8 + 0.2));
core.drawLine(
"popMove",
one.px + i * lx,
one.py + i * ly,
one.px + (i + 0.5) * lx,
one.py + (i + 0.5) * ly,
"#E1E1E1",
6
);
if (i == 0)
core.strokeCircle("popMove", one.px, one.py, 6, "#E1E1E1", 3);
}
core.strokeCircle("popMove", one.px2, one.py2, 6, "#E1E1E1", 3);
core.strokeCircle(
"popMove",
one.px2,
one.py2,
6 + (16 * one.frame) / 30,
"#E1E1E1",
6 * (1 - one.frame / 30)
);
//core.drawLine('popMove', one.px, one.py, one.px2, one.py2, '#E1E1E1', 6);
if (one.frame >= 30) count++;
});
if (count > 0) list.splice(0, count);
}
}
if (!main.replayChecking) {
core.registerAnimationFrame("popMove", true, popMove);
}
this.addPopMove = function (px, py, px2, py2, frame) {
var data = { px: px, py: py, px2: px2, py2: py2, frame: frame || 0 };
if (core.status.replay.speed <= 3) {
if (!core.status.popMove) core.status.popMove = [data];
else core.status.popMove.push(data);
}
};
},
"墓碑(编辑器)": function () {
// 在此增加新插件
if (main.mode != "editor") return; // 编辑器模式下使用
var mapData = null;
var drawMap = core.maps._drawMap_drawAll;
core.maps._drawMap_drawAll = function (floorId) {
floorId = floorId || core.status.floorId;
if (!main.useCompress) {
core.getLocalForage(
"autoSave",
{ maps: [] },
function (v) {
mapData = v.maps[floorId]?.map;
drawMap.call(core.maps, floorId);
},
function (e) {
console.log(e);
}
);
}
};
maps.prototype._drawBlockInfo = function (blockInfo, x, y) {
var alpha = 1.0;
if (mapData && !!mapData[y] && mapData[y][x] == 0) {
core.setAlpha("event", 0.5);
core.setAlpha("event2", 0.5);
}
var image = blockInfo.image,
posX = blockInfo.posX,
posY = blockInfo.posY,
height = blockInfo.height;
core.clearMap("event", x * 32, y * 32, 32, 32);
if (blockInfo.bigImage) {
this._drawBlockInfo_bigImage(blockInfo, x, y, "event");
core.setAlpha("event", alpha);
core.setAlpha("event2", alpha);
return;
}
core.drawImage(
"event",
image,
posX * 32,
posY * height + height - 32,
32,
32,
x * 32,
y * 32,
32,
32
);
if (height > 32) {
core.clearMap("event2", x * 32, y * 32 + 32 - height, 32, height - 32);
core.drawImage(
"event2",
image,
posX * 32,
posY * height,
32,
height - 32,
x * 32,
y * 32 + 32 - height,
32,
height - 32
);
}
core.setAlpha("event", alpha);
core.setAlpha("event2", alpha);
};
},
"小地图": function () {
// 在此增加新插件
// ----- 不可自定义 杂七杂八的变量
/** @type {{[x: string]: BFSResult}} */
let mapCache = {}; // 地图缓存
let drawCache = {}; // 绘制信息缓存
let status = "none"; // 当前的绘制状态
/** @type {{[x: string]: Sprite}} */
let sprites = {}; // 当前所有的sprite
/** @type {{[x: string]: Sprite}} */
let canDrag = {}; // 可以拖拽的sprite
/** @type {{[x: string]: Button}} */
let areaSprite = {}; // 区域列表对应的sprite
let clicking = false; // 是否正在点击,用于拖拽判定
let drawingMap = ""; // 正在绘制的中心楼层
let nowScale = 0; // 当前绘制的放缩比例
let lastTouch = {}; // 上一次的单点点击信息
let lastLength = 0; // 手机端缩放时上一次的两指间距离
let nowDepth = 0; // 当前的遍历深度
let drawedThumbnail = {}; // 已经绘制过的缩略图
let moved = false; // 鼠标按下后是否移动了
let noBorder = false; // 是否是无边框拼接模式
let lastScale = 0; // 上一次缩放,用于优化缩略图绘制
let areaPage = 0; // 区域显示的当前页数
let nowArea = 0; // 当前区域index
let selecting = ""; // 选择时当前正在选择的地图
// ---- 不可自定义,常量
/** @type {Area} */
let areas = []; // 区域信息
const perPage = Math.floor((core._PY_ - 60) / 30); // 区域的每页显示数量
// ---- 可自定义默认的切换地图的图块id
const defaultChange = {
left: "leftPortal", // 左箭头
up: "upPortal", // 上箭头
right: "rightPortal", // 右箭头
down: "downPortal", // 下箭头
upFloor: "upFloor", // 上楼
downFloor: "downFloor", // 下楼
};
// ---- 可自定义,默认数值
const defaultValue = {
font: "Verdana", // 默认字体
scale: 60, // 默认地图缩放比例
depth: Infinity, // 默认的遍历深度
};
// ---- 不可自定义,计算数据
const dirData = {
up: [1, 0],
down: [-1, 0],
left: [0, 1],
right: [0, -1],
upFloor: [0, 0],
downFloor: [0, 0],
};
let ignoreEnemies = (this.ignoreEnemies = []);
let allChangeEntries = Object.entries(defaultChange);
this.setq = function (floorId) {
core.setFlag("任务地点", floorId);
};
const reset = core.events.resetGame;
this.bfs = function () {
areas = [];
// 获取所有分区,使用异步函数,保证不会卡顿
// 原理是用bfs扫将所有连在一起的地图合并成一个区域
(async function () {
let all = core.floorIds.slice();
const scanned = {
[all[0]]: true,
};
while (all.length > 0) {
let now = all.shift();
if (core.status.maps[now].deleted) continue;
if (!now) return;
await new Promise((res) => {
const result = core.plugin.bfsSearch(now, Infinity, true);
mapCache[`${now}_Infinity_false`] = result;
areas.push({ name: core.floors[now].areas, maps: result.order });
for (const map of result.order) {
scanned[map] = true;
all = all.filter((v) => !result.order.includes(v));
}
res("success");
}).then(() => {
core.setFlag("areas", areas);
});
}
})();
};
core.events.resetGame = function () {
reset.apply(core.events, arguments);
core.plugin.bfs();
};
/**
* 广度优先搜索搜索地图路径
* @param {string} center 中心地图的id
* @param {number} depth 搜索深度
* @param {boolean} noCache 是否不使用缓存
* @returns {BFSResult} 格式floorId_x_y_dir: floorId_x_y
*/
this.bfsSearch = function bfsSearch(center, depth, noCache) {
// 检查缓存
const id = `${center}_${depth}_${noBorder}`;
if (mapCache[id] && !noCache) return mapCache[id];
const used = {
[center]: true,
}; // 搜索过的楼层
let queue = [];
let stack = [center]; // 当前栈
let nowDepth = -1;
const mapOrder = [center]; // 遍历顺序,顺便还能记录遍历了哪些楼层
const res = {}; // 输出结果格式floorId_x_y_dir: floorId_x_y
const enemies = {};
const upOrDown = {};
const mapdir = {};
// 开始循环搜索
while (nowDepth < depth && stack.length > 0) {
const now = stack.shift(); // 当前id
if (core.status.maps[now].deleted) continue;
mapdir[now] = mapdir[now] ?? [];
const blocks = core.getMapBlocksObj(now); // 获取当前地图的每点的事件
enemies[now] = {};
// 遍历,获取可以传送的点,只检测绿点事件,因此可用红点事件进行传送来实现分区功能
for (const i in blocks) {
const block = blocks[i];
// 整合漏怪检测,所以要检测怪物
if (block.event.trigger === "battle") {
const id = block.event.id;
if (ignoreEnemies.includes(id)) continue;
else enemies[now][i] = block.event.id;
continue;
}
// 检测触发器是否为切换楼层,不是则直接跳过
if (block.event.trigger !== "changeFloor") continue;
const dirEntries = allChangeEntries.find(
(v) => v[1] === block.event.id
);
// 如果不是那六种传送门,直接忽略
if (!dirEntries) continue;
const data = block.event.data;
const dir = dirEntries[0];
const route = now + "_" + i.replace(",", "_") + "_" + dir;
const target = data.floorId + "_" + data.loc.join("_");
mapdir[now].push(dir);
if (!used[data.floorId]) {
if (dir === "upFloor" || dir === "downFloor") {
upOrDown[now] = upOrDown[id] ?? [];
upOrDown[now].push(dir);
}
queue.push(data.floorId); // 没有搜索过,则加入栈中
mapOrder.push(data.floorId);
used[data.floorId] = true;
}
res[route] = target;
}
if (stack.length === 0) {
stack = queue;
queue = [];
nowDepth++;
}
if (stack.length === 0 && queue.length === 0) break;
}
return { res, order: mapOrder, enemies, upOrDown, mapdir };
};
/**
* 获取绘制信息
* @param {string?} center 中心地图id
* @param {number?} depth 搜索深度
* @param {boolean?} noCache 是否不使用缓存
* @returns {MapDrawInfo}
*/
this.getMapDrawInfo = function (
center = core.status.floorId,
depth = defaultValue.depth,
noCache = false
) {
nowDepth = depth;
drawingMap = center;
const id = `${center}_${depth}_${noBorder}`;
// 检查缓存
if (drawCache[id] && !noCache) return drawCache[id];
const map = core.plugin.bfsSearch(center, depth, noCache);
mapCache[id] = map;
const res = getDrawInfo(map.res, center, map.order);
res.upOrDown = map.upOrDown;
res.mapdir = map.mapdir;
drawCache[id] = res;
return res;
};
/**
* 提供地图的绘制信息
* @param {{[x: string]: string}} map 要绘制的地图格式floorId_x_y_dir: floorId_x_y
* @param {string} center 中心地图的id
* @param {string[]} order 遍历顺序
* @returns {MapDrawInfo} 地图的绘制信息
*/
function getDrawInfo(map, center, order) {
// 先根据地图id分类从而确定每个地图连接哪些地图同时方便处理
const links = {};
for (const i in map) {
const splitted = i.split("_");
const id = splitted[0];
if (!links[id]) links[id] = {};
links[id][i] = map[i];
}
// 分类完毕,然后根据连接点先计算出各个地图的坐标,然后再进行判断
const centerFloor = core.status.maps[center];
const visitedCenter = core.hasVisitedFloor(center);
const locs = {
// 格式:[中心x, 中心y, 宽, 高, 是否到达过]
[center]: [2, 2, 1, 1, visitedCenter],
};
// 可以上楼下楼的地图
const upOrDown = {};
for (const id of order) {
const now = links[id];
// 遍历每一个地图的连接情况
for (const from in now) {
const to = now[from];
// 先根据from to计算物理位置
const fromData = from.split("_"),
toData = to.split("_");
const dir = fromData[3];
if (dir === "upFloor" || dir === "downFloor") continue;
if (!defaultChange[dir]) continue;
const v = dirData[dir][1], // 竖直数值
h = dirData[dir][0], // 水平数值
ha = Math.abs(h),
va = Math.abs(v);
const ff = id, // fromFloorId
tf = toData[0]; // toFloorId
const fromFloor = core.status.maps[ff],
toFloor = core.status.maps[tf];
const fhw = Math.floor(fromFloor.width / 2), // fromFloorHalfWidth
fhh = Math.floor(fromFloor.height / 2),
thw = Math.floor(toFloor.width / 2),
thh = Math.floor(toFloor.height / 2);
const fLoc = locs[id] ?? [0, 0];
if (!locs[ff]) continue;
let x, y;
if (locs && locs[tf]) {
x = locs[tf][0];
y = locs[tf][1];
} else {
// 计算坐标,公式可以通过画图推断出
x = fLoc[0] - v;
y = fLoc[1] - h;
}
locs[tf] = locs[tf] ?? [x, y, 1, 1, core.hasVisitedFloor(tf)];
}
}
// 获取地图绘制需要的长宽
let width = 0,
height = 0;
let left, right, up, down;
for (const id in locs) {
const [x, y, w, h] = locs[id];
if (left === void 0) {
left = right = x;
up = down = y;
}
left = Math.min(x - 1, left);
right = Math.max(x + 1, right);
up = Math.min(y - 1, up);
down = Math.max(y + 1, down);
}
width = right - left;
height = down - up;
return { locs, width, height, layer: upOrDown };
}
function mapblock(mapdir) {
let mb = "";
if (mapdir.includes("up")) mb += "u";
if (mapdir.includes("down")) mb += "d";
if (mapdir.includes("left")) mb += "l";
if (mapdir.includes("right")) mb += "r";
return mb ? mb + ".webp" : "null.webp";
}
core.animateFrame.globalAlphaFloor = 0;
core.animateFrame.globalAlphaFloorStatus = 1;
const tesk = document.createElement("canvas");
tesk.width = 300;
tesk.height = 300;
const teskctx = tesk.getContext("2d");
let line = 50;
teskctx.strokeStyle = "green";
teskctx.fillStyle = "green";
let now = 0;
core.registerAnimationFrame("tesk", true, function (timestamp) {
if (timestamp - now > 1000 / 60) {
now = timestamp;
core.clearMap(teskctx);
teskctx.lineWidth = 150 - line;
if (line <= 150) {
teskctx.beginPath();
teskctx.arc(150, 150, line, 0, Math.PI * 2);
line += 2;
teskctx.stroke();
} else {
teskctx.beginPath();
teskctx.arc(150, 150, line - 150, 0, Math.PI * 2);
line += 2;
teskctx.fill();
if (line >= 250) line = 50;
}
}
});
/**
* 绘制小地图
* @param {MapDrawInfo} info 地图绘制信息
* @param {number} scale 地图的绘制比例
*/
this.drawSmallMap = function (
ctx,
info,
center,
sx,
sy,
sw,
sh,
scale = defaultValue.scale
) {
core.clearMap(ctx, sx, sy, sw + 40, sh + 60);
if (core.domStyle.isVertical) {
sy += 50;
sx += 15;
} else {
sy += 60;
sx += 30;
}
core.fillRect(ctx, sx - 10, sy - 10, sw + 20, sh + 20, "#000");
core.strokeRect(ctx, sx - 10, sy - 10, sw + 20, sh + 20, "#fff", 5);
core.setTextAlign("outerUI", "center");
core.fillBoldText1(
ctx,
core.status.maps[center].areas,
sx + sw / 2,
sy - 20,
"#FFFFFF",
"#000000",
6,
"bold 42px Verdana"
);
const locs = info.locs;
for (const id in locs) {
const loc = locs[id];
let color = "#000";
if (!loc[4]) color = "#f0f";
const [x, y, w, h] = loc.map((v) => typeof v === "number" && v * scale);
const fx = x + sx,
fy = y + sy;
const mapdir = info.mapdir[id];
const img = mapblock(mapdir);
if (x < 0 || x > 4 * scale || y < 0 || y > 4 * scale) continue;
core.drawImage(ctx, img, 0, 0, 60, 60, fx, fy, w, h);
const layer = info.upOrDown[id];
const min = Math.min(w, h);
if (layer?.includes("upFloor"))
core.drawIcon(
ctx,
defaultChange.upFloor,
fx + min / 4,
fy + min / 4,
min / 2,
min / 2
);
if (layer?.includes("downFloor"))
core.drawIcon(
ctx,
defaultChange.downFloor,
fx + min / 4,
fy + min / 4,
min / 2,
min / 2
);
if (core.getFlag("任务地点") && core.getFlag("任务地点") === id)
ctx.drawImage(tesk, fx + min / 4, fy + min / 4, min / 2, min / 2);
if (id === core.status.floorId)
core.drawImage(
ctx,
"hero.webp",
0,
0,
32,
19,
fx + min / 4,
fy + (min * 5) / 16,
32,
19
);
// 显示漏怪数量
if (core.getFlag("showEnemy")) {
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const c = drawingMap + "_" + nowDepth + "_" + noBorder;
const n = Object.keys(mapCache[c].enemies[id]).length;
color = "#fff";
if (n > 10) color = "#fc3";
if (n > 20) color = "#f22";
ctx.shadowBlur = 0.6 * nowScale;
ctx.shadowColor = "#000";
if (n > 0)
core.fillText(
ctx,
n,
fx + (w * 3) / 10,
fy + (h * 7) / 10,
color,
22 + "px normal"
);
ctx.shadowBlur = 0;
}
if (!core.hasVisitedFloor(id)) {
core.fillRect(ctx, fx, fy, w, h, "rgba(0,0,0,0.7)");
core.fillText(
ctx,
"?",
fx + min / 2,
fy + (min * 3) / 4,
"#FFFFFF",
"bold 42px Verdana"
);
}
}
};
},
"楼传": function () {
// 在此增加新插件
core.canMoveFloor = function () {
let canmove = false;
core.status.thisMap.blocks.forEach((block) => {
if (
!block.disable &&
(block.event.id == "upFloor" || block.event.id == "downFloor")
) {
let automaticRoute = core.automaticRoute(block.x, block.y);
if (!core.flags.flyNearStair || automaticRoute.length > 0) {
let loc = automaticRoute.pop();
loc = automaticRoute.pop();
if (core.canMoveDirectly(loc?.x, loc?.y) >= 0 || !loc) {
canmove = true;
}
}
}
});
return canmove;
};
ui.prototype._drawViewMaps_drawHint = function () {
core.playSound("打开界面");
};
////// 绘制浏览地图界面 //////
ui.prototype._drawViewMaps = function (index, x, y) {
core.lockControl();
core.clearMap("data");
core.status.event.id = "viewMaps";
this.clearUI();
//console.log(index)
if (index == null) index = core.floorIds.indexOf(core.status.floorId);
core.animateFrame.tip = null;
core.status.checkBlock.cache = {};
let data = this._drawViewMaps_buildData(index, x, y);
core.drawWindowSkin("winskin1.webp", "ui", 0, 0, 416, 416);
let page = core.status.event.data.index;
let floorId = core.status.event.data.floorId;
core.ui.statusBar._update_map(floorId);
const bfs = core.plugin.bfsSearch(floorId, 1, true);
const mapdir = bfs.mapdir[floorId];
core.setTextAlign("ui", "center");
let size = (core.__PIXELS__ * 3) / 4; //312
const areas = core.getFlag("areas");
let i = areas.findIndex((v) => v.maps.includes(floorId));
core.fillRoundRect("ui", 15 - 2, 15 - 2, 35 + 4, 35 + 4, 4, "#444444");
core.strokeRoundRect(
"ui",
15 - 4,
15 - 4,
35 + 8,
35 + 8,
4,
"#444444",
1
);
core.fillBoldText1(
"ui",
"当前",
13 + 20,
17 + 20,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
core.fillRoundRect(
"ui",
15 - 2,
15 - 2 + 35 + 8 + size + 8 - 54,
35 + 4,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4,
15 - 4 + 35 + 8 + size + 8 - 54,
35 + 8,
35 + 8,
4,
"#444444",
1
);
if (
!core.status.maps[core.floorIds[page]].canFlyTo ||
!core.hasVisitedFloor(core.floorIds[page])
) {
core.fillBoldText1(
"ui",
"预览",
13 + 20,
17 + 20 + 35 + 8 + size + 8 - 54,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"传送",
13 + 20,
17 + 20 + 35 + 8 + size + 8 - 54,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
15 - 4 + size - 2 + 45,
15 - 2 + size - 4 + 45,
35 + 4,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4 + size - 4 + 45,
15 - 4 + size - 4 + 45,
35 + 8,
35 + 8,
4,
"#444444",
1
);
core.fillBoldText1(
"ui",
"离开",
15 - 4 + size - 4 + 45 + 22,
15 - 4 + size - 4 + 45 + 26,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
core.fillRoundRect(
"ui",
15 + 44 - 2,
15 - 2,
size + 4 - 58,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 + 44 - 4,
15 - 4,
size + 8 - 58,
35 + 8,
4,
"#444444",
1
);
if (mapdir.includes("up")) {
core.fillBoldText1(
"ui",
"北▲",
30 + 145 + 10,
17 + 20,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"北▲",
30 + 145 + 10,
17 + 20,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
15 - 2,
59 - 2,
35 + 4,
size + 4 - 58,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4,
59 - 4,
35 + 8,
size + 8 - 58,
4,
"#444444",
1
);
if (mapdir.includes("left")) {
core.fillBoldText1(
"ui",
"西",
15 + 17,
25 + 150,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
core.fillBoldText1(
"ui",
"◀",
15 + 17,
45 + 150,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"西",
15 + 17,
25 + 150,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
core.fillBoldText1(
"ui",
"◀",
15 + 17,
45 + 150,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
15 + 44 - 2,
15 - 2 + size - 4,
size + 4 - 58,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 + 44 - 4,
15 - 4 + size - 4,
size + 8 - 58,
35 + 8,
4,
"#444444",
1
);
if (mapdir.includes("down")) {
core.fillBoldText1(
"ui",
"南▼",
30 + 145 + 10,
17 + 20 + size - 4,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"南▼",
30 + 145 + 10,
17 + 20 + size - 4,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
15 - 2 + size - 4,
59 - 2,
35 + 4,
size + 4 - 58,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4 + size - 4,
59 - 4,
35 + 8,
size + 8 - 58,
4,
"#444444",
1
);
if (mapdir.includes("right")) {
core.fillBoldText1(
"ui",
"东",
15 + 17 + size - 4,
25 + 150,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
core.fillBoldText1(
"ui",
"▶",
15 + 17 + size - 4,
45 + 150,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"东",
15 + 17 + size - 4,
25 + 150,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
core.fillBoldText1(
"ui",
"▶",
15 + 17 + size - 4,
45 + 150,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
60 - 2,
60 - 2,
size - 58 + 4,
size - 58 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
60 - 4,
60 - 4,
size - 58 + 8,
size - 58 + 8,
4,
"#444444",
1
);
core.drawThumbnail(floorId, null, {
damage: data.damage,
ctx: "ui",
x: 58,
y: 58,
size: 0.62,
all: data.all,
});
if (
!core.status.maps[core.floorIds[page]].canFlyTo ||
!core.hasVisitedFloor(core.floorIds[page])
) {
/*core.fillRect("ui", 58,
58,
size - 50,
size - 50, "rgba(0,0,0,0.5)")*/
core.getContextByName("ui").globalAlpha = 0.7;
core.drawImage(
"ui",
"miwu.webp",
0,
0,
size,
size,
58,
58,
size - 50,
size - 50
);
core.getContextByName("ui").globalAlpha = 1;
/*core.fillText("ui", '?', 188,
278,
"rgba(255,255,255,0.2)", this._buildFont(250, true))*/
}
core.fillRoundRect(
"ui",
15 + 44 - 2,
60 - 2 + size - 4,
size + 4 - 58,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 + 44 - 4,
60 - 4 + size - 4,
size + 8 - 58,
35 + 8,
4,
"#444444",
1
);
core.fillBoldText1(
"ui",
core.status.maps[floorId].areas,
30 + 145 + 10,
17 + 65 + size - 4,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
core.fillRoundRect(
"ui",
15 - 2,
60 - 2 + size - 4,
35 + 4,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4,
60 - 4 + size - 4,
35 + 8,
35 + 8,
4,
"#444444",
1
);
if (i === 0) {
core.fillBoldText1(
"ui",
"◀",
30,
17 + 65 + size - 4,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"◀",
30,
17 + 65 + size - 4,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
15 - 2 + size - 4,
60 - 2 + size - 4,
35 + 4,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4 + size - 4,
60 - 4 + size - 4,
35 + 8,
35 + 8,
4,
"#444444",
1
);
if (i === areas.length - 1) {
core.fillBoldText1(
"ui",
"▶",
30 + 300 + 10,
17 + 65 + size - 4,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"▶",
30 + 300 + 10,
17 + 65 + size - 4,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
15 - 2 + size - 4,
15 - 2,
80 + 4,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4 + size - 4,
15 - 4,
80 + 8,
35 + 8,
4,
"#444444",
1
);
if (mapdir.includes("upFloor")) {
core.fillBoldText1(
"ui",
"上楼",
30 + 320 + 10,
17 + 20,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"上楼",
30 + 320 + 10,
17 + 20,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
15 - 2 + size - 4,
15 - 2 + size - 4,
80 + 4,
35 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4 + size - 4,
15 - 4 + size - 4,
80 + 8,
35 + 8,
4,
"#444444",
1
);
if (mapdir.includes("downFloor")) {
core.fillBoldText1(
"ui",
"下楼",
30 + 320 + 10,
17 + 20 + size - 4,
"#FFFFFF",
"#000000",
2,
this._buildFont(18, true)
);
} else {
core.fillBoldText1(
"ui",
"下楼",
30 + 320 + 10,
17 + 20 + size - 4,
"#909090",
"#000000",
2,
this._buildFont(18, true)
);
}
core.fillRoundRect(
"ui",
15 - 2 + size - 4 + 35 + 8,
59 - 2,
37 + 4,
(size - 58) / 2 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4 + size - 4 + 35 + 8,
59 - 4,
37 + 8,
(size - 58) / 2 + 8,
4,
"#444444",
1
);
const title = core.status.maps[floorId].title;
//const length = title.length
fillTextVertical(
"ui",
title,
15 - 4 + size - 4 + 45,
85,
core.hasVisitedFloor(floorId) ? "#FFFFFF" : "#444444",
"#000000",
18
);
//const uictx = main.dom.gameCanvas.ui.getContext('2d')
core.fillRoundRect(
"ui",
15 - 2 + size - 4 + 35 + 8,
59 - 2 + (size - 58) / 2 + 8,
37 + 4,
119 + 4,
4,
"#444444"
);
core.strokeRoundRect(
"ui",
15 - 4 + size - 4 + 35 + 8,
59 - 4 + (size - 58) / 2 + 8,
37 + 8,
119 + 8,
4,
"#444444",
1
);
if (core.getFlag("showEnemy")) {
fillTextVertical(
"ui",
"关闭漏怪检测",
15 - 4 + size - 4 + 45,
220,
"#FFFFFF",
"#000000",
18
);
} else {
fillTextVertical(
"ui",
"开启漏怪检测",
15 - 4 + size - 4 + 45,
220,
"#FFFFFF",
"#000000",
18
);
}
//uictx.fillTextVertical(title, 15 - 4 + size - 4 + 35 + 29, 25 + 150)
//fillTextVertical('ui', title, 15 - 4 + size - 4 + 35 + 29, 25 + 150, '#FFFFFF', this._buildFont(18, true))
};
function fillTextVertical(name, text, x, y, style, boldstyle, fontsize) {
//竖向文字绘制
const ctx = core.ui.getContextByName(name);
if (!ctx) return;
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
fontsize *= 3;
const length = text.length;
canvas.width = fontsize * 2;
canvas.height = fontsize * length * 2;
if (style) context.fillStyle = core.arrayToRGBA(style);
if (boldstyle) context.strokeStyle = core.arrayToRGBA(boldstyle);
context.lineWidth = 2;
if (fontsize) context.font = core.ui._buildFont(fontsize, true);
let arrText = text.split("");
let arrWidth = arrText.map(function (letter) {
return context.measureText(letter).width;
});
let align = context.textAlign;
let baseline = context.textBaseline;
let sx = fontsize,
sy = fontsize * length;
if (align == "left") {
sx = sx + Math.max.apply(null, arrWidth) / 2;
} else if (align == "right") {
sx = sx - Math.max.apply(null, arrWidth) / 2;
}
if (
baseline == "bottom" ||
baseline == "alphabetic" ||
baseline == "ideographic"
) {
sy = sy - arrWidth[0] / 2;
} else if (baseline == "top" || baseline == "hanging") {
sy = sy + arrWidth[0] / 2;
}
context.textAlign = "center";
context.textBaseline = "middle";
context.lineWidth = 6;
// 开始逐字绘制
arrText.forEach(function (letter, index) {
// 确定下一个字符的纵坐标位置
context.strokeText(letter, sx, sy);
context.fillText(letter, sx, sy);
// 旋转坐标系还原成初始态
context.setTransform(1, 0, 0, 1, 0, 0);
// 确定下一个字符的纵坐标位置
var letterWidth = 54;
sy = sy + letterWidth;
});
// 水平垂直对齐方式还原
context.textAlign = align;
context.textBaseline = baseline;
//绘制到目标位置
ctx.drawImage(
canvas,
x,
y - (fontsize / 3) * length,
canvas.width / 3,
canvas.height / 3
);
}
////// 点击楼层传送器时的打开操作 //////
events.prototype.useFly = function (fromUserAction) {
if (!core.isPlaying()) return;
if (!core.status.maps[core.status.floorId].canFlyFrom) {
core.drawTip(core.material.items["fly"].name + "好像失效了", "fly");
return;
}
// 从“浏览地图”页面:尝试直接传送到该层
if (core.status.event.id == "viewMaps") {
if (!core.hasItem("fly")) {
core.playSound("操作失败");
core.drawTip("你没有" + core.material.items["fly"].name, "fly");
} else if (
core.flags.flyNearStair &&
!core.nearStair() &&
!flags.canMoveFloor
) {
core.playSound("操作失败");
core.drawTip(
"无法到达楼梯边使用" + core.material.items["fly"].name,
"fly"
);
} else {
core.flyTo(core.status.event.data.floorId);
core.updateStatusBar();
}
return;
}
if (!this._checkStatus("fly", fromUserAction, true)) return;
//if (core.flags.flyNearStair && !core.nearStair())
if (
(core.flags.flyNearStair && !core.nearStair()) ||
!flags.canMoveFloor
) {
core.playSound("操作失败");
core.drawTip(
"无法到达楼梯边使用" + core.material.items["fly"].name,
"fly"
);
core.unlockControl();
core.status.event.data = null;
core.status.event.id = null;
return;
}
if (!core.canUseItem("fly")) {
core.playSound("操作失败");
core.drawTip(core.material.items["fly"].name + "好像失效了", "fly");
core.unlockControl();
core.status.event.data = null;
core.status.event.id = null;
return;
}
core.playSound("打开界面");
core.useItem("fly", true);
core.updateStatusBar();
return;
};
////// 系统菜单栏界面时的点击操作 //////
actions.prototype._clickSettings = function (x, y) {
if (this._out(x)) return;
var choices = core.status.event.ui.choices;
var topIndex = this._getChoicesTopIndex(choices.length);
if (y >= topIndex && y < topIndex + choices.length) {
var selection = y - topIndex;
core.status.event.selection = selection;
switch (selection) {
case 0:
core.status.event.selection = 0;
core.playSound("确定");
core.ui._drawSwitchs();
break;
case 1:
// core.playSound('确定');
core.ui._drawKeyBoard();
break;
case 2:
// core.playSound('确定');
core.clearUI();
core.useItem("fly");
break;
case 3:
core.status.event.selection = 0;
core.playSound("确定");
core.ui._drawNotes();
break;
case 4:
core.status.event.selection = 0;
core.playSound("确定");
core.ui._drawSyncSave();
break;
case 5:
core.status.event.selection = 0;
core.playSound("确定");
core.ui._drawGameInfo();
break;
case 6:
return core.confirmRestart();
case 7:
core.playSound("取消");
core.ui.closePanel();
break;
}
}
return;
};
////// 查看地图界面时的点击操作 //////
actions.prototype._clickViewMaps = function (x, y, px, py) {
if (core.status.event.data == null) {
core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId));
return;
}
let now = core.floorIds.indexOf(core.status.floorId);
let index = core.status.event.data.index;
let cx = core.status.event.data.x,
cy = core.status.event.data.y;
let floorId = core.floorIds[index],
mw = core.floors[floorId].width,
mh = core.floors[floorId].height;
let perpx = core.__PIXELS__ / 5,
cornerpx = (perpx * 3) / 4;
const bfs = core.plugin.bfsSearch(floorId, 1, true);
const mapdir = bfs.mapdir[floorId];
const res = bfs.res;
const formto = {};
for (let from in res) {
const to = res[from];
const [fromfloorId, fromsx, fromsy, dir] = from.split("_");
const [tofloorId, tosx, tosy] = to.split("_");
if (!formto[fromfloorId]) formto[fromfloorId] = {};
if (!formto[fromfloorId][dir]) formto[fromfloorId][dir] = tofloorId;
}
const areas = core.getFlag("areas");
let i = areas.findIndex((v) => v.maps.includes(floorId));
if (px >= 11 && px <= 54 && py >= 11 && py <= 54) {
core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId));
} else if (px >= 362 && px <= 407 && py >= 191 && py <= 318) {
flags.showEnemy = !flags.showEnemy;
core.ui._drawViewMaps(index);
} else if (px >= 364 && px <= 407 && py >= 364 && py <= 407) {
core.clearMap("data");
core.playSound("取消");
core.ui.closePanel();
core.getItemDetail();
core.redrawMap();
core.updateStatusBar();
core.ui.statusBar._update_map();
return;
} else if (px >= 55 && px <= 317 && py >= 11 && py <= 54) {
if (mapdir.includes("up"))
core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].up));
} else if (px >= 55 && px <= 317 && py >= 319 && py <= 362) {
if (mapdir.includes("down"))
core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].down));
} else if (px >= 11 && px <= 54 && py >= 55 && py <= 317) {
if (mapdir.includes("left"))
core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].left));
} else if (px >= 319 && px <= 362 && py >= 55 && py <= 317) {
if (mapdir.includes("right"))
core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].right));
} else if (px >= 319 && px <= 407 && py >= 11 && py <= 54) {
if (mapdir.includes("upFloor"))
core.ui._drawViewMaps(core.floorIds.indexOf(formto[floorId].upFloor));
} else if (px >= 319 && px <= 407 && py >= 319 && py <= 362) {
if (mapdir.includes("downFloor"))
core.ui._drawViewMaps(
core.floorIds.indexOf(formto[floorId].downFloor)
);
} else if (
px >= 55 &&
px <= 317 &&
py >= 55 &&
py <= 317 &&
core.isPlaying()
) {
core.useFly(false);
return;
} else if (px >= 11 && px <= 54 && py >= 364 && py <= 407) {
if (i > 0) {
i -= 1;
core.ui._drawViewMaps(core.floorIds.indexOf(areas[i].maps[0]));
}
} else if (px >= 319 && px <= 362 && py >= 364 && py <= 407) {
if (i < areas.length - 1) {
i += 1;
core.ui._drawViewMaps(core.floorIds.indexOf(areas[i].maps[0]));
}
}
};
const replayAction_fly = function (action) {
//楼层传送的录像操作
if (action.indexOf("fly:") != 0) return false;
var floorId = action.substring(4);
var toIndex = core.floorIds.indexOf(floorId);
if (
!core.canUseItem("fly") ||
(core.flags.flyNearStair && !core.nearStair() && !flags.canMoveFloor)
)
return false;
core.ui._drawViewMaps(toIndex);
if (core.status.replay.speed == 24) {
if (!core.flyTo(floorId, core.replay))
core.control._replay_error(action);
return true;
}
setTimeout(function () {
if (!core.flyTo(floorId, core.replay))
core.control._replay_error(action);
}, core.control.__replay_getTimeout());
return true;
};
core.registerReplayAction("fly", replayAction_fly);
////// 查看地图界面时,放开某个键的操作 //////
actions.prototype._keyUpViewMaps = function (keycode) {
if (core.status.event.data == null) {
core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId));
return;
}
var floorId = core.floorIds[core.status.event.data.index];
if (keycode == 27 || keycode == 71) {
core.clearMap("data");
core.playSound("取消");
core.ui.closePanel();
core.getItemDetail();
core.redrawMap();
core.ui.statusBar._update_map();
core.updateStatusBar();
return;
}
if (keycode == 86) {
core.status.event.data.damage = !core.status.event.data.damage;
core.playSound("光标移动");
core.ui._drawViewMaps(core.status.event.data);
return;
}
if (keycode == 66 || keycode == 88) {
if (core.isReplaying()) {
core.control._replay_book();
} else {
core.openBook(false);
}
return;
}
if (
(keycode == 13 || keycode == 32 || keycode == 67) &&
!core.isReplaying()
) {
core.useFly(false);
return;
}
return;
};
actions.prototype._keyDownViewMaps = function (keycode) {
if (core.status.event.data == null) return;
var floorId = core.floorIds[core.status.event.data.index],
mh = core.floors[floorId].height;
if (keycode == 39) this._clickViewMaps(9, 1, 330, 250);
if (keycode == 37) this._clickViewMaps(9, 8, 25, 200);
if (keycode == 40) this._clickViewMaps(9, 6, 250, 330);
if (keycode == 38) this._clickViewMaps(9, 3, 200, 25);
if (keycode == 34) this._clickViewMaps(9, 3, 350, 330);
if (keycode == 33) this._clickViewMaps(9, 3, 350, 25);
return;
};
actions.prototype._sys_onmousewheel = function (direct) {
// 向下滚动是 -1 ,向上是 1
if (this._checkReplaying()) {
// 滚轮控制速度
if (direct == 1) core.speedUpReplay();
if (direct == -1) core.speedDownReplay();
return;
}
// 楼层飞行器
if (core.status.lockControl && core.status.event.id == "fly") {
if (direct == 1) core.ui.drawFly(this._getNextFlyFloor(1));
if (direct == -1) core.ui.drawFly(this._getNextFlyFloor(-1));
return;
}
// 怪物手册
if (core.status.lockControl && core.status.event.id == "book") {
var pageinfo = core.ui._drawBook_pageinfo();
if (direct == 1)
core.ui.drawBook(core.status.event.data - pageinfo.per_page);
if (direct == -1)
core.ui.drawBook(core.status.event.data + pageinfo.per_page);
return;
}
// 存读档
if (
core.status.lockControl &&
(core.status.event.id == "save" || core.status.event.id == "load")
) {
var index =
core.status.event.data.page * 10 + core.status.event.data.offset;
if (direct == 1) core.ui._drawSLPanel(index - 10);
if (direct == -1) core.ui._drawSLPanel(index + 10);
return;
}
// 浏览地图
if (core.status.lockControl && core.status.event.id == "viewMaps") {
let floorId = core.floorIds[core.status.event.data.index];
if (!flags.__visited__[floorId]) floorId = core.status.floorId;
const visit = Object.keys(flags.__visited__);
let index = visit.indexOf(floorId);
if (direct == 1) {
if (index > 0)
core.ui._drawViewMaps(core.floorIds.indexOf(visit[index - 1]));
}
if (direct == -1) {
if (index < visit.length - 1)
core.ui._drawViewMaps(core.floorIds.indexOf(visit[index + 1]));
}
return;
}
// wait事件
if (
core.status.lockControl &&
core.status.event.id == "action" &&
core.status.event.data.type == "wait"
) {
var timeout =
Math.max(0, core.status.event.timeout - new Date().getTime()) || 0;
core.setFlag("type", 0);
var keycode = direct == 1 ? 33 : 34;
core.setFlag("keycode", keycode);
core.setFlag("timeout", timeout);
var executed = core.events.__action_wait_afterGet(
core.status.event.data.current
);
if (executed || !core.status.event.data.current.forceChild) {
core.status.route.push("input:" + (1e8 * timeout + keycode));
clearTimeout(core.status.event.interval);
delete core.status.event.timeout;
core.doAction();
}
return;
}
};
core.registerAction(
"onmousewheel",
"_sys_onmousewheel",
actions.prototype._sys_onmousewheel,
0
);
},
"CG回廊": function () {
// 在此增加新插件
const CGUI = document.createElement("canvas"); //CGui画布设置
CGUI.style.position = "absolute";
CGUI.style.zIndex = 300;
CGUI.style.display = "none";
CGUI.id = "CGUI";
main.dom.gameGroup.insertAdjacentElement("afterend", CGUI);
CGUI.style.top = "50%";
CGUI.style.left = "50%";
CGUI.style.transform = "translate(-50%,-50%)";
const ctx = CGUI.getContext("2d");
main.dom.CGUI = CGUI;
let page = 0; //初始页面
let show = false; //展示状态
CGUI.onclick = function (e) {
try {
e.preventDefault();
if (core.isPlaying()) return false;
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor((e.clientX - left) / core.domStyle.scale),
py = Math.floor((e.clientY - top) / core.domStyle.scale);
core.ui.CG.onclick(px * 3, py * 3);
} catch (ee) {
main.log(ee);
}
};
class CG {
constructor() {
this.cgs;
//cg列表
this.UIMx = [
//空位用none填充当前ui3*2
[
["eve_010102.webp", "eve_010203.webp", "eve_010304.webp"],
["eve_010501.webp", "eve_010601.webp", "eve_010701.webp"],
],
[
["eve_010801.webp", "eve_010902.webp", "eve_011001.webp"],
["eve_011101.webp", "eve_011202.webp", "eve_011302.webp"],
],
[
["eve_011402.webp", "eve_020102.webp", "eve_020201.webp"],
["eve_020301.webp", "eve_020401.webp", "eve_020501.webp"],
],
[
["eve_020605.webp", "eve_020701.webp", "eve_020801.webp"],
["eve_030101.webp", "eve_030206.webp", "eve_030302.webp"],
],
[
["eve_030508.webp", "eve_030601.webp", "eve_030801.webp"],
["eve_030901.webp", "eve_031002.webp", "eve_031101.webp"],
],
[
["eve_040201.webp", "eve_040401.webp", "eve_040501.webp"],
["eve_040601.webp", "eve_040702.webp", "eve_040801.webp"],
],
[
["eve_050101.webp", "eve_050201.webp", "eve_050401.webp"],
["eve_050501.webp", "eve_050601.webp", "eve_050704.webp"],
],
[
["eve_050801.webp", "eve_070101.webp", "bg_1511.webp"],
["bg_1521.webp", "bg_2011.webp", "bg_2521.webp"],
],
[
["bg_3042.webp", "bg_3551.webp", "bg_3571.webp"],
["bg_3721.webp", "bg_5033.webp", "bg_5044.webp"],
],
];
}
//更新
update() {
this.background();
this.drawUI();
}
background() {
//画布大小设置
if (core.domStyle.isVertical) {
CGUI.width = 1248;
CGUI.height = 2028;
} else {
CGUI.width = 2028;
CGUI.height = 1248;
}
core.setTextAlign(ctx, "center");
}
onclick(px, py) {
//点击
if (show) {
show = !show;
core.clearMap(ctx);
this.update();
return;
}
const makeBox = ([x, y], [w, h]) => {
return [
[x, y],
[x + w, y + h],
];
};
const inRect = ([x, y], [
[sx, sy],
[dx, dy]
]) => {
return sx <= x && x <= dx && sy <= y && y <= dy;
};
const pos = [px, py];
const backbox = makeBox([15, 35], [210, 90]);
if (inRect(pos, backbox)) {
//离开按钮是一致的,其余的记区分横竖屏
CGUI.style.display = "none";
core.clearMap(ctx);
core.restart();
return;
}
if (core.domStyle.isVertical) {
//竖屏
const pageupbox = makeBox([200, 1830], [200, 100]);
const pagedownbox = makeBox([900, 1830], [200, 100]);
const imagebox0 = makeBox([50, 200], [560, 420]);
const imagebox1 = makeBox([50, 750], [560, 420]);
const imagebox2 = makeBox([50, 1300], [560, 420]);
const imagebox3 = makeBox([650, 200], [560, 420]);
const imagebox4 = makeBox([650, 750], [560, 420]);
const imagebox5 = makeBox([650, 1300], [560, 420]);
if (inRect(pos, pagedownbox)) {
//2代表当前最大页数-1
if (page < this.UIMx.length - 1) {
page++;
core.clearMap(ctx);
this.update();
}
} else if (inRect(pos, pageupbox)) {
if (page > 0) {
page--;
core.clearMap(ctx);
this.update();
}
} else if (inRect(pos, imagebox0)) {
if (this.cgs.includes(this.UIMx[page][0][0])) {
const img = core.material.images.images[this.UIMx[page][0][0]];
if (img) {
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
ctx.drawImage(img, 0, 0, 2028, 1248);
ctx.restore(); //重置画布设置
show = !show;
}
}
} else if (inRect(pos, imagebox1)) {
if (this.cgs.includes(this.UIMx[page][0][1])) {
const img = core.material.images.images[this.UIMx[page][0][1]];
if (img) {
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
ctx.drawImage(img, 0, 0, 2028, 1248);
ctx.restore(); //重置画布设置
show = !show;
}
}
} else if (inRect(pos, imagebox2)) {
if (this.cgs.includes(this.UIMx[page][0][2])) {
const img = core.material.images.images[this.UIMx[page][0][2]];
if (img) {
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
ctx.drawImage(img, 0, 0, 2028, 1248);
ctx.restore(); //重置画布设置
show = !show;
}
}
} else if (inRect(pos, imagebox3)) {
if (this.cgs.includes(this.UIMx[page][1][0])) {
const img = core.material.images.images[this.UIMx[page][1][0]];
if (img) {
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
ctx.drawImage(img, 0, 0, 2028, 1248);
ctx.restore(); //重置画布设置
show = !show;
}
}
} else if (inRect(pos, imagebox4)) {
if (this.cgs.includes(this.UIMx[page][1][1])) {
const img = core.material.images.images[this.UIMx[page][1][1]];
if (img) {
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
ctx.drawImage(img, 0, 0, 2028, 1248);
ctx.restore(); //重置画布设置
show = !show;
}
}
} else if (inRect(pos, imagebox5)) {
if (this.cgs.includes(this.UIMx[page][1][2])) {
const img = core.material.images.images[this.UIMx[page][1][2]];
if (img) {
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
ctx.drawImage(img, 0, 0, 2028, 1248);
ctx.restore(); //重置画布设置
show = !show;
}
}
}
} else {
const pageupbox = makeBox([200, 1110], [200, 100]);
const pagedownbox = makeBox([1600, 1110], [200, 100]);
const imagebox0 = makeBox([75, 150], [600, 450]);
const imagebox1 = makeBox([725, 150], [600, 450]);
const imagebox2 = makeBox([1300, 150], [600, 450]);
const imagebox3 = makeBox([75, 650], [600, 450]);
const imagebox4 = makeBox([725, 650], [600, 450]);
const imagebox5 = makeBox([1375, 650], [600, 450]);
if (inRect(pos, pagedownbox)) {
if (page < this.UIMx.length - 1) {
page++;
core.clearMap(ctx);
this.update();
}
} else if (inRect(pos, pageupbox)) {
if (page > 0) {
page--;
core.clearMap(ctx);
this.update();
}
} else if (inRect(pos, imagebox0)) {
if (this.cgs.includes(this.UIMx[page][0][0])) {
const img = core.material.images.images[this.UIMx[page][0][0]];
if (img) {
ctx.drawImage(img, 0, 0, 2028, 1248);
show = !show;
}
}
} else if (inRect(pos, imagebox1)) {
if (this.cgs.includes(this.UIMx[page][0][1])) {
const img = core.material.images.images[this.UIMx[page][0][1]];
if (img) {
ctx.drawImage(img, 0, 0, 2028, 1248);
show = !show;
}
}
} else if (inRect(pos, imagebox2)) {
if (this.cgs.includes(this.UIMx[page][0][2])) {
const img = core.material.images.images[this.UIMx[page][0][2]];
if (img) {
ctx.drawImage(img, 0, 0, 2028, 1248);
show = !show;
}
}
} else if (inRect(pos, imagebox3)) {
if (this.cgs.includes(this.UIMx[page][1][0])) {
const img = core.material.images.images[this.UIMx[page][1][0]];
if (img) {
ctx.drawImage(img, 0, 0, 2028, 1248);
show = !show;
}
}
} else if (inRect(pos, imagebox4)) {
if (this.cgs.includes(this.UIMx[page][1][1])) {
const img = core.material.images.images[this.UIMx[page][1][1]];
if (img) {
ctx.drawImage(img, 0, 0, 2028, 1248);
show = !show;
}
}
} else if (inRect(pos, imagebox5)) {
if (this.cgs.includes(this.UIMx[page][1][2])) {
const img = core.material.images.images[this.UIMx[page][1][2]];
if (img) {
ctx.drawImage(img, 0, 0, 2028, 1248);
show = !show;
}
}
}
}
}
drawUI() {
//绘制页面
core.clearMap(CGUI);
const bgVertical = core.material.images.images["bg_2010.webp"]; //竖屏背景
const bg = core.material.images.images["bg_5043.webp"]; //横屏背景
if (core.domStyle.isVertical) {
//竖屏
core.fillRect(ctx, 0, 0, 1248, 2028, "#000000"); //黑色背景
ctx.globalAlpha = 0.5; //透明度
if (bgVertical)
ctx.drawImage(bgVertical, 0, 0, 1280, 1500, 0, 0, 1248, 2028); //绘制半透明背景图片
ctx.globalAlpha = 1; //恢复为不透明
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
"◀离开",
100,
110,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
"上一页",
300,
1900,
page === 0 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
page + 1 + "/" + this.UIMx.length,
650,
1900,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
"下一页",
1000,
1900,
page === this.UIMx.length - 1 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
// 添加向上翻页和向下翻页的按钮
// 添加3*2个4:3的画框及图片
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 2; j++) {
const img = core.material.images.images[this.UIMx[page][j][i]];
core.strokeRect(
ctx,
50 + j * 600,
200 + i * 550,
560,
420,
"#444444",
5
);
if (this.cgs.includes(this.UIMx[page][j][i])) {
if (img)
ctx.drawImage(
img,
50 + j * 600 + 15,
200 + i * 550 + 15,
560 - 30,
420 - 30
);
} else {
ctx.fillStyle = "#000000";
ctx.fillRect(
50 + j * 600 + 15,
200 + i * 550 + 15,
560 - 30,
420 - 30
);
const img = core.material.images.images["LOGO.webp"];
if (img)
ctx.drawImage(
img,
50 + j * 600 + 15,
200 + i * 550 + 15,
560 - 30,
420 - 30
);
}
}
}
} else {
//横屏
core.fillRect(ctx, 0, 0, 2028, 1248, "#000000"); //黑色背景
ctx.globalAlpha = 0.5; //透明度
if (bg) ctx.drawImage(bg, 0, 0, 1280, 720, 0, 0, 2028, 1248); //绘制半透明背景图片
ctx.globalAlpha = 1; //恢复为不透明
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
"◀离开",
110,
100,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
// 添加向上翻页和向下翻页的按钮
core.fillBoldText1(
ctx,
"上一页",
300,
1180,
page === 0 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
page + 1 + "/" + this.UIMx.length,
1000,
1180,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
"下一页",
1700,
1180,
page === this.UIMx.length - 1 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
// 添加3*2个4:3的画框
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 3; j++) {
core.strokeRect(
ctx,
75 + j * 650,
150 + i * 500,
600,
450,
"#444444",
2
);
if (this.cgs.includes(this.UIMx[page][i][j])) {
const img = core.material.images.images[this.UIMx[page][i][j]];
if (img)
ctx.drawImage(
img,
75 + j * 650 + 15,
150 + i * 500 + 15,
600 - 30,
450 - 30
);
} else {
ctx.fillStyle = "#000000";
ctx.fillRect(
75 + j * 650 + 15,
150 + i * 500 + 15,
600 - 30,
450 - 30
);
const img = core.material.images.images["LOGO.webp"];
if (img)
ctx.drawImage(
img,
75 + j * 650 + 15,
150 + i * 500 + 15,
600 - 30,
450 - 30
);
}
}
}
}
}
}
this.setcgs = function (img) {
const a = core.getLocalStorage("cgs", []);
if (img) {
if (!a.includes(img)) a.push(img);
core.setLocalStorage("cgs", a);
} else core.setLocalStorage("cgs");
};
core.ui.CG = new CG();
main.dom.CGMode.onclick = function () {
//点击开始页面的CG MODE进入cg回廊
main.core.control.checkBgm();
page = 0;
main.core.ui.CG.cgs = core.getLocalStorage("cgs", []);
CGUI.style.display = "block";
main.core.ui.CG.update();
};
},
"光标设置": function () {
// 在此增加新插件
this.changeMouse = function (
icon,
div = "gameGroup",
translateX = 0,
translateY = 0,
scaleX = 1,
scaleY = 1,
degree = 0,
px = 0,
py = 0
) {
const canvas = document.createElement("canvas");
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext("2d");
ctx.translate(translateX, translateY); //偏移画布x,y
ctx.scale(scaleX, scaleY); //(x,y轴缩放倍率-1为沿XY轴翻转)
const angle = (degree * Math.PI) / 180; //根据角度计算参数
ctx.rotate(angle); //顺时针旋转(以画布原点为中心,可通过偏移画布影响中心点)
let info = {
image: core.statusBar.icons[icon],
posX: 0,
posY: 0,
height: 32,
};
core.drawIcon(ctx, icon, 0, 0, 32, 32);
const data = canvas.toDataURL("image/png");
core.dom[div].style.cursor = `url(${data}) ${px} ${py},url(${data}),auto`; //div为你要改变光标的元素默认为包含状态栏的整个游戏画面,px/py为点击点偏移像素
};
this.removeMouse = function (div = "gameGroup") {
core.dom[div].style.cursor = "auto";
};
},
"信息弹出": function () {
// 在此增加新插件
/* 弹出显示某个内容
* 使用方法core.addPop(px, py, value, color, boldColor, left, jump, time, show, font, speed)
* 参数说明:
* px & py: number 弹出位置
* value: string 显示内容
* color: string 填充颜色
* boldColor: string 描边颜色
*/
// 默认字体
var fontD = "16px Verdana";
// 默认颜色
var colorD = "red";
// 默认描边颜色
var boldColorD = "black";
/** 血量弹出 */
function pop() {
var ctx = core.getContextByName("pop");
if (!ctx)
ctx = core.createCanvas(
"pop",
0,
0,
core.__PIXELS__,
core.__PIXELS__,
90
);
ctx.canvas.classList.add("gameCanvas", "anti-aliasing");
core.clearMap(ctx);
core.setTextAlign("pop", "left");
var list = core.status.pop || [];
var count = 0;
list.forEach(function (one) {
// 由frame计算出dy
var dy = 6 - one.frame * 0.2;
var dx = one.speed;
if (one.jump) {
one.py -= dy;
}
if (!one.left) {
one.px += dx;
} else {
one.px -= dx;
}
one.frame++;
// 绘制
if (one.frame >= one.time)
core.setAlpha(ctx, 1 - (one.frame - one.time) / one.show);
else core.setAlpha(ctx, 1);
core.fillBoldText1(
ctx,
one.value,
one.px,
one.py,
one.color || "red",
one.boldColor || "black",
2,
one.font
);
if (one.frame >= one.time + one.show) count++;
});
if (count > 0) list.splice(0, count);
}
let now = 0;
if (!main.replayChecking)
core.registerAnimationFrame("pop", true, (temptime) => {
if (temptime - now > 1000 / 60) {
now = temptime;
pop();
}
});
/** 添加弹出内容 */
this.addPop = function (
value,
px,
py,
color,
boldColor,
left,
jump,
time,
show,
font,
speed
) {
var data = {
px: px,
py: py,
value: value,
color: color || colorD,
boldColor: boldColor || boldColorD,
frame: 0,
left: left || false,
jump: jump || false,
time: time || 60,
show: show || 30,
font: font || fontD,
speed: speed || 1,
};
if (!core.status.pop) core.status.pop = [data];
else core.status.pop.push(data);
};
},
"warning": function () {
// 默认音效名
var defaultSound = "jingbao.mp3";
// 默认字体名
var defaultFont = "Verdana";
var timeout;
/** warning提示
* @param {number} x 横坐标
* @param {number} y 纵坐标
* @param {string} text 显示的文字
*/
this.drawWarning = function (
x,
y,
text,
text2,
warning,
large = 2,
size = 36
) {
if (timeout) return;
x = x ?? 6;
y = y ?? 6;
text = text || "boss";
text += "</br>";
for (var i = 0; i < 10; i++) text += " ";
text += text2;
// 生成文字
var elements = document.querySelectorAll(".gameCanvas");
var t = document.createElement("p");
t.innerHTML = text;
t.style.position = "absolute";
t.style.fontSize = size * core.domStyle.scale + "px";
t.style.left = -(300 * core.domStyle.scale) + "px";
t.style.top = parseInt(elements[0].style.height) / 2 - 100 + "px";
t.style.zIndex = "300";
t.style.color = "#f11";
t.style.fontFamily = defaultFont;
t.style.overflow = "none";
t.style.width = "100%";
t.classList.add("warning");
core.dom.gameDraw.appendChild(t);
setTimeout(function () {
t.style.left = 416 * core.domStyle.scale + "px";
}, 50);
// 计算偏移量
var px = ((6 - x) / 12) * (100 - 100 / large + 2);
var py = ((6 - y) / 12) * (100 - 100 / large + 2);
// 修改画布的scale和transform
let time = 0;
let s = 1;
let sx = 0;
let sy = 0;
let cishu = 1;
core.registerAnimationFrame("big", true, function (temptime) {
if (temptime - time > 10) {
time = temptime;
s += (large - 1) / 30;
sx += px / 30;
sy += py / 30;
elements.forEach(function (v) {
if (v instanceof HTMLCanvasElement) {
v.style.transform =
"scale(" + s + ")translate(" + sx + "%, " + sy + "%)";
}
});
cishu++;
if (cishu == 30) {
core.unregisterAnimationFrame("big");
}
}
});
if (!warning) core.playSound(defaultSound);
// 拉回镜头
timeout = setTimeout(function () {
// timeout = setTimeout(function () {
// timeout = void 0;
// core.dom.gameDraw.removeChild(t);
// }, 1500);
let time2 = 0;
let s2 = large - (large - 1) / 30;
let sx2 = px;
let sy2 = py;
let cishu2 = 1;
core.registerAnimationFrame("small", true, function (temptime) {
if (temptime - time2 > 10) {
time2 = temptime;
s2 -= (large - 1) / 30;
sx2 -= px / 30;
sy2 -= py / 30;
elements.forEach(function (v) {
if (v instanceof HTMLCanvasElement) {
v.style.transform =
"scale(" + s2 + ")translate(" + sx2 + "%, " + sy2 + "%)";
}
});
cishu2++;
if (cishu2 == 30) {
core.unregisterAnimationFrame("small");
elements.forEach(function (v) {
if (v instanceof HTMLCanvasElement) {
v.style.transform = "none";
}
});
}
}
});
// elements.forEach(function (v) {
// if (v instanceof HTMLCanvasElement) {
// v.style.transform = "none";
// }
// });
}, 1600);
};
},
"滑动转场": function () {
// 在此增加新插件
const defaultChange = {
left: "leftPortal", // 左箭头
up: "upPortal", // 上箭头
right: "rightPortal", // 右箭头
down: "downPortal", // 下箭头
upFloor: "upFloor", // 上楼
downFloor: "downFloor", // 下楼
};
const dirData = {
//方向坐标
up: [-1, 0],
down: [1, 0],
left: [0, -1],
right: [0, 1],
upFloor: [0, 0],
downFloor: [0, 0],
};
let allChangeEntries = Object.entries(defaultChange);
const move = document.createElement("canvas");
const speed = 12;
let modedata = 0;
move.width = 1248;
move.height = 1248;
const ctx = move.getContext("2d");
events.prototype.changeFloor = function (
floorId,
stair,
heroLoc,
time,
callback
) {
let block = core.getBlock(hero.loc.x, hero.loc.y);
var info = this._changeFloor_getInfo(floorId, stair, heroLoc, time);
if (info == null) {
if (callback) callback();
return;
}
floorId = info.floorId;
info.locked = core.status.lockControl;
core.dom.floorNameLabel.innerText = core.status.maps[floorId].title;
core.lockControl();
core.stopAutomaticRoute();
core.clearContinueAutomaticRoute();
core.status.replay.animate = true;
clearInterval(core.interval.onDownInterval);
delete core.animateFrame.tip;
core.interval.onDownInterval = "tmp";
this._changeFloor_beforeChange(info, block, callback);
};
events.prototype._changeFloor_beforeChange = function (
info,
block,
callback
) {
this._changeFloor_playSound();
if (
block &&
block?.event &&
!main.replayChecking &&
!core.isReplaying() &&
!core.hasFlag("__isFlying__") && !core.hasFlag("__fromLoad__")
) {
const dirEntries = allChangeEntries.find(
(v) => v[1] === block.event.id
);
if (block?.event?.trigger === "changeFloor" && dirEntries) {
const toFloorId = block.event.data.floorId;
const dir = dirEntries[0];
const data = core.ui._drawViewMaps_buildData(
core.floorIds.indexOf(core.status.floorId)
);
const dataTo = core.ui._drawViewMaps_buildData(
core.floorIds.indexOf(toFloorId)
);
const v = dirData[dir][1], // 水平数值
h = dirData[dir][0]; //竖直数值
ctx.clearRect(0, 0, 1248, 1248);
core.drawThumbnail(core.status.floorId, null, {
damage: data.damage,
ctx: ctx,
x: 416,
y: 416,
size: 1,
all: data.all,
});
if (dir !== "upFloor" && dir !== "downFloor") {
core.drawThumbnail(toFloorId, null, {
damage: dataTo.damage,
ctx: ctx,
x: 416 + 416 * v,
y: 416 + 416 * h,
size: 1,
all: dataTo.all,
});
var _run = function () {
var cb = function () {
modedata = 0;
core.clearUI();
core.clearMap("data");
core.events._changeFloor_changing(info, callback);
};
var animate = window.setInterval(
function () {
if (modedata >= 416) {
delete core.animateFrame.asyncId[animate];
clearInterval(animate);
cb();
} else {
core.clearUI();
core.clearMap("data");
core.canvas.data.drawImage(
move,
416 + modedata * v,
416 + modedata * h,
416,
416,
0,
0,
416,
416
);
let status = "leftFoot";
if (modedata > 208) {
status = "rightFoot";
}
const img = core.material.images.hero;
const heroIconArr = core.material.icons.hero;
const width = core.material.icons.hero.width || 32;
const height = core.material.icons.hero.height;
const heroIcon = heroIconArr[dir];
core.canvas.data.drawImage(
img,
(heroIcon[status] % 4) * width,
heroIcon.loc * height,
width,
height,
core.status.hero.loc.x * 32 -
core.bigmap.offsetX -
(modedata - (modedata * 32) / 416) * v,
core.status.hero.loc.y * 32 -
16 -
(modedata - (modedata * 32) / 416) * h,
width,
height
);
modedata += speed;
clearInterval(animate);
delete core.animateFrame.asyncId[animate];
_run();
}
},
core.status.replay.speed == 24 ?
1 :
10 / core.status.replay.speed
);
core.animateFrame.lastAsyncId = animate;
core.animateFrame.asyncId[animate] = cb;
};
_run();
return;
}
}
}
// 需要 setTimeout 执行,不然会出错
window.setTimeout(function () {
if (info.time == 0) core.events._changeFloor_changing(info, callback);
else
core.showWithAnimate(
core.dom.floorMsgGroup,
info.time / 2,
function () {
core.events._changeFloor_changing(info, callback);
}
);
}, 25);
};
},
"剧情cg": function () {
// 在此增加新插件
// 在此增加新插件
const cg = document.createElement("canvas"); //cg画布设置
cg.style.position = "absolute";
cg.style.zIndex = 300;
cg.style.display = "none";
cg.id = "cgText";
main.dom.gameGroup.insertAdjacentElement("afterend", cg);
cg.style.top = "50%";
cg.style.left = "50%";
cg.style.transform = "translate(-50%,-50%)";
const ctx = cg.getContext("2d");
main.dom.cgText = cg;
const logcanvas = document.createElement("canvas"); //cg画布设置
logcanvas.style.position = "absolute";
logcanvas.style.zIndex = 301;
logcanvas.style.display = "none";
logcanvas.id = "cgText";
main.dom.gameGroup.insertAdjacentElement("afterend", logcanvas);
logcanvas.style.top = "50%";
logcanvas.style.left = "50%";
logcanvas.style.transform = "translate(-50%,-50%)";
const logctx = logcanvas.getContext("2d");
main.dom.logcanvas = logcanvas;
logcanvas.onmouseup = function (e) {
//鼠标抬起
try {
if (!core.isPlaying()) return false;
core.unregisterAnimationFrame("skip");
let a = core.getFlag("skip", false);
core.setFlag("skip", false);
if (a) {
const data = core.clone(core.status.event.data.current);
core.insertAction(data);
core.doAction();
}
} catch (ee) {
console.error(ee);
}
};
logcanvas.onmousedown = function (e) {
//鼠标按下
try {
if (!core.isPlaying()) return false;
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor((e.clientX - left) / core.domStyle.scale),
py = Math.floor((e.clientY - top) / core.domStyle.scale);
core.ui.cgText.click(px * 3, py * 3);
} catch (ee) {
main.log(ee);
}
};
logcanvas.ontouchend = function (e) {
//触摸抬起
try {
if (!core.isPlaying()) return false;
core.unregisterAnimationFrame("skip");
core.setFlag("skip", false);
let a = core.getFlag("skip", false);
core.setFlag("skip", false);
if (a) {
const data = core.clone(core.status.event.data.current);
core.insertAction(data);
core.doAction();
}
} catch (ee) {}
};
logcanvas.ontouchstart = function (e) {
//触摸按下
try {
if (!core.isPlaying()) return false;
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor(
(e.targetTouches[0].clientX - left) / core.domStyle.scale
),
py = Math.floor(
(e.targetTouches[0].clientY - top) / core.domStyle.scale
);
core.ui.cgText.click(px * 3, py * 3);
} catch (ee) {
main.log(ee);
}
};
cg.onmouseup = function (e) {
//鼠标抬起
try {
if (!core.isPlaying()) return false;
core.unregisterAnimationFrame("skip");
let a = core.getFlag("skip", false);
core.setFlag("skip", false);
if (a) {
const data = core.clone(core.status.event.data.current);
core.insertAction(data);
core.doAction();
}
} catch (ee) {
console.error(ee);
}
};
cg.onmousedown = function (e) {
//鼠标按下
try {
if (!core.isPlaying()) return false;
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor((e.clientX - left) / core.domStyle.scale),
py = Math.floor((e.clientY - top) / core.domStyle.scale);
core.ui.cgText.click(px * 3, py * 3);
} catch (ee) {
main.log(ee);
}
};
cg.ontouchend = function (e) {
//触摸抬起
try {
if (!core.isPlaying()) return false;
core.unregisterAnimationFrame("skip");
core.setFlag("skip", false);
let a = core.getFlag("skip", false);
core.setFlag("skip", false);
if (a) {
const data = core.clone(core.status.event.data.current);
core.insertAction(data);
core.doAction();
}
} catch (ee) {
console.error(ee);
}
};
cg.ontouchstart = function (e) {
//触摸按下
try {
if (!core.isPlaying()) return false;
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor(
(e.targetTouches[0].clientX - left) / core.domStyle.scale
),
py = Math.floor(
(e.targetTouches[0].clientY - top) / core.domStyle.scale
);
core.ui.cgText.click(px * 3, py * 3);
} catch (ee) {
main.log(ee);
}
};
let auto = false;
class cgText {
constructor() {
this.nobg = false;
//绘制需要的变量
this.image = "";
this.head = { name: "face_050445.webp", px: -300 };
this.bodyList = [
{ name: "tati_050145a.webp", px: 100, filter: false },
{ name: "tati_120124.webp", px: 1100, filter: true },
];
this.name = "";
this.text = "";
this.time = 0;
this.WindowSkin = false;
this.sound = "";
this.beforeSound = 0;
this.wait = 1000;
this.memory = false;
this.textList = [];
this.page = 1;
this.overpage = 1;
this.log = false;
this.index = 0;
}
click(px, py) {
//点击效果
const makeBox = ([x, y], [w, h]) => {
return [
[x, y],
[x + w, y + h],
];
};
const inRect = ([x, y], [
[sx, sy],
[dx, dy]
]) => {
return sx <= x && x <= dx && sy <= y && y <= dy;
};
const pos = [px, py];
const savebox = makeBox([1700, 1100], [192, 96]);
const saveboxVertical = makeBox([52, 1700], [96, 192]);
const skipbox = makeBox([1700, 1000], [192, 96]);
const skipboxVertical = makeBox([152, 1700], [96, 192]);
const autobox = makeBox([1700, 900], [192, 96]);
const autoboxVertical = makeBox([252, 1700], [96, 192]);
const textbox = makeBox([1700, 800], [192, 96]);
const textboxVertical = makeBox([352, 1700], [96, 192]);
const backbox = makeBox([15, 35], [210, 90]);
const backboxVertical = makeBox([1123, 15], [90, 210]);
const pageupbox = makeBox([300, 1130], [200, 100]);
const pageupboxVertical = makeBox([18, 300], [100, 200]);
const pagedownbox = makeBox([1500, 1130], [200, 100]);
const pagedownboxVertical = makeBox([18, 1500], [100, 200]);
const soundbox = makeBox([550, 150], [100, 900]);
const soundboxVertical = makeBox([198, 550], [900, 100]);
if (this.log) {
if (
(core.domStyle.isVertical && inRect(pos, backboxVertical)) ||
(!core.domStyle.isVertical && inRect(pos, backbox))
) {
core.clearMap(logctx);
core.stopSound();
main.dom.logcanvas.style.display = "none";
this.log = false;
} else if (
(core.domStyle.isVertical && inRect(pos, pageupboxVertical)) ||
(!core.domStyle.isVertical && inRect(pos, pageupbox))
) {
core.clearMap(logctx);
if (this.page > 1) this.page--;
this.logdraw(this.page);
} else if (
(core.domStyle.isVertical && inRect(pos, pagedownboxVertical)) ||
(!core.domStyle.isVertical && inRect(pos, pagedownbox))
) {
core.clearMap(logctx);
if (this.page < this.overpage) this.page++;
this.logdraw(this.page);
} else if (
(core.domStyle.isVertical && inRect(pos, soundboxVertical)) ||
(!core.domStyle.isVertical && inRect(pos, soundbox))
) {
if (core.domStyle.isVertical) {
const sound =
this.textList[
(this.page - 1) * 6 +
Math.min(Math.floor((px - 198) / 150), 5)
][2];
core.stopSound();
core.playSound(sound);
} else {
const sound =
this.textList[
(this.page - 1) * 6 +
Math.min(Math.floor((py - 150) / 150), 5)
][2];
core.stopSound();
core.playSound(sound);
}
}
} else {
if (
(core.domStyle.isVertical &&
inRect(pos, skipboxVertical) &&
!this.WindowSkin) ||
(!core.domStyle.isVertical &&
!this.WindowSkin &&
inRect(pos, skipbox))
) {
auto = false;
let time = 0;
core.stopSound(this.beforeSound);
core.registerAnimationFrame("skip", true, (timestamp) => {
if (timestamp > time + 50) {
time = timestamp;
if (
core.status.event.id == "action" &&
core.status.event.data.type == "cgtext"
) {
core.setFlag("skip", true);
main.dom.cgText.style.display = "none";
core.doAction();
}
}
});
} else if (
(core.domStyle.isVertical &&
inRect(pos, textboxVertical) &&
!this.WindowSkin) ||
(!core.domStyle.isVertical &&
!this.WindowSkin &&
inRect(pos, textbox))
) {
auto = false;
this.log = true;
this.overpage = Math.floor(this.index / 6) + 1;
this.page = this.overpage;
logcanvas.style.display = "block";
const data = core.clone(core.status.event.data.current);
data.showAll = true;
data.time = 0;
data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
data.sound = "";
core.insertAction(data);
core.doAction();
this.logdraw(this.page);
} else if (
(core.domStyle.isVertical &&
inRect(pos, autoboxVertical) &&
!this.WindowSkin) ||
(!core.domStyle.isVertical &&
!this.WindowSkin &&
inRect(pos, autobox))
) {
auto = !auto;
const data = core.clone(core.status.event.data.current);
data.showAll = true;
data.time = 0;
data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
data.sound = "";
core.insertAction(data);
core.doAction();
} else if (
(core.domStyle.isVertical &&
inRect(pos, saveboxVertical) &&
!this.WindowSkin) ||
(!core.domStyle.isVertical &&
!this.WindowSkin &&
inRect(pos, savebox))
) {
//存档
auto = false;
if (core.status.event.animateUI) return;
if (core.status.event.interval != null) return;
const current = core.clone(core.status.event.data.current);
current.showAll = true;
current.time = 0;
current.sound = "";
current.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除当前事件所有打字机效果
cg.style.display = "none";
const data = [{ type: "callSave" }, current]; //插入存档事件
core.insertAction(data);
core.doAction();
} else if (!core.status.event.data) {
cg.style.display = "none";
core.ui._animateUI("hide", null, () => {
core.doAction();
});
} else {
// 正在淡入淡出的话不执行
if (core.status.event.animateUI) return;
auto = false;
// 打字机效果显示全部文字
if (core.status.event.interval != null) {
const data = core.clone(core.status.event.data?.current);
data.showAll = true;
data.time = 0;
data.text = this.text.replaceAll(/(\\(z))(\[.*?\])?/g, ""); //去除打字机暂停效果
data.sound = "";
core.insertAction(data);
core.doAction();
return;
} else {
core.stopSound(this.beforeSound);
}
cg.style.display = "none";
core.ui._animateUI("hide", null, () => {
core.doAction();
});
}
}
}
drawTextContent(ctx, content, config) {
//绘制多行文字并执行打字机效果
ctx = core.getContextByName(ctx);
// 设置默认配置项
var textAttribute =
core.status.textAttribute || core.initStatus.textAttribute;
var globalAttribute =
core.status.globalAttribute || core.initStatus.globalAttribute;
config = core.clone(config || {});
config.left = config.left || 0;
config.right =
config.left + (config.maxWidth == null ? core._PX_ : config.maxWidth);
config.top = config.top || 0;
config.color = core.arrayToRGBA(config.color || textAttribute.text);
if (config.bold == null) config.bold = textAttribute.bold;
config.italic = config.italic || false;
config.align = config.align || textAttribute.align || "left";
config.fontSize = config.fontSize || textAttribute.textfont;
config.lineHeight = config.lineHeight || config.fontSize * 1.3;
config.defaultFont = config.font = config.font || globalAttribute.font;
config.time = config.time || 0;
config.letterSpacing =
config.letterSpacing == null ?
textAttribute.letterSpacing || 0 :
config.letterSpacing;
config.index = 0;
config.currcolor = config.color;
config.currfont = config.fontSize;
config.lineMargin = Math.max(
Math.round(config.fontSize / 4),
config.lineHeight - config.fontSize
);
config.topMargin = parseInt(config.lineMargin / 2);
config.lineMaxHeight = config.lineMargin + config.fontSize;
config.offsetX = 0;
config.offsetY = 0;
config.line = 0;
config.blocks = [];
config.isHD = ctx == null || ctx.canvas.hasAttribute("isHD");
// 创建一个新的临时画布
var tempCtx = document.createElement("canvas").getContext("2d");
if (config.isHD && ctx) {
core.maps._setHDCanvasSize(
tempCtx,
ctx.canvas.width,
ctx.canvas.height
);
} else {
tempCtx.canvas.width = ctx == null ? 1 : ctx.canvas.width;
tempCtx.canvas.height = ctx == null ? 1 : ctx.canvas.height;
}
tempCtx.textBaseline = "top";
tempCtx.font = core.ui._buildFont(
config.fontSize,
config.bold,
config.italic,
config.font
);
tempCtx.fillStyle = config.color;
config = this._drawTextContent_draw(ctx, tempCtx, content, config);
return config;
}
_drawTextContent_draw(ctx, tempCtx, content, config) {
// Step 1: 绘制到tempCtx上并记录下图块信息
while (core.ui._drawTextContent_next(tempCtx, content, config));
if (ctx == null) return config;
// Step 2: 从tempCtx绘制到画布上
config.index = 0;
var _drawNext = function () {
if (config.index >= config.blocks.length) return false;
var block = config.blocks[config.index++];
if (block != null) {
// It works, why?
const scale = config.isHD ?
devicePixelRatio * core.domStyle.scale :
1;
ctx.restore();
ctx.save(); //保存设置
if (core.domStyle.isVertical) {
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
}
ctx.drawImage(
tempCtx.canvas,
block.left * scale,
block.top * scale,
block.width * scale,
block.height * scale,
config.left + block.left + block.marginLeft,
config.top + block.top + block.marginTop,
block.width,
block.height
);
ctx.restore();
}
return true;
};
if (config.time == 0) {
while (_drawNext());
if (
(auto && !core.ui.cgText.WindowSkin && !core.ui.cgText.sound) ||
(core.ui.cgText.sound && !core.musicStatus.soundStatus)
) {
setTimeout(() => {
if (auto) {
cg.style.display = "none";
core.ui._animateUI("hide", null, () => {
core.doAction();
});
}
}, core.ui.cgText.wait);
}
} else {
clearInterval(core.status.event.interval);
core.status.event.interval = setInterval(function () {
if (!_drawNext()) {
clearInterval(core.status.event.interval);
core.status.event.interval = null;
if (
(auto && !core.ui.cgText.WindowSkin && !core.ui.cgText.sound) ||
(core.ui.cgText.sound && !core.musicStatus.soundStatus)
)
setTimeout(() => {
if (auto) {
cg.style.display = "none";
core.ui._animateUI("hide", null, () => {
core.doAction();
});
}
}, core.ui.cgText.wait);
}
}, config.time);
}
return config;
}
update() {
this.background();
if (this.log) this.logdraw(this.page);
}
logdraw(page) {
if (core.domStyle.isVertical) {
logctx.canvas.width = 1248;
logctx.canvas.height = 2028;
logctx.save(); //保存设置
logctx.translate(1248, 0); //重新定位右上角为基准
logctx.rotate(Math.PI / 2); //旋转90度
} else {
logctx.canvas.width = 2028;
logctx.canvas.height = 1248;
}
core.fillRect(logctx, 0, 0, 2028, 1248, "rgba(0,0,0,0.5)");
core.setTextAlign(logctx, "center");
core.fillBoldText1(
logctx,
"◀离开",
110,
100,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
logctx.strokeStyle = "#FFFFFF";
logctx.lineWidth = 3;
logctx.beginPath();
logctx.moveTo(100, 150);
logctx.lineTo(1928, 150);
logctx.stroke();
let posy = 150;
const indexList = this.textList;
core.setTextAlign(logctx, "left");
for (
let i = (page - 1) * 6; i <= Math.min(this.index, page * 6 - 1); i++
) {
const text = this.textList[i][1].replaceAll(
/(\\(d|e|f|g|i|n|r|b|c|t|z))(\[.*?\])?/g,
""
); //取消打字机
const name = this.textList[i][0] ?
"【" + this.textList[i][0] + "】" :
"";
const sound = this.textList[i][2];
if (name) {
core.fillBoldText1(
logctx,
name,
150,
posy + 50,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(42, true)
);
}
if (sound) core.drawImage(logctx, "sound.webp", 550, posy + 30);
if (text.length < 30) {
core.fillBoldText1(
logctx,
text,
650,
posy + 50,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(42, true)
);
} else {
let text1 = text.slice(0, 30);
core.fillBoldText1(
logctx,
text1,
650,
posy + 50,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(42, true)
);
if (text.length > 60) {
let text2 = text.slice(30, 60);
let text3 = text.slice(60);
core.fillBoldText1(
logctx,
text2,
650,
posy + 100,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(42, true)
);
core.fillBoldText1(
logctx,
text3,
650,
posy + 150,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(42, true)
);
} else {
let text2 = text.slice(30);
core.fillBoldText1(
logctx,
text2,
650,
posy + 100,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(42, true)
);
}
}
logctx.strokeStyle = "#FFFFFF";
logctx.lineWidth = 3;
logctx.beginPath();
logctx.moveTo(100, posy);
logctx.lineTo(1928, posy);
logctx.stroke();
posy += 160;
}
logctx.beginPath();
logctx.moveTo(100, 1120);
logctx.lineTo(1928, 1120);
logctx.moveTo(100, 1110);
logctx.lineTo(1928, 1110);
logctx.stroke();
core.fillBoldText1(
logctx,
"上一页",
300,
1200,
page === 1 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
logctx,
page + "/" + this.overpage,
1000,
1200,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
logctx,
"下一页",
1500,
1200,
page === this.overpage ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
}
background() {
const img = core.material.images.images?.[this.image];
if (core.domStyle.isVertical) {
ctx.canvas.width = 1248;
ctx.canvas.height = 2028;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 2028;
ctx.canvas.height = 1248;
}
if (this.nobg) {} else {
if (img) {
//绘制背景
if (this.memory) ctx.filter = "sepia(50%)";
ctx.drawImage(img, 0, 0, 2028, 1248);
ctx.filter = "none";
} else {
core.fillRect(ctx, 0, 0, 2028, 1248);
}
}
this.bodyList.forEach((v) => {
//绘制立绘
const body = core.material.images.images?.[v.name];
if (v.filter) ctx.filter = "brightness(50%)";
if (body) {
if (!v.w && !v.h && !v.scale) v.scale = 1.7;
if (!v.w && !v.h) {
ctx.drawImage(
body,
0,
0,
body.width,
body.height,
v.px,
1248 - body.height * v.scale,
body.width * v.scale,
body.height * v.scale
);
} else {
ctx.drawImage(
body,
0,
0,
body.width,
body.height,
v.px,
1248 - (v.h ?? body.height),
v.w ?? body.width,
v.h ?? body.height
);
}
}
ctx.filter = "none";
});
if (core.isPlaying() && !this.WindowSkin)
core.drawWindowSkin(
"winskin.webp",
ctx,
30,
780,
1968,
436,
null,
null,
null,
3
); //绘制对话框
const head = core.material.images.images?.[this.head.name];
if (head) {
//绘制头像
ctx.drawImage(
head,
0,
0,
head.width,
head.height,
this.head.px,
1248 - head.height * 2.2,
head.width * 2.2,
head.height * 2.2
);
}
if (core.isPlaying() && !this.WindowSkin) {
core.drawWindowSkin(
"winskin.webp",
ctx,
1700,
800,
192,
96,
null,
null,
null,
3
);
core.fillBoldText1(
ctx,
"记 录",
1736,
866,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
core.drawWindowSkin(
"winskin.webp",
ctx,
1700,
1100,
192,
96,
null,
null,
null,
3
);
core.fillBoldText1(
ctx,
"存 档",
1736,
1166,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
core.drawWindowSkin(
"winskin.webp",
ctx,
1700,
1000,
192,
96,
null,
null,
null,
3
);
core.fillBoldText1(
ctx,
"▶▶",
1756,
1066,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
core.drawWindowSkin(
"winskin.webp",
ctx,
1700,
900,
192,
96,
null,
null,
null,
3
);
let autoText = "AUTO";
if (auto) autoText = "STOP";
core.fillBoldText1(
ctx,
autoText,
1722,
966,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
}
if (this.name)
core.fillBoldText1(
ctx,
`${this.name}`,
550,
880,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
); //绘制名字
if (
this.sound &&
core.sounds &&
!core.getFlag("skip", false) &&
core.musicStatus.soundStatus
) {
this.beforeSound = core.playSound(this.sound, null, () => {
if (
this.sound &&
auto &&
!this.WindowSkin &&
core.musicStatus.soundStatus
) {
setTimeout(() => {
if (auto) {
cg.style.display = "none";
core.ui._animateUI("hide", null, () => {
core.doAction();
});
}
}, this.wait);
}
});
}
if (this.text && !core.getFlag("skip", false)) {
//绘制对话
this.drawTextContent(ctx, this.text, {
left: 550,
top: 950,
bold: true,
color: "#FFFFFF",
align: "left",
fontSize: 48,
time: this.time || 0,
font: "Verdana",
maxWidth: 1000,
});
}
ctx.restore();
}
}
core.ui.cgText = new cgText();
},
"旁白": function () {
// 在此增加新插件
const over = document.createElement("canvas"); //over画布设置
over.style.position = "absolute";
over.style.zIndex = 310;
over.style.display = "none";
over.id = "over";
main.dom.gameGroup.insertAdjacentElement("afterend", over);
over.style.top = "50%";
over.style.left = "50%";
over.style.transform = "translate(-50%,-50%)";
const ctx = over.getContext("2d");
main.dom.over = over;
this.over = function (
image,
memory,
time = 100,
hidetime = 30,
sound = "",
textColor = "#FFFFFF",
boldColor = "#000000",
font = "bold 48px Verdana",
text = ""
) {
if (!core.isPlaying()) {
return core.doAction();
}
const img = core.material.images.images?.[image];
over.style.display = "block";
let frame = 0;
let sod = 0;
let now = 0;
core.registerAnimationFrame("over", true, (timestamp) => {
if (timestamp - now > 1000 / 60) {
now = timestamp;
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 416 * 3;
ctx.canvas.height = 676 * 3;
ctx.save(); //保存设置
ctx.translate(416 * 3, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 676 * 3;
ctx.canvas.height = 416 * 3;
}
ctx.globalAlpha = 1;
if (img) {
//绘制背景
if (memory) ctx.filter = "sepia(50%)";
ctx.drawImage(img, 0, 0, 676 * 3, 416 * 3);
ctx.filter = "none";
} else {
core.fillRect(ctx, 0, 0, 676 * 3, 416 * 3);
}
frame++;
// 绘制
if (frame <= hidetime)
core.setAlpha(ctx, 1 - (hidetime - frame) / hidetime);
if (frame > hidetime && frame <= hidetime + time) ctx.globalAlpha = 1;
if (frame > hidetime + time && frame <= hidetime * 2 + time)
core.setAlpha(ctx, 1 - (frame - hidetime - time) / hidetime);
const lisen =
sound && core.sounds[sound] && core.musicStatus.soundStatus;
if (frame == hidetime && lisen) {
sod = core.playSound(sound);
}
if (frame > hidetime * 2 + time) {
core.unregisterAnimationFrame("over");
ctx.restore();
over.style.display = "none";
core.stopSound(sod);
core.doAction();
return;
}
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
text,
1014,
624,
textColor,
boldColor,
6,
font
);
ctx.restore();
}
});
};
this.overlist = function (
image,
memory,
hidetime = 30,
list = [
{
text: "",
sound: "",
time: 50,
textColor: "#FFFFFF",
boldColor: "#000000",
font: "bold 48px Verdana",
frame: 0,
},
]
) {
if (!core.isPlaying()) {
return core.doAction();
}
const img = core.material.images.images?.[image];
over.style.display = "block";
let sod = 0;
let i = 0;
let now = 0;
core.registerAnimationFrame("overlist", true, (timestamp) => {
if (timestamp - now > 1000 / 60) {
now = timestamp;
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 416 * 3;
ctx.canvas.height = 676 * 3;
ctx.save(); //保存设置
ctx.translate(416 * 3, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 676 * 3;
ctx.canvas.height = 416 * 3;
}
ctx.globalAlpha = 1;
if (img) {
//绘制背景
if (memory) ctx.filter = "sepia(50%)";
ctx.drawImage(img, 0, 0, 676 * 3, 416 * 3);
ctx.filter = "none";
} else {
core.fillRect(ctx, 0, 0, 676 * 3, 416 * 3);
}
const a = list[i];
const b = list[i - 1];
const c = list[i - 2];
const d = list[i - 3];
let ay = 624,
by = 624,
cy = 624,
dy = 624;
if (i === 0 && !list[1]) {
core.over(
image,
memory,
a.time,
hidetime,
a.sound,
a.textColor,
a.boldColor,
a.font,
a.text
);
} else {
const numa =
parseInt(a?.font?.match(/\s*[\d.-]+[a-zA-Z%]*\s*/)?.[0].trim()) ||
48;
const numb =
parseInt(b?.font?.match(/\s*[\d.-]+[a-zA-Z%]*\s*/)?.[0].trim()) ||
48;
const numc =
parseInt(c?.font?.match(/\s*[\d.-]+[a-zA-Z%]*\s*/)?.[0].trim()) ||
48;
const numd =
parseInt(d?.font?.match(/\s*[\d.-]+[a-zA-Z%]*\s*/)?.[0].trim()) ||
48;
// 绘制
if (a) {
if (a.frame < hidetime / 2) {
a.frame++;
core.setAlpha(ctx, 1 - (hidetime - a.frame) / hidetime);
ay += ((numa * (hidetime - a.frame)) / hidetime) * 3;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
a.text,
1014,
ay,
a.textColor,
a.boldColor,
6,
a.font
);
}
if (a.frame === hidetime / 2) {
core.setAlpha(ctx, 1 - (hidetime - a.frame) / hidetime);
ay = 624 + ((numa * (hidetime - a.frame)) / hidetime) * 3;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
a.text,
1014,
ay,
a.textColor,
a.boldColor,
6,
a.font
);
if (!b) {
a.frame++;
i++;
}
}
}
if (b) {
if (b.frame > hidetime / 2 && b.frame <= hidetime) {
b.frame++;
core.setAlpha(ctx, 1 - (hidetime - b.frame) / hidetime);
by += ((numb * (hidetime - b.frame)) / hidetime) * 3;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
b.text,
1014,
by,
b.textColor,
b.boldColor,
6,
b.font
);
core.stopSound(sod);
}
const lisenb =
b.sound && core.sounds[b.sound] && core.musicStatus.soundStatus;
if (b.frame && lisenb) {
sod = core.playSound(sound);
}
if (b.frame > hidetime && b.frame < hidetime + b.time) {
b.frame++;
ctx.globalAlpha = 1;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
b.text,
1014,
by,
b.textColor,
b.boldColor,
6,
b.font
);
}
if (b.frame == hidetime + b.time) {
ctx.globalAlpha = 1;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
b.text,
1014,
by,
b.textColor,
b.boldColor,
6,
b.font
);
if (a) a.frame++;
if (b) b.frame++;
if (c) c.frame++;
i++;
}
}
if (c) {
if (
c.frame > hidetime + c.time &&
c.frame < (hidetime * 3) / 2 + c.time
) {
c.frame++;
core.setAlpha(
ctx,
1 - (c.frame - hidetime - c.time) / hidetime
);
cy -= ((numc * (c.frame - hidetime - c.time)) / hidetime) * 3;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
c.text,
1014,
cy,
c.textColor,
c.boldColor,
6,
c.font
);
}
if (c.frame === (hidetime * 3) / 2 + c.time) {
core.setAlpha(
ctx,
1 - (c.frame - hidetime - c.time) / hidetime
);
cy =
624 - ((numc * (c.frame - hidetime - c.time)) / hidetime) * 3;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
c.text,
1014,
cy,
c.textColor,
c.boldColor,
6,
c.font
);
if (!b) {
c.frame++;
i++;
}
}
}
if (d) {
if (
d.frame > (hidetime * 3) / 2 + d.time &&
d.frame < hidetime * 2 + d.time
) {
d.frame++;
core.setAlpha(
ctx,
1 - (d.frame - hidetime - d.time) / hidetime
);
dy -= ((numd * (d.frame - hidetime - d.time)) / hidetime) * 3;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
d.text,
1014,
dy,
d.textColor,
d.boldColor,
6,
d.font
);
}
if (d.frame == hidetime * 2 + d.time && !c) {
core.unregisterAnimationFrame("overlist");
ctx.restore();
over.style.display = "none";
core.stopSound(sod);
core.doAction();
return;
}
}
}
ctx.restore();
}
});
};
this.changebg = function (img1, memory1, img2, memory2, time, style) {
let globalAlpha1 = 0;
let globalAlpha2 = time;
img1 = core.material.images.images?.[img1];
img2 = core.material.images.images?.[img2];
over.style.display = "block";
let now = 0;
switch (style) {
case "引入":
core.registerAnimationFrame("bgin", true, (timestamp) => {
if (timestamp - now > 1000 / 60) {
now = timestamp;
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 1248;
ctx.canvas.height = 676 * 3;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 676 * 3;
ctx.canvas.height = 1248;
}
ctx.globalAlpha = globalAlpha1 / time;
if (img2) {
//绘制背景
if (memory2) ctx.filter = "sepia(50%)";
ctx.drawImage(img2, 0, 0, 676 * 3, 1248);
ctx.filter = "none";
} else {
core.fillRect(ctx, 0, 0, 676 * 3, 1248);
}
globalAlpha1++;
ctx.restore();
if (globalAlpha1 >= time) {
core.unregisterAnimationFrame("bgin");
over.style.display = "none";
core.doAction();
}
}
});
break;
case "引出":
core.registerAnimationFrame("bgout", true, (timestamp) => {
if (timestamp - now > 1000 / 60) {
now = timestamp;
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 1248;
ctx.canvas.height = 676 * 3;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 676 * 3;
ctx.canvas.height = 1248;
}
ctx.globalAlpha = globalAlpha2 / time;
if (img1) {
//绘制背景
if (memory1) ctx.filter = "sepia(50%)";
ctx.drawImage(img1, 0, 0, 676 * 3, 1248);
ctx.filter = "none";
} else {
core.fillRect(ctx, 0, 0, 676 * 3, 1248);
}
globalAlpha2--;
ctx.restore();
if (globalAlpha2 <= 0) {
core.unregisterAnimationFrame("bgout");
over.style.display = "none";
core.doAction();
}
}
});
break;
case "场景切换":
core.registerAnimationFrame("changebg", true, (timestamp) => {
if (timestamp - now > 1000 / 60) {
now = timestamp;
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 1248;
ctx.canvas.height = 676 * 3;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 676 * 3;
ctx.canvas.height = 1248;
}
ctx.globalAlpha = 1;
core.fillRect(ctx, 0, 0, 676 * 3, 1248);
ctx.globalAlpha = globalAlpha2 / time;
if (img1) {
//绘制背景
if (memory1) ctx.filter = "sepia(50%)";
ctx.drawImage(img1, 0, 0, 676 * 3, 1248);
ctx.filter = "none";
} else {
core.fillRect(ctx, 0, 0, 676 * 3, 1248);
}
ctx.globalAlpha = globalAlpha1 / time;
if (img2) {
//绘制背景
if (memory2) ctx.filter = "sepia(50%)";
ctx.drawImage(img2, 0, 0, 676 * 3, 1248);
ctx.filter = "none";
} else {
core.fillRect(ctx, 0, 0, 676 * 3, 1248);
}
globalAlpha2--;
globalAlpha1++;
ctx.restore();
if (globalAlpha2 <= 0 || globalAlpha1 >= time) {
core.unregisterAnimationFrame("changebg");
over.style.display = "none";
core.doAction();
}
}
});
break;
}
};
},
"回合制boss战": function () {
// 在此增加新插件
const boss = document.createElement("canvas"); //boss战画布设置
boss.style.position = "absolute";
boss.style.zIndex = 310;
boss.style.display = "none";
boss.id = "boss";
main.dom.gameGroup.insertAdjacentElement("afterend", boss);
boss.style.top = "50%";
boss.style.left = "50%";
boss.style.transform = "translate(-50%,-50%)";
const ctx = boss.getContext("2d");
main.dom.boss = boss;
const boss1 = document.createElement("canvas"); //boss战画布设置
boss1.style.position = "absolute";
boss1.style.zIndex = 300;
boss1.style.display = "none";
boss1.id = "boss1";
main.dom.gameGroup.insertAdjacentElement("afterend", boss1);
boss1.style.top = "50%";
boss1.style.left = "50%";
boss1.style.transform = "translate(-50%,-50%)";
const ctx1 = boss1.getContext("2d");
main.dom.boss1 = boss1;
const boss2 = document.createElement("canvas"); //boss战画布设置
boss2.style.position = "absolute";
boss2.style.zIndex = 301;
boss2.style.display = "none";
boss2.id = "boss2";
main.dom.gameGroup.insertAdjacentElement("afterend", boss2);
boss2.style.top = "50%";
boss2.style.left = "50%";
boss2.style.transform = "translate(-50%,-50%)";
main.dom.boss2 = boss2;
const ctx2 = boss2.getContext("2d");
const boss3 = document.createElement("canvas"); //boss战画布设置
boss3.style.position = "absolute";
boss3.style.zIndex = 302;
boss3.style.display = "none";
boss3.id = "boss3";
main.dom.gameGroup.insertAdjacentElement("afterend", boss3);
boss3.style.top = "50%";
boss3.style.left = "50%";
boss3.style.transform = "translate(-50%,-50%)";
main.dom.boss3 = boss3;
const ctx3 = boss3.getContext("2d");
const boss4 = document.createElement("canvas"); //boss战画布设置
boss4.style.position = "absolute";
boss4.style.zIndex = 303;
boss4.style.display = "none";
boss4.id = "boss4";
main.dom.gameGroup.insertAdjacentElement("afterend", boss4);
boss4.style.top = "50%";
boss4.style.left = "50%";
boss4.style.transform = "translate(-50%,-50%)";
const ctx4 = boss4.getContext("2d");
main.dom.boss4 = boss4;
const boss5 = document.createElement("canvas"); //boss战画布设置
boss5.style.position = "absolute";
boss5.style.zIndex = 304;
boss5.style.display = "none";
boss5.id = "boss5";
main.dom.gameGroup.insertAdjacentElement("afterend", boss5);
boss5.style.top = "50%";
boss5.style.left = "50%";
boss5.style.transform = "translate(-50%,-50%)";
const ctx5 = boss5.getContext("2d");
main.dom.boss5 = boss5;
const boss6 = document.createElement("canvas"); //boss战画布设置
boss6.style.position = "absolute";
boss6.style.zIndex = 305;
boss6.style.display = "none";
boss6.id = "boss6";
main.dom.gameGroup.insertAdjacentElement("afterend", boss6);
boss6.style.top = "50%";
boss6.style.left = "50%";
boss6.style.transform = "translate(-50%,-50%)";
const ctx6 = boss6.getContext("2d");
main.dom.boss6 = boss6;
const boss7 = document.createElement("canvas"); //boss战画布设置
boss7.style.position = "absolute";
boss7.style.zIndex = 306;
boss7.style.display = "none";
boss7.id = "boss7";
main.dom.gameGroup.insertAdjacentElement("afterend", boss7);
boss7.style.top = "50%";
boss7.style.left = "50%";
boss7.style.transform = "translate(-50%,-50%)";
const ctx7 = boss7.getContext("2d");
main.dom.boss7 = boss7;
const boss8 = document.createElement("canvas"); //boss战画布设置
boss8.style.position = "absolute";
boss8.style.zIndex = 307;
boss8.style.display = "none";
boss8.id = "boss8";
main.dom.gameGroup.insertAdjacentElement("afterend", boss8);
boss8.style.top = "50%";
boss8.style.left = "50%";
boss8.style.transform = "translate(-50%,-50%)";
const ctx8 = boss8.getContext("2d");
main.dom.boss8 = boss8;
const { imagelighter } = core.plugin.utils;
function getClick() {
return new Promise((resolve) => {
function handleBossClick(e) {
try {
e.preventDefault();
if (!core.isPlaying()) return false;
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor((e.clientX - left) / core.domStyle.scale),
py = Math.floor((e.clientY - top) / core.domStyle.scale);
let x, y;
if (core.domStyle.isVertical) {
x = py * 3;
y = 1248 - px * 3;
} else {
x = px * 3;
y = py * 3;
}
core.ui.boss.click(x, y);
if (
x > 1050 &&
x < 1450 &&
y > 250 &&
y < 390 &&
!core.ui.boss.show &&
core.ui.boss.hasEnemy()
) {
// 移除事件监听器
boss.removeEventListener("click", handleBossClick);
resolve("普通攻击"); //有小怪时点击普通攻击
return; // 退出函数
}
if (
x > 850 &&
x < 1250 &&
y > 250 &&
y < 390 &&
!core.ui.boss.show &&
!core.ui.boss.hasEnemy()
) {
// 移除事件监听器
boss.removeEventListener("click", handleBossClick);
resolve("普通攻击"); //无小怪时点击普通攻击
return; // 退出函数
}
} catch (ee) {
main.log(ee);
}
}
boss.addEventListener("click", handleBossClick);
});
}
const { sleep } = core.plugin.utils;
class Boss {
constructor() {
//绘制需要的变量
this.enemyfarme = 0;
this.bg = "bg_3512.webp";
this.heroImage = "tati_310101.webp";
this.hero = {
id: "hero",
name: "凯伊姆",
hp: 1000,
atk: 150,
def: 100,
spell: 100,
speed: 10,
mdef: 10,
};
this.boss = {
name: "菲奥奈",
id: "angel",
image: "tati_050143.webp",
hp: 1000,
atk: 200,
def: 100,
speed: 10,
mdef: 10,
skill: ["普通攻击", "重斩"],
index: 0,
};
this.enemy = [];
this.enemy = [{
name: "小蝙蝠",
id: "bat",
image: "tati_020125a.webp",
hp: 1000,
atk: 100,
def: 20,
speed: 10,
mdef: 10,
skill: ["普通攻击", "重斩"],
index: 0,
},
{
name: "红蝙蝠",
id: "redBat",
image: "tati_050301.webp",
hp: 1000,
atk: 100,
def: 120,
speed: 10,
mdef: 10,
skill: ["普通攻击", "重斩"],
index: 0,
},
{
name: "大蝙蝠",
id: "bigBat",
image: "tati_120101.webp",
hp: 1000,
atk: 100,
def: 100,
speed: 10,
mdef: 10,
skill: ["普通攻击", "重斩"],
index: 0,
},
{
name: "绿色史莱姆",
id: "greenSlime",
image: "tati_340115.webp",
hp: 1000,
atk: 100,
def: 100,
speed: 10,
mdef: 10,
skill: ["普通攻击", "重斩"],
index: 0,
},
{
name: "红色史莱姆",
id: "redSlime",
image: "tati_430101.webp",
hp: 1000,
atk: 100,
def: 100,
speed: 10,
mdef: 10,
skill: ["普通攻击", "重斩"],
index: 0,
},
{
name: "黑色史莱姆",
id: "blackSlime",
image: "tati_440101.webp",
hp: 1000,
atk: 100,
def: 100,
speed: 10,
mdef: 10,
skill: ["普通攻击", "重斩"],
index: 0,
},
];
this.selection = "boss";
this.herobuff = [
{ id: "sword1", count: 1 },
{ id: "fly", count: 30 },
];
this.bossbuff = [
{ id: "fly", count: 2 },
{ id: "book", count: 12, hp: 100 },
];
this.enemybuff = [
[],
[],
[],
[],
[],
[]
];
this.skills = {
//技能列表,便于调用(可通过this.skills[name]调用)
菲奥奈: ["普通攻击", "重斩"],
};
this.skillShow = {
//技能说明
普通攻击: "常规攻击形式,伤害为自身攻击-对手防御",
};
this.turn = 0;
this.playingAnimate = new Set();
this.playerTurn = false;
this.show = false;
}
buffshow(a) {
//buff说明均未实装之后改为实际需要的buff
let text = "";
switch (a.id) {
case "sword1":
text = `\r[rgb(30,66,30)]剑气:\r造成伤害提升\r[red]${
10 * a.count
}%\r`;
break;
case "fly":
text = `羽翼守护:接下来\r[gold]${a.count}回合\r不受速度差值影响`;
break;
case "book":
text = `恢复之书:接下来\r[#000033]${a.count}回合\r,每回合行动时回复\r[green]${a.hp}\r点生命`;
break;
}
return text;
}
shake(hero) {
let time = 0,
farme = 0;
const xlist = [Math.random() * 40 + 10, Math.random() * -40 - 10, 0];
return new Promise((resolve) => {
core.registerAnimationFrame("shake", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
const x = xlist[Math.floor(farme / 5) % 3];
if (hero) {
const img = imagelighter(
core.material.images.images[this.heroImage]
);
core.clearMap(ctx2);
if (core.domStyle.isVertical) {
ctx2.canvas.width = 1248;
ctx2.canvas.height = 2028;
ctx2.save(); //保存设置
ctx2.translate(1248, 0); //重新定位右上角为基准
ctx2.rotate(Math.PI / 2); //旋转90度
} else {
ctx2.canvas.width = 2028;
ctx2.canvas.height = 1248;
}
core.drawImage(ctx2, img, x, 168, 750, 1080);
ctx2.restore();
} else {
core.clearMap(ctx3);
if (core.domStyle.isVertical) {
ctx3.canvas.width = 1248;
ctx3.canvas.height = 2028;
ctx3.save(); //保存设置
ctx3.translate(1248, 0); //重新定位右上角为基准
ctx3.rotate(Math.PI / 2); //旋转90度
} else {
ctx3.canvas.width = 2028;
ctx3.canvas.height = 1248;
}
if (this.selection === "boss" || this.selection === "") {
const img = imagelighter(
core.material.images.images[this.boss.image]
);
core.drawImage(ctx3, img, 1400 + x, 168, 750, 1080);
} else {
const img = imagelighter(
core.material.images.images[
this.enemy[this.selection].image
]
);
core.drawImage(ctx3, img, 1400 + x, 168, 750, 1080);
}
ctx3.restore();
}
farme++;
if (farme > 30) {
core.unregisterAnimationFrame("shake");
this.drawhero();
this.drawboss();
resolve();
}
}
});
});
}
popDamage(damage, onhero) {
if (core.domStyle.isVertical) {
ctx.canvas.width = 1248;
ctx.canvas.height = 2028;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 2028;
ctx.canvas.height = 1248;
}
let color = "#FFFFFF";
if (typeof damage === "number") {
color = damage < 0 ? "#22FF44" : "lightcoral";
}
let farme = 0,
time = 0;
let posx = onhero ? 300 : 1800;
const speed = 9;
return new Promise((resolve) => {
core.registerAnimationFrame("popDamageonboss", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
core.clearMap(ctx);
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
damage,
posx,
800 - speed * farme,
color,
"#000000",
6,
"bold 72px Arial"
);
farme++;
ctx2.restore();
if (farme > 30) {
core.unregisterAnimationFrame("popDamageonboss");
core.clearMap(ctx);
resolve();
}
}
});
});
}
async skill(sk, a, b) {
//a为发起方属性,sk为技能名
let damage = 0;
switch (
sk //所有技能效果及动画写在这里
) {
case "普通攻击":
damage = Math.max(a.atk - b.def, 0); //基础伤害
damage = Math.floor((damage * a.speed) / b.speed); //速度比值伤害加成
b.hp -= damage; //承受伤害
if (b.id === "hero")
core.status.hero.statistics.battleDamage += damage; //数据统计记录伤害
if (damage === 0) damage = "抵抗";
switch (
a.id //根据id选取不同的特效
) {
case "hero":
this.popDamage(damage, false);
if (damage > 0 && typeof damage !== "string") this.shake(false);
await this.playanimate("sword", 1800, 800);
break;
case "angel":
this.popDamage(damage, true);
if (damage > 0 && typeof damage !== "string") this.shake(true);
await this.playanimate("sword", 350, 800); //播放动画sword
break;
case "bat":
this.popDamage(damage, true);
if (damage > 0 && typeof damage !== "string") this.shake(true);
await this.playanimate("sword", 350, 800); //播放动画sword
break;
case "redBat":
this.popDamage(damage, true);
if (damage > 0 && typeof damage !== "string") this.shake(true);
await this.playanimate("Fire01", 350, 800); //播放动画Fire01
break;
case "bigBat":
this.popDamage(damage, true);
if (damage > 0 && typeof damage !== "string") this.shake(true);
await this.playanimate("Fire02", 350, 800); //播放动画Fire02
break;
case "greenSlime":
this.popDamage(damage, true);
if (damage > 0 && typeof damage !== "string") this.shake(true);
await this.playanimate("005-Attack03", 350, 800); //播放动画005-Attack03
break;
case "redSlime":
this.popDamage(damage, true);
if (damage > 0 && typeof damage !== "string") this.shake(true);
await this.playanimate("012-Heal01", 350, 800); //播放动画012-Heal01
break;
case "blackSlime":
this.popDamage(damage, true);
if (damage > 0 && typeof damage !== "string") this.shake(true);
await this.playanimate("sword", 350, 800); //播放动画sword
break;
}
break; //下面写其余技能
}
await sleep(500); //等待1000ms
}
click(px, py) {
//点击效果
const makeBox = ([x, y], [w, h]) => {
return [
[x, y],
[x + w, y + h],
];
};
const inRect = ([x, y], [
[sx, sy],
[dx, dy]
]) => {
return sx <= x && x <= dx && sy <= y && y <= dy;
};
const pos = [px, py];
if (main.replayChecking || core.isReplaying()) return;
const enemyStatusBox = makeBox([50, 50], [1900, 200]),
heroStatusBox = makeBox([600, 920], [900, 300]),
bossBox = makeBox([800, 300], [100, 150]),
enemyBox = makeBox([700, 500], [300, 300]),
imageBox = makeBox([1500, 250], [550, 1000]);
if (this.show) {
//清除展示画面
this.show = !this.show;
core.clearMap(ctx8);
} else {
if (
inRect(pos, enemyStatusBox) &&
this.selection !== "" &&
this.selection !== "boss" &&
this.hasEnemy()
) {
//绘制怪物详情
this.show = !this.show;
this.moreShow(this.selection);
} else if (
inRect(pos, enemyStatusBox) &&
(!this.hasEnemy() || this.selection === "boss")
) {
//绘制boss详情
this.show = !this.show;
this.moreShow("boss");
} else if (inRect(pos, heroStatusBox)) {
//绘制勇士详情
this.show = !this.show;
this.moreShow("hero");
} else if (inRect(pos, bossBox) && this.hasEnemy()) {
//切换selection为boss
this.selection = "boss";
this.update();
} else if (inRect(pos, enemyBox) && this.hasEnemy()) {
//切换selection为enemy
const symbol =
Math.floor((px - 700) / 100) + Math.floor((py - 500) / 150) * 3;
if (this.enemy[symbol] && this.enemy[symbol].hp > 0) {
this.selection = symbol;
this.update();
}
}
}
}
drawchoose() {
if (core.domStyle.isVertical) {
ctx7.canvas.width = 1248;
ctx7.canvas.height = 2028;
ctx7.save(); //保存设置
ctx7.translate(1248, 0); //重新定位右上角为基准
ctx7.rotate(Math.PI / 2); //旋转90度
} else {
ctx7.canvas.width = 2028;
ctx7.canvas.height = 1248;
}
boss7.style.display = "block";
core.clearMap(ctx7);
if (this.hasEnemy()) {
core.drawWindowSkin(
"winskin.webp",
ctx7,
1050,
250,
400,
660,
null,
null,
null,
3
);
core.fillBoldText1(
ctx7,
"普通攻击",
1120,
350,
"#FFFFFF",
"#000000",
6,
"bold 64px Verdana"
);
core.drawLine(ctx7, 1050, 390, 1450, 390, "#FFFFFF", 6);
} else {
core.drawWindowSkin(
"winskin.webp",
ctx7,
850,
250,
400,
660,
null,
null,
null,
3
);
core.fillBoldText1(
ctx7,
"普通攻击",
920,
350,
"#FFFFFF",
"#000000",
6,
"bold 64px Verdana"
);
core.drawLine(ctx7, 850, 390, 1250, 390, "#FFFFFF", 6);
}
}
moreShow(select) {
if (core.domStyle.isVertical) {
ctx8.canvas.width = 1248;
ctx8.canvas.height = 2028;
ctx8.save(); //保存设置
ctx8.translate(1248, 0); //重新定位右上角为基准
ctx8.rotate(Math.PI / 2); //旋转90度
} else {
ctx8.canvas.width = 2028;
ctx8.canvas.height = 1248;
}
core.clearMap(ctx8);
core.fillRect(ctx8, 100, 100, 1800, 1000, "rgba(0,0,0,0.7)");
core.drawWindowSkin(
"winskin.webp",
ctx8,
100,
100,
1800,
1000,
null,
null,
null,
3
);
let posy = 200;
switch (select) {
case "hero":
//勇士技能/buff
core.fillBoldText1(
ctx8,
this.hero.name,
1000,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
if (this.herobuff.length === 0) {
core.fillBoldText1(
ctx8,
"当前无特殊状态",
200,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
} else {
this.herobuff.forEach((v) => {
core.drawTextContent(ctx8, this.buffshow(v), {
left: 200,
top: posy,
bold: true,
color: "#FFFFFF",
align: "left",
fontSize: 48,
time: 0,
font: "Verdana",
maxWidth: 1600,
});
posy += 100;
});
}
posy += 50;
core.fillBoldText1(
ctx8,
"技能说明",
1000,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
core.fillBoldText1(
ctx8,
"普通攻击:" + this.skillShow["普通攻击"],
200,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
break;
case "boss":
core.fillBoldText1(
ctx8,
this.boss.name,
1000,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
//boss技能/buff
if (this.bossbuff.length === 0) {
core.fillBoldText1(
ctx8,
"当前无特殊状态",
200,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
} else {
this.bossbuff.forEach((v) => {
core.drawTextContent(ctx8, this.buffshow(v), {
left: 200,
top: posy,
bold: true,
color: "#FFFFFF",
align: "left",
fontSize: 48,
time: 0,
font: "Verdana",
maxWidth: 1600,
});
posy += 100;
});
}
posy += 50;
core.fillBoldText1(
ctx8,
"当前技能",
1000,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
core.fillBoldText1(
ctx8,
this.boss.skill[this.boss.index] +
this.skillShow[this.boss.skill[this.boss.index]],
200,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
break;
default:
const enemy = this.enemy[select];
const enemybuff = this.enemybuff[select];
core.fillBoldText1(
ctx8,
enemy.name,
1000,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
if (enemybuff.length === 0) {
core.fillBoldText1(
ctx8,
"当前无特殊状态",
200,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
} else {
enemybuff.forEach((v) => {
core.drawTextContent(ctx8, this.buffshow(v), {
left: 200,
top: posy,
bold: true,
color: "#FFFFFF",
align: "left",
fontSize: 48,
time: 0,
font: "Verdana",
maxWidth: 1600,
});
posy += 100;
});
}
posy += 50;
core.fillBoldText1(
ctx8,
"当前技能",
1000,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
posy += 100;
core.fillBoldText1(
ctx8,
enemy.skill[enemy.index] +
this.skillShow[enemy.skill[enemy.index]],
200,
posy,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
break;
}
ctx8.restore();
}
hasEnemy() {
let enemy = false;
this.enemy.forEach((v) => {
if (v.hp > 0) enemy = true;
});
return enemy;
}
async bossStart() {
boss.style.display = "block";
this.selection = "boss";
this.playerturn = false;
this.show = false;
this.turn = 0;
core.lockControl();
await this.blackBg();
this.moveboss();
await this.movehero();
await this.moveStatus();
this.update();
this.fight();
}
async bossEnd() {
hero.hp = this.hero.hp;
this.selection = "boss";
await this.close();
core.unlockControl();
core.updateStatusBar();
if (hero.hp <= 0) {
hero.hp = 0;
core.events.lose("BOSS战失败");
}
}
async fight() {
await this.drawturn();
const fightList = [];
fightList.push(["hero", this.hero.speed]);
if (this.boss.hp > 0) fightList.push(["boss", this.boss.speed]);
this.enemy.forEach((v, i) => {
if (v.id && v.hp > 0) fightList.push([i, v.speed]);
});
fightList.sort((a, b) => b[1] - a[1]);
let damage;
for (const v of fightList) {
switch (v[0]) {
case "hero":
this.drawchoose();
const skill = await getClick();
let select = this.boss;
if (this.selection !== "" && this.selection !== "boss")
select = this.enemy[this.selection];
this.skill(skill, this.hero, select);
core.clearMap(ctx7);
break;
case "boss":
if (this.boss.hp > 0) {
this.selection = "boss";
this.update();
await sleep(500); //等待500ms
//这里写boss技能的效果
this.skill(
this.boss.skill[this.boss.index],
this.boss,
this.hero
);
this.boss.index++;
if (this.boss.index >= this.boss.skill.length)
this.boss.index = 0;
}
break;
default:
const enemy = this.enemy[v[0]];
if (enemy.hp > 0) {
this.selection = v[0];
this.update();
await sleep(500); //等待500ms
this.skill(enemy.skill[enemy.index], enemy, this.hero);
enemy.index++;
if (enemy.index >= enemy.skill.length) enemy.index = 0;
}
break;
}
await sleep(1000);
this.selection = "";
this.update();
}
let end = true;
if (this.boss.hp > 0) end = false;
this.enemy.forEach((v) => {
if (v.hp > 0) end = false;
});
if (this.hero.hp <= 0) end = true;
if (end) {
this.bossEnd();
} else {
this.fight();
}
}
drawturn() {
boss8.style.display = "block";
this.turn += 1;
let time = 0,
frame = 0,
frame2 = 0,
right = 1,
once = 10;
return new Promise((resolve) => {
core.registerAnimationFrame("drawturn", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
frame += 1 * right;
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 1248;
ctx.canvas.height = 2028;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 2028;
ctx.canvas.height = 1248;
}
core.fillRect(
ctx,
0,
624 - once * frame,
2028,
once * frame * 2,
"rgba(0,0,0,0.7)"
);
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
"ROUND " + this.turn,
1014,
624,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(frame * 5, true)
);
if (1014 - once * frame * 7 <= 0) {
frame -= 1;
frame2++;
if (frame2 > 30) right = -1;
}
if (frame < 0) {
core.clearMap(ctx);
core.unregisterAnimationFrame("drawturn");
resolve();
}
}
});
});
}
drawenemy() {
let block,
time = 0;
boss5.style.display = "block";
core.registerAnimationFrame("enemyanimate", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
this.enemyfarme += 1;
let animate = Math.floor(this.enemyfarme / 30),
posx = 700,
posy = 500;
core.clearMap(ctx5);
if (core.domStyle.isVertical) {
ctx5.canvas.width = 1248;
ctx5.canvas.height = 2028;
ctx5.save(); //保存设置
ctx5.translate(1248, 0); //重新定位右上角为基准
ctx5.rotate(Math.PI / 2); //旋转90度
} else {
ctx5.canvas.width = 2028;
ctx5.canvas.height = 1248;
}
if (this.hasEnemy()) {
core.drawWindowSkin(
"winskin.webp",
ctx5,
650,
250,
400,
660,
null,
null,
null,
3
);
if (this.selection === "boss")
core.strokeRect(ctx5, 800, 300, 100, 150, "yellow", 6);
const bossBlock = core.getBlockInfo(this.boss.id);
core.drawImage(
ctx5,
bossBlock.image,
32 * (animate % 4),
bossBlock.posY * 48,
32,
48,
800,
300,
96,
144
);
core.drawImage(
ctx5,
"hero.webp",
32 * (animate % 4),
144,
32,
48,
800,
750,
96,
144
);
for (let i = 0; i < this.enemy.length; i++) {
if (this.enemy[i].id && this.enemy[i].hp > 0) {
block = core.getBlockInfo(this.enemy[i].id);
} else {
posx += 100;
if (i === 2) {
posx = 700;
posy += 150;
}
continue;
}
if (block.cls === "enemys") {
core.drawImage(
ctx5,
block.image,
32 * (animate % 2),
block.posY * 32,
32,
32,
posx,
posy,
96,
96
);
if (this.selection === i)
core.strokeRect(ctx5, posx, posy, 100, 100, "yellow", 6);
} else {
core.drawImage(
ctx5,
block.image,
32 * (animate % 4),
block.posY * 48,
32,
48,
posx,
posy,
96,
144
);
if (this.selection === i)
core.strokeRect(ctx5, posx, posy, 100, 150, "yellow", 6);
}
posx += 100;
if (i === 2) {
posx = 700;
posy += 150;
}
}
}
ctx5.restore();
}
});
}
drawhero() {
core.clearMap(ctx2);
if (core.domStyle.isVertical) {
ctx2.canvas.width = 1248;
ctx2.canvas.height = 2028;
ctx2.save(); //保存设置
ctx2.translate(1248, 0); //重新定位右上角为基准
ctx2.rotate(Math.PI / 2); //旋转90度
} else {
ctx2.canvas.width = 2028;
ctx2.canvas.height = 1248;
}
core.drawImage(ctx2, this.heroImage, 0, 168, 750, 1080);
ctx2.restore();
}
movehero() {
boss2.style.display = "block";
core.clearMap(ctx2);
let time = 0,
px = -200;
return new Promise((resolve) => {
core.registerAnimationFrame("moveheroImage", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
core.clearMap(ctx2);
if (core.domStyle.isVertical) {
ctx2.canvas.width = 1248;
ctx2.canvas.height = 2028;
ctx2.save(); //保存设置
ctx2.translate(1248, 0); //重新定位右上角为基准
ctx2.rotate(Math.PI / 2); //旋转90度
} else {
ctx2.canvas.width = 2028;
ctx2.canvas.height = 1248;
}
core.drawImage(ctx2, this.heroImage, px, 168, 750, 1080);
px += 10;
ctx2.restore();
if (px >= 0) {
core.unregisterAnimationFrame("moveheroImage");
this.drawhero();
resolve();
}
}
});
});
}
update() {
core.drawImage(ctx1, this.bg, 0, 0, 2028, 1248);
this.drawboss();
this.drawhero();
this.drawStatus();
let dodraw = false;
for (let i = 0; i < this.enemy.length; i++) {
if (this.enemy[i].id && this.enemy[i].hp > 0) {
dodraw = true;
}
}
if (dodraw === true) this.drawenemy();
}
playanimate(name, x, y, scalex = 10, scaley = 10) {
const one = {
name: name,
x: x,
y: y,
scalex: scalex,
scaley: scaley,
farme: 0,
};
let time = 0;
boss6.style.display = "block";
return new Promise((resolve) => {
core.registerAnimationFrame("animateboss", true, (timestamp) => {
if (timestamp - time > 1000 / 60) {
time = timestamp;
core.clearMap(ctx6);
const data = flags["animate_" + one.name];
if (core.domStyle.isVertical) {
ctx6.canvas.width = 1248;
ctx6.canvas.height = 2028;
ctx6.save(); //保存设置
ctx6.translate(1248, 0); //重新定位右上角为基准
ctx6.rotate(Math.PI / 2); //旋转90度
} else {
ctx6.canvas.width = 2028;
ctx6.canvas.height = 1248;
}
if (!data) {
core.unregisterAnimationFrame("animateboss");
resolve();
} else {
data.imageList.forEach(function (image) {
if (
one.farme >= (image.beforefarme ?? 0) &&
one.farme <= (image.afterfarme ?? data.allFarme)
) {
const img = core.material.images.images?.[image.image];
if (img) {
const gla = image.globalAlpha ?? 100;
const agla = image.aglobalAlpha ?? gla,
beforefarme = image.beforefarme ?? 0;
const afterfarme = image.afterfarme ?? data.allFarme;
ctx6.globalAlpha =
(gla +
((agla - gla) * (one.farme - beforefarme)) /
(afterfarme - beforefarme || 1)) /
100;
const cx =
(image.cx ?? 0) +
(((image.acx ?? 0) - (image.cx ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
cy =
(image.cy ?? 0) +
(((image.acy ?? 0) - (image.cy ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
cw =
(image.cw ?? img.width) +
(((image.acw ?? img.width) -
(image.cw ?? img.width)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
ch =
(image.ch ?? img.height) +
(((image.acw ?? img.height) -
(image.cw ?? img.height)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
x =
(image.x ?? 0) +
(((image.ax ?? 0) - (image.x ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
y =
(image.y ?? 0) +
(((image.ay ?? 0) - (image.y ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
w =
(image.w ?? one.width) +
(((image.aw ?? one.width) - (image.w ?? one.width)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
h =
(image.h ?? one.height) +
(((image.aw ?? one.height) -
(image.w ?? one.height)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
angle =
(Math.PI *
((image.image.angle ?? 0) +
(((image.aangle ?? 0) -
(image.image.angle ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1))) /
180;
core.drawImage(
ctx6,
img,
cx,
cy,
cw,
ch,
one.x + (x - data.px) * one.scalex,
one.y + (y - data.py) * one.scaley,
w * one.scalex,
h * one.scaley,
angle
);
}
}
});
data.soundList.forEach(function (sound) {
const lisen =
sound.sound &&
core.sounds[sound.sound] &&
core.musicStatus.soundStatus;
if (one.farme == sound.startfarme && lisen) {
if (sound.stopbefore) core.stopSound();
core.playSound(sound.sound);
}
});
one.farme++;
ctx6.restore();
if (one.farme > data.allFarme) {
core.clearMap(ctx6);
core.unregisterAnimationFrame("animateboss");
resolve();
}
}
}
});
});
}
drawboss() {
core.clearMap(ctx3);
if (core.domStyle.isVertical) {
ctx3.canvas.width = 1248;
ctx3.canvas.height = 2028;
ctx3.save(); //保存设置
ctx3.translate(1248, 0); //重新定位右上角为基准
ctx3.rotate(Math.PI / 2); //旋转90度
} else {
ctx3.canvas.width = 2028;
ctx3.canvas.height = 1248;
}
if (this.selection === "boss" || this.selection === "") {
core.drawImage(ctx3, this.boss.image, 1400, 168, 750, 1080);
} else {
core.drawImage(
ctx3,
this.enemy[this.selection].image,
1400,
168,
750,
1080
);
}
ctx3.restore();
}
moveboss() {
boss3.style.display = "block";
core.clearMap(ctx3);
let time = 0,
px = 1600;
return new Promise((resolve) => {
core.registerAnimationFrame("movebossImage", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
core.clearMap(ctx3);
if (core.domStyle.isVertical) {
ctx3.canvas.width = 1248;
ctx3.canvas.height = 2028;
ctx3.save(); //保存设置
ctx3.translate(1248, 0); //重新定位右上角为基准
ctx3.rotate(Math.PI / 2); //旋转90度
} else {
ctx3.canvas.width = 2028;
ctx3.canvas.height = 1248;
}
core.drawImage(ctx3, this.boss.image, px, 168, 750, 1080);
px -= 10;
ctx3.restore();
if (px <= 1400) {
core.unregisterAnimationFrame("movebossImage");
this.drawboss();
resolve();
}
}
});
});
}
drawStatus() {
core.clearMap(ctx4);
if (core.domStyle.isVertical) {
ctx4.canvas.width = 1248;
ctx4.canvas.height = 2028;
ctx4.save(); //保存设置
ctx4.translate(1248, 0); //重新定位右上角为基准
ctx4.rotate(Math.PI / 2); //旋转90度
} else {
ctx4.canvas.width = 2028;
ctx4.canvas.height = 1248;
}
core.drawWindowSkin(
"winskin.webp",
ctx4,
600,
920,
900,
300,
null,
null,
null,
3
);
core.fillBoldText1(
ctx4,
hero.name,
630,
1000,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"状态",
880,
1000,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"生命 " + this.hero.hp,
630,
1070,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"攻击 " + this.hero.atk,
630,
1120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"防御 " + this.hero.def,
630,
1170,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"法强 " + this.hero.spell,
1080,
1070,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"法抗 " + this.hero.mdef + "%",
1080,
1120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"速度 " + this.hero.speed,
1080,
1170,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
let posx = 980;
this.herobuff.forEach((v) => {
if (v) {
core.drawIcon(ctx4, v.id, posx, 950, 64, 64);
core.setTextAlign(ctx4, "right");
core.fillBoldText1(
ctx4,
v.count,
posx + 50,
1000,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(24, true)
);
core.setTextAlign(ctx4, "left");
posx += 80;
}
});
if (this.selection === "boss" || !this.hasEnemy()) {
core.drawWindowSkin(
"winskin.webp",
ctx4,
50,
50,
1900,
200,
null,
null,
null,
3
);
core.fillBoldText1(
ctx4,
this.boss.name,
100,
120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"状态",
500,
120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"当前技能",
1400,
120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
const bosstext = this.boss.skill[this.boss.index];
core.fillBoldText1(
ctx4,
bosstext,
1600,
120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"生命 " + this.boss.hp,
100,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"攻击 " + this.boss.atk,
500,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"防御 " + this.boss.def,
900,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"法抗 " + this.boss.mdef + "%",
1300,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"速度 " + this.boss.speed,
1700,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
let posx = 600;
this.bossbuff.forEach((v) => {
if (v) {
core.drawIcon(ctx4, v.id, posx, 80, 64, 64);
core.setTextAlign(ctx4, "right");
core.fillBoldText1(
ctx4,
v.count,
posx + 50,
130,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(24, true)
);
core.setTextAlign(ctx4, "left");
posx += 80;
}
});
} else if (this.selection === "") {} else {
core.drawWindowSkin(
"winskin.webp",
ctx4,
50,
50,
1900,
200,
null,
null,
null,
3
);
core.fillBoldText1(
ctx4,
this.enemy[this.selection].name,
100,
120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"状态",
500,
120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"当前技能",
1400,
120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
const enemytext =
this.enemy[this.selection].skill[this.enemy[this.selection].index];
core.fillBoldText1(
ctx4,
enemytext,
1600,
120,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"生命 " + this.enemy[this.selection].hp,
100,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"攻击 " + this.enemy[this.selection].atk,
500,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"防御 " + this.enemy[this.selection].def,
900,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"法抗 " + this.enemy[this.selection].mdef + "%",
1300,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"速度 " + this.enemy[this.selection].speed,
1700,
220,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
let posx = 600;
this.enemybuff[this.selection].forEach((v) => {
if (v) {
core.drawIcon(ctx4, v.id, posx, 80, 64, 64);
core.setTextAlign(ctx4, "right");
core.fillBoldText1(
ctx4,
v.count,
posx + 50,
130,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(24, true)
);
core.setTextAlign(ctx4, "left");
posx += 80;
}
});
}
ctx4.restore();
}
moveStatus() {
boss4.style.display = "block";
let time = 0,
posy = 400;
return new Promise((resolve) => {
core.registerAnimationFrame("moveStatus", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
core.clearMap(ctx4);
if (core.domStyle.isVertical) {
ctx4.canvas.width = 1248;
ctx4.canvas.height = 2028;
ctx4.save(); //保存设置
ctx4.translate(1248, 0); //重新定位右上角为基准
ctx4.rotate(Math.PI / 2); //旋转90度
} else {
ctx4.canvas.width = 2028;
ctx4.canvas.height = 1248;
}
core.drawWindowSkin(
"winskin.webp",
ctx4,
600,
920 + posy,
900,
300,
null,
null,
null,
3
);
core.fillBoldText1(
ctx4,
hero.name,
630,
1000 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"状态",
880,
1000 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"生命 " + this.hero.hp,
630,
1070 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"攻击 " + this.hero.atk,
630,
1120 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"防御 " + this.hero.def,
630,
1170 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"法强 " + this.hero.spell,
1080,
1070 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"法抗 " + this.hero.mdef + "%",
1080,
1120 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"速度 " + this.hero.speed,
1080,
1170 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
let posx = 980;
this.herobuff.forEach((v) => {
if (v) {
core.drawIcon(ctx4, v.id, posx, 950 + posy, 64, 64);
core.setTextAlign(ctx4, "right");
core.fillBoldText1(
ctx4,
v.count,
posx + 50,
1000 + posy,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(24, true)
);
core.setTextAlign(ctx4, "left");
posx += 80;
}
});
if (this.selection === "boss") {
core.drawWindowSkin(
"winskin.webp",
ctx4,
50,
50 - posy / 2,
1900,
200,
null,
null,
null,
3
);
core.fillBoldText1(
ctx4,
this.boss.name,
100,
120 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"状态",
500,
120 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"生命 " + this.boss.hp,
100,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"攻击 " + this.boss.atk,
500,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"防御 " + this.boss.def,
900,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"法抗 " + this.boss.mdef + "%",
1300,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"速度 " + this.boss.speed,
1700,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
let posx = 600;
this.bossbuff.forEach((v) => {
if (v) {
core.drawIcon(ctx4, v.id, posx, 80 - posy / 2, 64, 64);
core.setTextAlign(ctx4, "right");
core.fillBoldText1(
ctx4,
v.count,
posx + 50,
130 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(24, true)
);
core.setTextAlign(ctx4, "left");
posx += 80;
}
});
} else if (this.selection === "") {} else {
core.drawWindowSkin(
"winskin.webp",
ctx4,
50,
50 - posy / 2,
1900,
200,
null,
null,
null,
3
);
core.fillBoldText1(
ctx4,
this.enemy[this.selection].name,
100,
120 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"状态",
500,
120 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"生命 " + this.enemy[this.selection].hp,
100,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"攻击 " + this.enemy[this.selection].atk,
500,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"防御 " + this.enemy[this.selection].def,
900,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"法抗 " + this.enemy[this.selection].mdef + "%",
1300,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
core.fillBoldText1(
ctx4,
"速度 " + this.enemy[this.selection].speed,
1700,
220 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(44, true)
);
let posx = 600;
this.enemybuff[this.selection].forEach((v) => {
if (v) {
core.drawIcon(ctx4, v.id, posx, 80 - posy / 2, 64, 64);
core.setTextAlign(ctx4, "right");
core.fillBoldText1(
ctx4,
v.count,
posx + 50,
130 - posy / 2,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(24, true)
);
core.setTextAlign(ctx4, "left");
posx += 80;
}
});
}
ctx4.restore();
posy -= 10;
if (posy <= 0) {
core.unregisterAnimationFrame("moveStatus");
this.drawStatus();
resolve();
}
}
});
});
}
close() {
let globalAlpha = 0,
time = 0;
return new Promise((resolve) => {
core.registerAnimationFrame("closeblack", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 1248;
ctx.canvas.height = 2028;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 2028;
ctx.canvas.height = 1248;
}
ctx.globalAlpha = globalAlpha;
core.fillRect(ctx, 0, 0, 2028, 1248);
globalAlpha += 1 / 30;
ctx.restore();
if (globalAlpha > 1) {
time = 0;
globalAlpha = 1;
core.unregisterAnimationFrame("closeblack");
core.unregisterAnimationFrame("enemyanimate");
boss1.style.display = "none";
boss2.style.display = "none";
boss3.style.display = "none";
boss4.style.display = "none";
boss5.style.display = "none";
boss6.style.display = "none";
boss7.style.display = "none";
boss8.style.display = "none";
core.registerAnimationFrame("closeblack2", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 1248;
ctx.canvas.height = 2028;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 2028;
ctx.canvas.height = 1248;
}
ctx.globalAlpha = globalAlpha;
core.fillRect(ctx, 0, 0, 2028, 1248, "#000000");
ctx.restore();
globalAlpha -= 1 / 30;
if (globalAlpha < 0) {
core.unregisterAnimationFrame("closeblack2");
boss.style.display = "none";
resolve();
}
}
});
}
}
});
});
}
blackBg() {
let globalAlpha = 0,
time = 0,
img = core.material.images.images[this.bg];
boss1.style.display = "block";
return new Promise((resolve) => {
core.registerAnimationFrame("bossblack", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
core.clearMap(ctx1);
if (core.domStyle.isVertical) {
ctx1.canvas.width = 1248;
ctx1.canvas.height = 2028;
ctx1.save(); //保存设置
ctx1.translate(1248, 0); //重新定位右上角为基准
ctx1.rotate(Math.PI / 2); //旋转90度
} else {
ctx1.canvas.width = 2028;
ctx1.canvas.height = 1248;
}
ctx1.globalAlpha = globalAlpha;
core.fillRect(ctx1, 0, 0, 2028, 1248);
globalAlpha += 1 / 30;
ctx1.restore();
if (globalAlpha > 1) {
time = 0;
globalAlpha = 0;
core.unregisterAnimationFrame("bossblack");
core.registerAnimationFrame("bossBg", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
core.clearMap(ctx1);
if (core.domStyle.isVertical) {
ctx1.canvas.width = 1248;
ctx1.canvas.height = 2028;
ctx1.save(); //保存设置
ctx1.translate(1248, 0); //重新定位右上角为基准
ctx1.rotate(Math.PI / 2); //旋转90度
} else {
ctx1.canvas.width = 2028;
ctx1.canvas.height = 1248;
}
ctx1.globalAlpha = 1;
core.fillRect(ctx1, 0, 0, 2028, 1248);
ctx1.globalAlpha = globalAlpha;
if (img) ctx1.drawImage(img, 0, 0, 2028, 1248);
ctx1.restore();
globalAlpha += 1 / 30;
if (globalAlpha > 1) {
core.unregisterAnimationFrame("bossBg");
resolve();
}
}
});
}
}
});
});
}
}
core.ui.boss = new Boss();
},
"剧情视频引用": function () {
// 在此增加新插件
let a;
let bgm;
function gtouchstart() {
timeOutEvent = setTimeout(() => {
video.remove();
video1.remove();
core.doAction();
clearTimeout(a);
core.playBgm(bgm);
}, 2000); //这里设置定时器定义长按500毫秒触发长按事件时间可以自己改个人感觉500毫秒非常合适
return false;
}
//手释放如果在500毫秒内就释放则取消长按事件此时可以执行onclick应该执行的事件
function gtouchend() {
if (timeOutEvent != 0) {
//这里写要执行的内容尤如onclick事件
console.log("你这是点击,不是长按");
}
clearTimeout(timeOutEvent); //清除定时器
return false;
}
this.openvideo = function () {
if (!core.isPlaying()) return;
const video = document.createElement("iframe"); //iframe设置
video.style.position = "absolute";
video.style.zIndex = 320;
video.style.display = "block";
video.id = "video";
main.dom.gameGroup.insertAdjacentElement("afterend", video);
video.style.top = "50%";
video.style.left = "50%";
video.style.transform = "translate(-50%,-50%)";
main.dom.video = video;
const video1 = document.createElement("canvas"); //video1画布设置
video1.style.position = "absolute";
video1.style.zIndex = 330;
video1.style.display = "block";
video1.id = "video1";
main.dom.gameGroup.insertAdjacentElement("afterend", video1);
video1.style.top = "50%";
video1.style.left = "50%";
video1.style.transform = "translate(-50%,-50%)";
const ctx = video1.getContext("2d");
main.dom.video1 = video1;
if (core.domStyle.isVertical) {
video.width = 416 * 3;
video.height = 676 * 3;
video.style.transform = "translate(-50%,-50%) rotate(90deg)"; //重新定位右上角为基准
} else {
video.width = 676 * 3;
video.height = 416 * 3;
video.style.transform = "translate(-50%,-50%)";
}
video1.ontouchstart = function (e) {
try {
e.preventDefault();
if (!core.isPlaying()) return false;
gtouchstart();
} catch (ee) {
main.log(ee);
}
};
video1.ontouchend = function (e) {
try {
e.preventDefault();
if (!core.isPlaying()) return false;
gtouchend();
} catch (ee) {
main.log(ee);
}
};
video1.onmouseup = function (e) {
//鼠标抬起
try {
e.stopPropagation();
if (!core.isPlaying()) return false;
gtouchend();
} catch (ee) {
console.error(ee);
}
};
video1.onmousedown = function (e) {
//鼠标按下
try {
e.stopPropagation();
if (!core.isPlaying()) return false;
gtouchstart();
} catch (ee) {
main.log(ee);
}
};
let globalAlpha = 0;
let frame = 1;
let al = 0;
core.registerAnimationFrame("beforeop", true, function () {
al++;
core.clearMap(ctx);
ctx.globalAlpha = al / 30;
core.fillRect(ctx, 0, 0, video1.width, video1.height, "#000000");
ctx.globalAlpha = 1;
core.fillBoldText1(
ctx,
"Loading...",
1014,
624,
"#FFFFFF",
"#000000",
6,
"bold 72px Verdana"
);
});
core.control.resize();
//player.bilibili.com/player.html
//www.bilibili.com/blackboard/html5mobileplayer.html
//<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=6484104&bvid=BV1cs411b7cH&cid=10546155&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
video.src =
"///www.bilibili.com/blackboard/html5mobileplayer.html?isOutside=true&aid=6484104&bvid=BV1cs411b7cH&cid=10546155&p=1&poster=0&autoplay=1&high_quality=1&muted=0&danmaku=0";
video.scrolling = "no";
video.border = "0";
video.crossorigin = true;
video.frameborder = "no";
video.framespacing = "0";
video.allowfullscreen = false;
video.sandbox =
"allow-top-navigation allow-same-origin allow-forms allow-scripts";
//gsl_play_mask
video.addEventListener("load", function () {
core.unregisterAnimationFrame("beforeop");
core.registerAnimationFrame("op", true, function () {
core.clearMap(ctx);
if (core.domStyle.isVertical) {
ctx.canvas.width = 416 * 3;
ctx.canvas.height = 676 * 3;
ctx.save(); //保存设置
ctx.translate(416 * 3, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 676 * 3;
ctx.canvas.height = 416 * 3;
}
ctx.globalAlpha = 1;
core.fillRect(ctx, 0, 0, video1.width, video1.height, "#000000");
ctx.globalAlpha = globalAlpha / 30;
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
"长按2秒后跳过op",
1014,
624,
"#FFFFFF",
"#000000",
6,
"bold 48px Verdana"
);
globalAlpha += frame;
if (globalAlpha > 29) frame = -1;
ctx.restore();
if (frame === -1 && globalAlpha < 0) {
core.clearMap(ctx);
core.unregisterAnimationFrame("op");
}
});
bgm = core.musicStatus.playingBgm;
core.playBgm("op.opus");
a = setTimeout(() => {
video.remove();
video1.remove();
core.playBgm(bgm);
core.doAction();
}, 127500);
});
};
},
"帧动画/图片叠拼": function () {
// 在此增加新插件
this.animationDrawable = function (
allFarme,
color,
globalAlpha,
imageList,
soundList
) {
if (!core.isPlaying()) {
return core.doAction();
}
const over = main.dom.over;
const ctx = over.getContext("2d");
over.style.display = "block";
let farme = 0;
let now = 0;
core.registerAnimationFrame(
"animationDrawable",
true,
function (timestamp) {
if (timestamp - now > 1000 / 60) {
now = timestamp;
if (core.domStyle.isVertical) {
over.width = 1248;
over.height = 2028;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
over.width = 2028;
over.height = 1248;
}
ctx.globalAlpha = (globalAlpha ?? 100) / 100;
core.fillRect(ctx, 0, 0, 2028, 1248, color);
imageList.forEach(function (one) {
if (
farme >= (one.beforefarme ?? 0) &&
farme <= (one.afterfarme ?? allFarme)
) {
const img = core.material.images.images?.[one.image];
if (img) {
const gla = one.globalAlpha ?? 100;
const agla = one.aglobalAlpha ?? gla,
beforefarme = one.beforefarme ?? 0;
const afterfarme = one.afterfarme ?? allFarme;
ctx.globalAlpha =
(gla +
((agla - gla) * (farme - beforefarme)) /
(afterfarme - beforefarme || 1)) /
100;
const cx =
(one.cx ?? 0) +
(((one.acx ?? 0) - (one.cx ?? 0)) *
(farme - beforefarme)) /
(afterfarme - beforefarme || 1),
cy =
(one.cy ?? 0) +
(((one.acy ?? 0) - (one.cy ?? 0)) *
(farme - beforefarme)) /
(afterfarme - beforefarme || 1),
cw =
(one.cw ?? img.width) +
(((one.acw ?? img.width) - (one.cw ?? img.width)) *
(farme - beforefarme)) /
(afterfarme - beforefarme || 1),
ch =
(one.ch ?? img.height) +
(((one.acw ?? img.height) - (one.cw ?? img.height)) *
(farme - beforefarme)) /
(afterfarme - beforefarme || 1),
x =
(one.x ?? 0) +
(((one.ax ?? 0) - (one.x ?? 0)) * (farme - beforefarme)) /
(afterfarme - beforefarme || 1),
y =
(one.y ?? 0) +
(((one.ay ?? 0) - (one.y ?? 0)) * (farme - beforefarme)) /
(afterfarme - beforefarme || 1),
w =
(one.w ?? 2028) +
(((one.aw ?? 2028) - (one.w ?? 2028)) *
(farme - beforefarme)) /
(afterfarme - beforefarme || 1),
h =
(one.h ?? 1248) +
(((one.aw ?? 1248) - (one.w ?? 1248)) *
(farme - beforefarme)) /
(afterfarme - beforefarme || 1);
ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h);
}
}
});
soundList.forEach(function (one) {
const lisen =
one.sound &&
core.sounds[one.sound] &&
core.musicStatus.soundStatus;
if (farme == one.startfarme && lisen) {
if (one.stopbefore) core.stopSound();
core.playSound(one.sound);
}
});
farme++;
ctx.globalAlpha = 1;
ctx.restore();
if (farme > allFarme) {
core.unregisterAnimationFrame("animationDrawable");
over.style.display = "none";
core.doAction();
}
}
}
);
};
},
"musicMode": function () {
// 在此增加新插件
const music = document.createElement("canvas");
music.style.position = "absolute";
music.style.zIndex = 300;
music.style.display = "none";
music.id = "music";
main.dom.gameGroup.insertAdjacentElement("afterend", music);
music.style.top = "50%";
music.style.left = "50%";
music.style.transform = "translate(-50%,-50%)";
const ctx = music.getContext("2d");
main.dom.music = music;
const audio = core.plugin.audioSystem.bgmController;
let page = 0; //初始页面
let isvolume = false;
function shuffle(arr) {
let n = arr.length,
random;
while (n) {
random = (Math.random() * n--) >>> 0;
[arr[n], arr[random]] = [arr[random], arr[n]];
}
return arr;
}
music.addEventListener("mousedown", function (e) {
e.stopPropagation();
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor((e.clientX - left) / core.domStyle.scale),
py = Math.floor((e.clientY - top) / core.domStyle.scale);
core.ui.music.mousedown(px * 3, py * 3);
});
music.addEventListener("mousemove", function (e) {
e.stopPropagation();
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor((e.clientX - left) / core.domStyle.scale),
py = Math.floor((e.clientY - top) / core.domStyle.scale);
core.ui.music.mousemove(px * 3, py * 3);
});
music.addEventListener("mouseup", function (e) {
e.stopPropagation();
isvolume = false;
});
music.addEventListener("mouseleave", function (e) {
e.stopPropagation();
isvolume = false;
});
music.addEventListener("touchstart", function (e) {
e.preventDefault();
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor(
(e.touches[0].clientX - left) / core.domStyle.scale
),
py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale);
core.ui.music.mousedown(px * 3, py * 3);
});
music.addEventListener("touchmove", function (e) {
e.stopPropagation();
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor(
(e.touches[0].clientX - left) / core.domStyle.scale
),
py = Math.floor((e.touches[0].clientY - top) / core.domStyle.scale);
core.ui.music.mousemove(px * 3, py * 3);
});
music.addEventListener("touchend", function (e) {
e.stopPropagation();
isvolume = false;
});
music.addEventListener("touchcancel", function (e) {
e.stopPropagation();
isvolume = false;
});
class musicclass {
constructor() {
this.musics = ["theme.mp3"];
//music列表
//需全塔属性注册并保存在bgms文件夹,每个数组为显示的一页内容
this.musicMx = [
[
"Crawler.opus",
"Blood_Stain.opus",
"Blind_Alley.opus",
"Halbmond.opus",
],
["theme.mp3", "op.opus", "Asphodelus_Ceui.opus", "ed.opus"],
];
//音乐别名将在播放器内显示的音乐名music列表内的都要有对应歌名
this.musicname = {
"Asphodelus_Ceui.opus": "Asphodelus (Full.ver)",
"Blind_Alley.opus": "Blind Alley",
"Crawler.opus": "Crawler",
"op.opus": "Asphodelus",
"theme.mp3": "One of Episodes",
"ed.opus": "親愛なる世界へ",
"Blood_Stain.opus": "Blood Stain",
"Halbmond.opus": "Halbmond",
};
this.selection = [0, 0];
this.stop = false;
this.type = "xunhuan";
this.randomList = [];
this.random = 0;
}
//更新
update() {
this.background();
this.drawUI();
}
background() {
//画布大小设置
if (core.domStyle.isVertical) {
music.width = 1248;
music.height = 2028;
} else {
music.width = 2028;
music.height = 1248;
}
}
mousedown(px, py) {
//鼠标按下时
const makeBox = ([x, y], [w, h]) => {
return [
[x, y],
[x + w, y + h],
];
};
const inRect = ([x, y], [[sx, sy], [dx, dy]]) => {
return sx <= x && x <= dx && sy <= y && y <= dy;
};
const pos = [px, py];
const backbox = makeBox([15, 35], [210, 90]);
if (inRect(pos, backbox)) {
//离开按钮是一致的,其余的记区分横竖屏
music.style.display = "none";
core.clearMap(ctx);
core.unregisterAnimationFrame("music");
core.restart();
return;
}
if (core.domStyle.isVertical) {
//竖屏
const pageupbox = makeBox([100, 1230], [200, 100]);
const pagedownbox = makeBox([950, 1230], [200, 100]);
const musicbox = makeBox(
[100, 200],
[1048, this.musicMx[page].length * 100]
);
const beforebox = makeBox([120, 1620], [100, 100]);
const afterbox = makeBox([780, 1620], [100, 100]);
const playbox = makeBox([420, 1580], [200, 200]);
const typebox = makeBox([1040, 1600], [120, 120]);
const volumebox = makeBox([250, 1940], [1050, 20]);
if (inRect(pos, pageupbox)) {
if (page !== 0) page -= 1;
return;
}
if (inRect(pos, pagedownbox)) {
if (page !== this.musicMx.length - 1) page += 1;
return;
}
if (inRect(pos, playbox)) {
if (this.stop) {
this.stop = !this.stop;
core.resumeBgm();
} else {
this.stop = !this.stop;
core.pauseBgm();
}
return;
}
if (inRect(pos, beforebox)) {
this.stop = false;
switch (this.type) {
case "danqu":
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
page = this.selection[0];
break;
case "xunhuan":
for (;;) {
if (this.selection[1] === 0) {
if (this.selection[0] === 0) {
this.selection[0] = this.musicMx.length - 1;
this.selection[1] =
this.musicMx[this.selection[0]].length - 1;
} else {
this.selection[0] -= 1;
this.selection[1] =
this.musicMx[this.selection[0]].length - 1;
}
} else {
this.selection[1] -= 1;
}
this.random = this.randomList.indexOf(
this.musicMx[this.selection[0]][this.selection[1]]
);
page = this.selection[0];
if (
this.musics.includes(
this.musicMx[this.selection[0]][this.selection[1]]
) ||
page !== this.musicMx.length - 1
)
break;
}
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
break;
case "suiji":
for (;;) {
if (this.random > 0) {
this.random -= 1;
} else {
this.random = this.randomList.length - 1;
}
this.selection[0] = this.musicMx.findIndex((v) =>
v.includes(this.randomList[this.random])
);
this.selection[1] = this.musicMx[this.selection[0]].indexOf(
this.randomList[this.random]
);
page = this.selection[0];
if (
this.musics.includes(
this.musicMx[this.selection[0]][this.selection[1]]
) ||
page !== this.musicMx.length - 1
)
break;
}
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
break;
}
return;
}
if (inRect(pos, afterbox)) {
this.stop = false;
switch (this.type) {
case "danqu":
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
page = this.selection[0];
break;
case "xunhuan":
for (;;) {
if (
this.selection[1] ===
this.musicMx[this.selection[0]].length - 1
) {
if (this.selection[0] === this.musicMx.length - 1) {
this.selection[0] = 0;
this.selection[1] = 0;
} else {
this.selection[0] += 1;
this.selection[1] = 0;
}
} else {
this.selection[1] += 1;
}
this.random = this.randomList.indexOf(
this.musicMx[this.selection[0]][this.selection[1]]
);
page = this.selection[0];
if (
this.musics.includes(
this.musicMx[this.selection[0]][this.selection[1]]
) ||
page !== this.musicMx.length - 1
)
break;
}
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
break;
case "suiji":
for (;;) {
if (this.random < this.randomList.length - 1) {
this.random += 1;
} else {
this.random = 0;
}
this.selection[0] = this.musicMx.findIndex((v) =>
v.includes(this.randomList[this.random])
);
this.selection[1] = this.musicMx[this.selection[0]].indexOf(
this.randomList[this.random]
);
page = this.selection[0];
if (
this.musics.includes(
this.musicMx[this.selection[0]][this.selection[1]]
) ||
page !== this.musicMx.length - 1
)
break;
}
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
break;
}
return;
}
if (inRect(pos, typebox)) {
switch (this.type) {
case "danqu":
this.type = "xunhuan";
break;
case "xunhuan":
this.type = "suiji";
break;
case "suiji":
this.type = "danqu";
break;
}
return;
}
if (inRect(pos, musicbox)) {
const index = Math.floor((py - 200) / 100);
if (page !== this.selection[0] || index !== this.selection[1]) {
if (
this.musics.includes(this.musicMx[page][index]) ||
page !== this.musicMx.length - 1
) {
this.selection[0] = page;
this.selection[1] = index;
this.randomList.indexOf(
this.musicMx[this.selection[0]][this.selection[1]]
);
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
this.stop = false;
}
} else {
if (this.stop) {
this.stop = !this.stop;
core.resumeBgm();
} else {
this.stop = !this.stop;
core.pauseBgm();
}
}
return;
}
if (inRect(pos, volumebox)) {
const time = Math.min(Math.max((px - 250) / 800, 0), 1);
audio.setVolume(time);
core.plugin.audioSystem.soundPlayer.setVolume(time);
isvolume = true;
}
} else {
//横屏
const pageupbox = makeBox([1050, 1100], [200, 100]);
const pagedownbox = makeBox([1550, 1100], [200, 100]);
const musicbox = makeBox(
[900, 100],
[1000, this.musicMx[page].length * 100]
);
const beforebox = makeBox([60, 620], [100, 100]);
const afterbox = makeBox([450, 620], [100, 100]);
const playbox = makeBox([200, 570], [200, 200]);
const typebox = makeBox([620, 600], [120, 120]);
const volumebox = makeBox([100, 990], [600, 20]);
if (inRect(pos, pageupbox)) {
if (page !== 0) page -= 1;
return;
}
if (inRect(pos, pagedownbox)) {
if (page !== this.musicMx.length - 1) page += 1;
return;
}
if (inRect(pos, playbox)) {
if (this.stop) {
this.stop = !this.stop;
core.resumeBgm();
} else {
this.stop = !this.stop;
core.pauseBgm();
}
return;
}
if (inRect(pos, beforebox)) {
this.stop = false;
switch (this.type) {
case "danqu":
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
page = this.selection[0];
break;
case "xunhuan":
for (;;) {
if (this.selection[1] === 0) {
if (this.selection[0] === 0) {
this.selection[0] = this.musicMx.length - 1;
this.selection[1] =
this.musicMx[this.selection[0]].length - 1;
} else {
this.selection[0] -= 1;
this.selection[1] =
this.musicMx[this.selection[0]].length - 1;
}
} else {
this.selection[1] -= 1;
}
this.random = this.randomList.indexOf(
this.musicMx[this.selection[0]][this.selection[1]]
);
page = this.selection[0];
if (
this.musics.includes(
this.musicMx[this.selection[0]][this.selection[1]]
) ||
page !== this.musicMx.length - 1
)
break;
}
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
break;
case "suiji":
for (;;) {
if (this.random > 0) {
this.random -= 1;
} else {
this.random = this.randomList.length - 1;
}
this.selection[0] = this.musicMx.findIndex((v) =>
v.includes(this.randomList[this.random])
);
this.selection[1] = this.musicMx[this.selection[0]].indexOf(
this.randomList[this.random]
);
page = this.selection[0];
if (
this.musics.includes(
this.musicMx[this.selection[0]][this.selection[1]]
) ||
page !== this.musicMx.length - 1
)
break;
}
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
break;
}
return;
}
if (inRect(pos, afterbox)) {
this.stop = false;
switch (this.type) {
case "danqu":
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
page = this.selection[0];
break;
case "xunhuan":
for (;;) {
if (
this.selection[1] ===
this.musicMx[this.selection[0]].length - 1
) {
if (this.selection[0] === this.musicMx.length - 1) {
this.selection[0] = 0;
this.selection[1] = 0;
} else {
this.selection[0] += 1;
this.selection[1] = 0;
}
} else {
this.selection[1] += 1;
}
this.randomList.findIndex(
(v) =>
v === this.musicMx[this.selection[0]][this.selection[1]]
);
page = this.selection[0];
if (
this.musics.includes(
this.musicMx[this.selection[0]][this.selection[1]]
) ||
page !== this.musicMx.length - 1
)
break;
}
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
break;
case "suiji":
for (;;) {
if (this.random < this.randomList.length - 1) {
this.random += 1;
} else {
this.random = 0;
}
this.selection[0] = this.musicMx.findIndex((v) =>
v.includes(this.randomList[this.random])
);
this.selection[1] = this.musicMx[this.selection[0]].indexOf(
main.core.ui.music.randomList[main.core.ui.music.random]
);
page = this.selection[0];
if (
this.musics.includes(
this.musicMx[this.selection[0]][this.selection[1]]
) ||
page !== this.musicMx.length - 1
)
break;
}
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
break;
}
return;
}
if (inRect(pos, typebox)) {
switch (this.type) {
case "danqu":
this.type = "xunhuan";
break;
case "xunhuan":
this.type = "suiji";
break;
case "suiji":
this.type = "danqu";
break;
}
return;
}
if (inRect(pos, musicbox)) {
const index = Math.floor((py - 100) / 100);
if (page !== this.selection[0] || index !== this.selection[1]) {
if (
this.musics.includes(this.musicMx[page][index]) ||
page !== this.musicMx.length - 1
) {
this.selection[0] = page;
this.selection[1] = index;
this.randomList.indexOf(
(v) =>
v === this.musicMx[this.selection[0]][this.selection[1]]
);
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
this.stop = false;
}
} else {
if (this.stop) {
this.stop = !this.stop;
core.resumeBgm();
} else {
this.stop = !this.stop;
core.pauseBgm();
}
}
return;
}
if (inRect(pos, volumebox)) {
const time = Math.min(Math.max((px - 100) / 600, 0), 1);
audio.setVolume(time);
core.plugin.audioSystem.soundPlayer.setVolume(time);
isvolume = true;
}
}
}
mousemove(px, py) {
if (isvolume) {
if (core.domStyle.isVertical) {
const time = Math.min(Math.max((px - 250) / 800, 0), 1);
audio.setVolume(time);
core.plugin.audioSystem.soundPlayer.setVolume(time);
} else {
const time = Math.min(Math.max((px - 100) / 600, 0), 1);
audio.setVolume(time);
core.plugin.audioSystem.soundPlayer.setVolume(time);
}
}
}
drawUI() {
//绘制页面
core.clearMap(music);
const bgVertical = core.material.images.images["bg_2010.webp"]; //竖屏背景
const bg = core.material.images.images["bg_5043.webp"]; //竖屏背景
if (core.domStyle.isVertical) {
//竖屏
core.fillRect(ctx, 0, 0, 1248, 2028, "#000000"); //黑色背景
ctx.globalAlpha = 0.3; //透明度
if (bgVertical)
ctx.drawImage(bgVertical, 0, 0, 1280, 1500, 0, 0, 1248, 2028); //绘制半透明背景图片
ctx.globalAlpha = 1; //恢复为不透明
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
"◀离开",
110,
100,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
ctx.strokeStyle = "#FFFFFF";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(100, 200);
ctx.lineTo(1148, 200);
ctx.stroke();
let posy = 300;
const indexList = this.musicMx[page];
core.setTextAlign(ctx, "left");
for (let i = 0; i < indexList.length; i++) {
const text = this.musicname[indexList[i]];
if (
this.musics.includes(this.musicMx[page][i]) ||
page !== this.musicMx.length - 1
) {
core.fillBoldText1(
ctx,
text,
150,
posy - 30,
page === this.selection[0] && i === this.selection[1]
? "#FFFFFF"
: "#444444",
"#000000",
6,
core.ui._buildFont(66, true)
);
ctx.strokeStyle = "#FFFFFF";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(100, posy);
ctx.lineTo(1148, posy);
ctx.stroke();
}
posy += 100;
}
ctx.beginPath();
ctx.moveTo(100, 1210);
ctx.lineTo(1148, 1210);
ctx.moveTo(100, 1200);
ctx.lineTo(1148, 1200);
ctx.stroke();
core.fillBoldText1(
ctx,
"上一页",
100,
1300,
page === 0 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
page + 1 + "/" + this.musicMx.length,
580,
1300,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
"下一页",
950,
1300,
page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
ctx.strokeStyle = "#ffffff";
ctx.lineWidth = 3;
core.fillBoldText(
ctx,
"|",
100,
1697,
"#FFFFFF",
6,
core.ui._buildFont(96, true)
);
core.fillBoldText(
ctx,
"◀",
115,
1700,
"#FFFFFF",
6,
core.ui._buildFont(96, true)
);
ctx.beginPath();
ctx.arc(505, 1670, 80, 0, 3 * Math.PI);
ctx.stroke();
core.fillText(
ctx,
"|",
835,
1697,
"#FFFFFF",
core.ui._buildFont(96, true)
);
core.fillText(
ctx,
"▶",
785,
1700,
"#FFFFFF",
core.ui._buildFont(96, true)
);
if (this.stop) {
core.fillText(
ctx,
"▶",
473,
1700,
"#FFFFFF",
core.ui._buildFont(96, true)
);
} else {
core.fillText(
ctx,
"||",
453,
1700,
"#FFFFFF",
core.ui._buildFont(96, true)
);
}
const img = core.material.images.images[this.type + ".webp"];
if (img) ctx.drawImage(img, 1000, 1555, 200, 200);
core.setTextAlign(ctx, "center");
ctx.font = "bold 52px Verdana";
ctx.fillText("当前歌曲", 625, 1397);
ctx.fillText(
this.musicname[this.musicMx[this.selection[0]][this.selection[1]]],
625,
1507
);
ctx.fillStyle = "#ffffff";
ctx.font = "bold 48px Verdana";
ctx.fillText("音量", 150, 1970);
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(250, 1950);
ctx.lineTo(1050, 1950);
ctx.stroke();
ctx.strokeStyle = "#ffffff";
ctx.lineWidth = 9;
ctx.fillStyle = "rgba(255,255,255,0.5)";
ctx.beginPath();
ctx.moveTo(250, 1950);
ctx.lineTo(800 * audio.getVolume() + 250, 1950);
ctx.stroke();
ctx.beginPath();
ctx.arc(800 * audio.getVolume() + 250, 1950, 10, 0, 2 * Math.PI);
ctx.fill();
core.fillBoldText1(
ctx,
Math.floor(100 * audio.getVolume()),
1120,
1970,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(56, true)
);
} else {
//横屏
core.fillRect(ctx, 0, 0, 2028, 1248, "#000000"); //黑色背景
ctx.globalAlpha = 0.5; //透明度
if (bg) ctx.drawImage(bg, 0, 0, 1280, 720, 0, 0, 2028, 1248); //绘制半透明背景图片
ctx.globalAlpha = 1; //恢复为不透明
core.setTextAlign(ctx, "center");
core.fillBoldText1(
ctx,
"◀离开",
110,
100,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
ctx.strokeStyle = "#FFFFFF";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(800, 100);
ctx.lineTo(800, 1148);
ctx.moveTo(900, 100);
ctx.lineTo(1900, 100);
ctx.stroke();
let posy = 200;
const indexList = this.musicMx[page];
core.setTextAlign(ctx, "left");
for (let i = 0; i < indexList.length; i++) {
const text = this.musicname[indexList[i]];
if (
this.musics.includes(this.musicMx[page][i]) ||
page !== this.musicMx.length - 1
) {
core.fillBoldText1(
ctx,
text,
950,
posy - 30,
page === this.selection[0] && i === this.selection[1]
? "#FFFFFF"
: "#444444",
"#000000",
6,
core.ui._buildFont(66, true)
);
ctx.strokeStyle = "#FFFFFF";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(900, posy);
ctx.lineTo(1900, posy);
ctx.stroke();
}
posy += 100;
}
core.fillBoldText1(
ctx,
"上一页",
1050,
1200 - 30,
page === 0 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
page + 1 + "/" + this.musicMx.length,
1350,
1200 - 30,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
core.fillBoldText1(
ctx,
"下一页",
1550,
1200 - 30,
page === this.musicMx.length - 1 ? "#444444" : "#FFFFFF",
"#000000",
6,
core.ui._buildFont(66, true)
);
ctx.strokeStyle = "#ffffff";
ctx.lineWidth = 3;
core.fillText(
ctx,
"|",
55,
697,
"#FFFFFF",
core.ui._buildFont(96, true)
);
core.fillText(
ctx,
"◀",
70,
700,
"#FFFFFF",
core.ui._buildFont(96, true)
);
ctx.beginPath();
ctx.arc(295, 670, 80, 0, 2 * Math.PI);
ctx.stroke();
if (this.stop) {
core.fillText(
ctx,
"▶",
265,
700,
"#FFFFFF",
core.ui._buildFont(96, true)
);
} else {
core.fillText(
ctx,
"||",
245,
700,
"#FFFFFF",
core.ui._buildFont(96, true)
);
}
core.fillText(
ctx,
"|",
495,
697,
"#FFFFFF",
core.ui._buildFont(96, true)
);
core.fillText(
ctx,
"▶",
450,
700,
"#FFFFFF",
core.ui._buildFont(96, true)
);
ctx.font = "bold 48px Verdana";
ctx.fillText("音量", 350, 900);
ctx.beginPath();
ctx.moveTo(100, 1000);
ctx.lineTo(700, 1000);
ctx.stroke();
ctx.strokeStyle = "#ffffff";
ctx.lineWidth = 9;
ctx.fillStyle = "rgba(255,255,255,0.5)";
ctx.beginPath();
ctx.moveTo(100, 1000);
ctx.lineTo(600 * audio.getVolume() + 100, 1000);
ctx.stroke();
ctx.beginPath();
ctx.arc(600 * audio.getVolume() + 100, 1000, 10, 0, 2 * Math.PI);
ctx.fill();
core.fillBoldText1(
ctx,
Math.floor(100 * audio.getVolume()),
720,
1010,
"#FFFFFF",
"#000000",
6,
core.ui._buildFont(56, true)
);
const img = core.material.images.images[this.type + ".webp"];
if (img) ctx.drawImage(img, 580, 560, 200, 200);
core.setTextAlign(ctx, "center");
ctx.font = "bold 48px Verdana";
ctx.fillText("当前歌曲", 400, 297);
ctx.fillText(
this.musicname[this.musicMx[this.selection[0]][this.selection[1]]],
400,
397
);
}
}
}
core.ui.music = new musicclass();
main.dom.musicMode.onclick = function () {
//点击开始页面的CG MODE进入cg回廊
if (
(core.getLocalStorage("musics") &&
core.getLocalStorage("musics").length === 0) ||
!core.getLocalStorage("musics")
)
core.setLocalStorage("musics", ["theme.mp3"]);
core.ui.music.musics = core.getLocalStorage("musics");
core.playBgm(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
],
0
);
const arr = main.core.ui.music.musicMx.flat(Infinity);
main.core.ui.music.randomList = shuffle(arr);
main.core.ui.music.random = main.core.ui.music.randomList.indexOf(
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
]
);
page = 0;
music.style.display = "block";
let time = 0;
core.registerAnimationFrame("music", null, (temptime) => {
if (temptime > time + 1000 / 60) {
time = temptime;
main.core.ui.music.update();
const duration =
core.plugin.audioSystem.bgmController.player.getRoute(
"bgms." +
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
]
).duration;
const currentTime =
core.plugin.audioSystem.bgmController.player.getRoute(
"bgms." +
main.core.ui.music.musicMx[main.core.ui.music.selection[0]][
main.core.ui.music.selection[1]
]
).currentTime;
if (currentTime && duration && duration - currentTime < 0.05) {
if (core.domStyle.isVertical) {
core.ui.music.mousedown(830, 1770);
} else {
core.ui.music.mousedown(475, 765);
}
}
}
});
};
loader.prototype.loadOneMusic = function (name) {
/* var music = new Audio();
music.preload = "none";
if (main.bgmRemote)
music.src = main.bgmRemoteRoot + core.firstData.name + "/" + name;
else music.src = "project/bgms/" + name;
music.loop = "loop";
core.material.bgms[name] = music;*/
};
},
"横屏切换": function () {
// 在此增加新插件
this.triggerFullscreen = async function (full) {
if (!!document.fullscreenElement && !full) {
if (window.jsinterface) {
window.jsinterface.requestPortrait();
return;
}
await document.exitFullscreen();
}
if (full && !document.fullscreenElement) {
if (window.jsinterface) {
window.jsinterface.requestLandscape();
return;
}
await document.body.requestFullscreen();
}
};
this.abc = function () {
var orientation =
(screen.orientation || {}).type ||
screen.mozOrientation ||
screen.msOrientation;
if (orientation === "landscape-primary") {
console.log("That looks good.");
} else if (orientation === "landscape-secondary") {
console.log("Mmmh... the screen is upside down!");
} else if (
orientation === "portrait-secondary" ||
orientation === "portrait-primary"
) {
console.log("Mmmh... you should rotate your device to landscape");
} else if (orientation === undefined) {
console.log("The orientation API isn't supported in this browser :(");
}
};
},
"图片压缩webp导出": function () {
// 在此增加新插件
//使用方法进入游戏后开始游戏F12打开控制台输入core.towebp(image),image为已在全塔属性中注册过的图片名字需要""括起来
this.towebp = function (image) {
const canvas = document.createElement("canvas"); //背景画布设置
const ctx = canvas.getContext("2d");
const img = core.material.images.images[image];
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const webpDataURL = canvas.toDataURL("image/webp", 0.85); //第二个参数为画面质量范围为0-1,1为无损
console.log(webpDataURL);
const link = document.createElement("a");
link.href = webpDataURL;
const name = image.substring(0, image.indexOf("."));
link.download = name + ".webp";
link.click();
};
this.towebpall = function () {
const canvas = document.createElement("canvas"); //背景画布设置
const ctx = canvas.getContext("2d");
for (const image in core.material.images.images) {
this.towebp(image);
}
};
this.towebpsome = function (images) {
images.forEach((image) => {
core.towebp(image);
});
};
},
"帧动画特效(游戏界面)": function () {
// 在此增加新插件
const animate2 = document.createElement("canvas"); //画布设置
animate2.style.zIndex = 91;
animate2.id = "animate2";
animate2.classList.add("gameCanvas", "anti-aliasing");
animate2.style.display = "block";
animate2.width = 416;
animate2.height = 416;
animate2.style.width = core.__PIXELS__ * core.domStyle.scale + "px";
animate2.style.height = core.__PIXELS__ * core.domStyle.scale + "px";
main.dom.animate2 = animate2;
const anctx = animate2.getContext("2d");
main.dom.gameDraw.appendChild(animate2);
core.plugin.playing = new Set();
this.setanimate = function (
name,
px,
py,
width,
height,
allFarme,
imageList,
soundList
) {
const data = {
px: px,
py: py,
width: width,
height: height,
allFarme: allFarme,
imageList: imageList,
soundList: soundList,
};
core.setFlag("animate_" + name, data);
};
this.deleteanimate = function (name) {
core.setFlag("animate_" + name);
};
let thistime = 0;
this.playanimate = function (name, x, y, hero, scalex, scaley, callback) {
const data = {
name: name,
x: x,
y: y,
hero: hero,
scalex: scalex,
scaley: scaley,
farme: 0,
callback,
};
core.plugin.playing.add(data);
};
core.registerAnimationFrame("animateonmap", true, function (timestamp) {
if (timestamp - thistime > 1000 / 30) {
thistime = timestamp;
core.clearMap(anctx);
core.plugin.playing.forEach((one) => {
const data = flags["animate_" + one.name];
if (!data) {
core.plugin.playing.delete(one);
} else {
data.imageList.forEach(function (image) {
if (
one.farme >= (image.beforefarme ?? 0) &&
one.farme <= (image.afterfarme ?? data.allFarme)
) {
const img = core.material.images.images?.[image.image];
if (img) {
const gla = image.globalAlpha ?? 100;
const agla = image.aglobalAlpha ?? gla,
beforefarme = image.beforefarme ?? 0;
const afterfarme = image.afterfarme ?? data.allFarme;
anctx.globalAlpha =
(gla +
((agla - gla) * (one.farme - beforefarme)) /
(afterfarme - beforefarme || 1)) /
100;
const cx =
(image.cx ?? 0) +
(((image.acx ?? 0) - (image.cx ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
cy =
(image.cy ?? 0) +
(((image.acy ?? 0) - (image.cy ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
cw =
(image.cw ?? img.width) +
(((image.acw ?? img.width) - (image.cw ?? img.width)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
ch =
(image.ch ?? img.height) +
(((image.acw ?? img.height) - (image.cw ?? img.height)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
x =
(image.x ?? 0) +
(((image.ax ?? 0) - (image.x ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
y =
(image.y ?? 0) +
(((image.ay ?? 0) - (image.y ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
w =
(image.w ?? one.width) +
(((image.aw ?? one.width) - (image.w ?? one.width)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
h =
(image.h ?? one.height) +
(((image.aw ?? one.height) - (image.w ?? one.height)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
angle =
(Math.PI *
((image.image.angle ?? 0) +
(((image.aangle ?? 0) - (image.image.angle ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1))) /
180;
if (one.hero) {
let sx, sy;
if (core.status.heroMoving < 0) {
sx = 0;
sy = 0;
} else {
sx =
core.utils.scan[core.status.hero.loc.direction].x *
4 *
core.status.heroMoving;
sy =
core.utils.scan[core.status.hero.loc.direction].y *
4 *
core.status.heroMoving;
}
const herox = core.status.hero.loc.x * 32 + 16 + sx;
const heroy = core.status.hero.loc.y * 32 + 16 + sy;
core.drawImage(
anctx,
img,
cx,
cy,
cw,
ch,
herox + (x - data.px) * one.scalex,
heroy + (y - data.py) * one.scaley,
w * one.scalex,
h * one.scaley,
angle
);
} else {
core.drawImage(
anctx,
img,
cx,
cy,
cw,
ch,
one.x + (x - data.px) * one.scalex,
one.y + (y - data.py) * one.scaley,
w * one.scalex,
h * one.scaley,
angle
);
}
}
}
});
data.soundList.forEach(function (sound) {
const lisen =
sound.sound &&
core.sounds[sound.sound] &&
core.musicStatus.soundStatus;
if (one.farme == sound.startfarme && lisen) {
if (sound.stopbefore) core.stopSound();
core.playSound(sound.sound);
}
});
one.farme++;
if (one.farme > data.allFarme) {
core.plugin.playing.delete(one);
if (one.callback) {
one.callback();
}
}
}
});
}
});
},
"intro&loop": function () {
// 在此增加新插件
this.introAndLoop = function (intro, time, loop) {
core.playBgm(intro);
setTimeout(() => {
core.playBgm(loop);
}, time * 1000);
};
},
"开局选项悬停": function () {
// 在此增加新插件
main.dom.playGame.addEventListener("mouseenter", () => {
core.dom.playGame.style.backgroundColor = "#808080";
});
main.dom.playGame.addEventListener("mouseleave", () => {
core.dom.playGame.style.backgroundColor = "transparent";
});
main.dom.playGame.addEventListener("touchmove", () => {
core.dom.playGame.style.backgroundColor = "#808080";
});
main.dom.playGame.addEventListener("touchend", () => {
core.dom.playGame.style.backgroundColor = "transparent";
});
main.dom.playGame.addEventListener("touchcancel", () => {
core.dom.playGame.style.backgroundColor = "transparent";
});
main.dom.loadGame.addEventListener("mouseenter", () => {
core.dom.loadGame.style.backgroundColor = "#808080";
});
main.dom.loadGame.addEventListener("mouseleave", () => {
core.dom.loadGame.style.backgroundColor = "transparent";
});
main.dom.loadGame.addEventListener("touchmove", () => {
core.dom.loadGame.style.backgroundColor = "#808080";
});
main.dom.loadGame.addEventListener("touchend", () => {
core.dom.loadGame.style.backgroundColor = "transparent";
});
main.dom.loadGame.addEventListener("touchcancel", () => {
core.dom.loadGame.style.backgroundColor = "transparent";
});
main.dom.CGMode.addEventListener("mouseenter", () => {
core.dom.CGMode.style.backgroundColor = "#808080";
});
main.dom.CGMode.addEventListener("mouseleave", () => {
core.dom.CGMode.style.backgroundColor = "transparent";
});
main.dom.CGMode.addEventListener("touchmove", () => {
core.dom.CGMode.style.backgroundColor = "#808080";
});
main.dom.CGMode.addEventListener("touchend", () => {
core.dom.CGMode.style.backgroundColor = "transparent";
});
main.dom.CGMode.addEventListener("touchcancel", () => {
core.dom.CGMode.style.backgroundColor = "transparent";
});
main.dom.musicMode.addEventListener("mouseenter", () => {
core.dom.musicMode.style.backgroundColor = "#808080";
});
main.dom.musicMode.addEventListener("mouseleave", () => {
core.dom.musicMode.style.backgroundColor = "transparent";
});
main.dom.musicMode.addEventListener("touchmove", () => {
core.dom.musicMode.style.backgroundColor = "#808080";
});
main.dom.musicMode.addEventListener("touchend", () => {
core.dom.musicMode.style.backgroundColor = "transparent";
});
main.dom.musicMode.addEventListener("touchcancel", () => {
core.dom.musicMode.style.backgroundColor = "transparent";
});
main.dom.replayGame.addEventListener("mouseenter", () => {
core.dom.replayGame.style.backgroundColor = "#808080";
});
main.dom.replayGame.addEventListener("mouseleave", () => {
core.dom.replayGame.style.backgroundColor = "transparent";
});
main.dom.replayGame.addEventListener("touchmove", () => {
core.dom.replayGame.style.backgroundColor = "#808080";
});
main.dom.replayGame.addEventListener("touchend", () => {
core.dom.replayGame.style.backgroundColor = "transparent";
});
main.dom.replayGame.addEventListener("touchcancel", () => {
core.dom.replayGame.style.backgroundColor = "transparent";
});
},
"天气叠加": function () {
//使用方法使用core.setWeather(天气,等级)来增加天气使用core.setWeather()来清空天气
// 天气叠加功能
////// 更改天气效果 //////
control.prototype.setWeather = function (type, level) {
// 非雨雪
if (type == null) {
Object.keys(core.control.weathers).forEach(function (one) {
core.deleteCanvas("weather" + one);
});
core.animateFrame.weather.type = [];
core.animateFrame.weather.nodes = {};
core.animateFrame.weather.level = {};
core.animateFrame.weather.time = {};
return;
}
if (!core.animateFrame.weather.level || level == null)
core.animateFrame.weather.level = {};
if (!core.animateFrame.weather.type) core.animateFrame.weather.type = [];
level = core.clamp(parseInt(level) || 5, 1, 10);
// 当前天气:则忽略
if (
core.animateFrame.weather.type.includes(type) &&
level == core.animateFrame.weather.level[type]
)
return;
if (core.animateFrame.weather.nodes[type]) return;
// 计算当前的宽高
core.createCanvas(
"weather" + type,
0,
0,
core.__PIXELS__,
core.__PIXELS__,
80
);
core.animateFrame.weather.type.push(type);
core.animateFrame.weather.level[type] = level;
this._setWeather_createNodes(type, level);
};
control.prototype._setWeather_createNodes = function (type, level) {
var number =
level *
parseInt(
(20 * core.bigmap.width * core.bigmap.height) /
(core.__SIZE__ * core.__SIZE__)
);
if (!core.animateFrame.weather.nodes[type])
core.animateFrame.weather.nodes[type] = [];
switch (type) {
case "rain":
for (var a = 0; a < number; a++) {
core.animateFrame.weather.nodes.rain.push({
x: Math.random() * core.bigmap.width * 32,
y: Math.random() * core.bigmap.height * 32,
l: Math.random() * 2.5,
xs: -4 + Math.random() * 4 + 2,
ys: Math.random() * 10 + 10,
});
}
break;
case "snow":
for (var a = 0; a < number; a++) {
core.animateFrame.weather.nodes.snow.push({
x: Math.random() * core.bigmap.width * 32,
y: Math.random() * core.bigmap.height * 32,
r: Math.random() * 5 + 1,
d: Math.random() * Math.min(level, 200),
});
}
break;
case "fog":
if (core.animateFrame.weather.fog) {
core.animateFrame.weather.nodes[type] = [
{
level: number,
x: 0,
y: -core.__PIXELS__ / 2,
dx: -Math.random() * 1.5,
dy: Math.random(),
delta: 0.001,
},
];
}
break;
case "cloud":
if (core.animateFrame.weather.cloud) {
core.animateFrame.weather.nodes[type] = [
{
level: number,
x: 0,
y: -core.__PIXELS__ / 2,
dx: -Math.random() * 1.5,
dy: Math.random(),
delta: 0.001,
},
];
}
break;
case "sun":
if (core.animateFrame.weather.sun) {
// 直接绘制
core.clearMap("weather" + type);
core.setAlpha("weather" + type, level / 10);
core.drawImage(
"weather" + type,
core.animateFrame.weather.sun,
0,
0,
core.animateFrame.weather.sun.width,
core.animateFrame.weather.sun.height,
0,
0,
core.__PIXELS__,
core.__PIXELS__
);
core.setAlpha("weather" + type, 1);
}
break;
}
};
core.registerAnimationFrame("weather", true, function (timestamp) {
var weather = core.animateFrame.weather;
if (!weather.type) return;
weather.type.forEach(function (one) {
if (
timestamp - weather.time[one] <= 30 ||
!core.dymCanvas["weather" + one]
)
return;
core.control["_animationFrame_weather_" + one]();
weather.time[one] = timestamp;
});
});
// 雨
control.prototype._animationFrame_weather_rain = function () {
var ctx = core.dymCanvas.weatherrain,
ox = core.bigmap.offsetX,
oy = core.bigmap.offsetY;
core.clearMap("weatherrain");
ctx.strokeStyle = "rgba(174,194,224,0.8)";
ctx.lineWidth = 1;
ctx.lineCap = "round";
core.animateFrame.weather.nodes.rain.forEach(function (p) {
ctx.beginPath();
ctx.moveTo(p.x - ox, p.y - oy);
ctx.lineTo(p.x + p.l * p.xs - ox, p.y + p.l * p.ys - oy);
ctx.stroke();
p.x += p.xs;
p.y += p.ys;
if (p.x > core.bigmap.width * 32 || p.y > core.bigmap.height * 32) {
p.x = Math.random() * core.bigmap.width * 32;
p.y = -10;
}
});
ctx.fill();
};
// 雪
control.prototype._animationFrame_weather_snow = function () {
var ctx = core.dymCanvas.weathersnow,
ox = core.bigmap.offsetX,
oy = core.bigmap.offsetY;
core.clearMap("weathersnow");
ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
ctx.beginPath();
if (!core.animateFrame.weather.data) core.animateFrame.weather.data = {};
core.animateFrame.weather.data.snow =
core.animateFrame.weather.data.snow || 0;
core.animateFrame.weather.data.snow += 0.01;
var angle = core.animateFrame.weather.data.snow;
core.animateFrame.weather.nodes.snow.forEach(function (p) {
ctx.moveTo(p.x - ox, p.y - oy);
ctx.arc(p.x - ox, p.y - oy, p.r, 0, Math.PI * 2, true);
// update
p.x += Math.sin(angle) * core.animateFrame.weather.level.snow;
p.y += Math.cos(angle + p.d) + 1 + p.r / 2;
if (
p.x > core.bigmap.width * 32 + 5 ||
p.x < -5 ||
p.y > core.bigmap.height * 32
) {
if (Math.random() > 1 / 3) {
p.x = Math.random() * core.bigmap.width * 32;
p.y = -10;
} else {
if (Math.sin(angle) > 0) p.x = -5;
else p.x = core.bigmap.width * 32 + 5;
p.y = Math.random() * core.bigmap.height * 32;
}
}
});
ctx.fill();
};
// 图片天气
control.prototype.__animateFrame_weather_image = function (image, type) {
if (!image) return;
var node = core.animateFrame.weather.nodes[type][0];
core.setAlpha("weather" + type, node.level / 500);
var wind = 1.5;
var width = image.width,
height = image.height;
node.x += node.dx * wind;
node.y += (2 * node.dy - 1) * wind;
if (node.x + 3 * width <= core.__PIXELS__) {
node.x += 4 * width;
while (node.x > 0) node.x -= width;
}
node.dy += node.delta;
if (node.dy >= 1) {
node.delta = -0.001;
} else if (node.dy <= 0) {
node.delta = 0.001;
}
if (node.y + 3 * height <= core.__PIXELS__) {
node.y += 4 * height;
while (node.y > 0) node.y -= height;
} else if (node.y >= 0) {
node.y -= height;
}
for (var i = 0; i < 3; ++i) {
for (var j = 0; j < 3; ++j) {
if (
node.x + (i + 1) * width <= 0 ||
node.x + i * width >= core.__PIXELS__ ||
node.y + (j + 1) * height <= 0 ||
node.y + j * height >= core.__PIXELS__
)
continue;
core.drawImage(
"weather" + type,
image,
node.x + i * width,
node.y + j * height
);
}
}
core.setAlpha("weather" + type, 1);
};
// 雾
control.prototype._animationFrame_weather_fog = function () {
core.clearMap("weatherfog");
this.__animateFrame_weather_image(core.animateFrame.weather.fog, "fog");
};
// 云
control.prototype._animationFrame_weather_cloud = function () {
core.clearMap("weathercloud");
this.__animateFrame_weather_image(
core.animateFrame.weather.cloud,
"cloud"
);
};
},
"回合战斗动画": function () {
// 在此增加新插件
const animateAttack = document.createElement("canvas"); //画布设置
animateAttack.style.zIndex = 80;
animateAttack.id = "animateAttack";
animateAttack.classList.add("gameCanvas", "anti-aliasing");
animateAttack.style.display = "block";
animateAttack.width = 416;
animateAttack.height = 416;
animateAttack.style.width = core.__PIXELS__ * core.domStyle.scale + "px";
animateAttack.style.height = core.__PIXELS__ * core.domStyle.scale + "px";
main.dom.animateAttack = animateAttack;
const ctx = animateAttack.getContext("2d");
core.plugin.playingattack = new Set();
const ctx6 = main.dom.animate2.getContext("2d");
let easy = false;
const { imagelighter } = core.plugin.utils;
function playanimate(name, x, y, scalex = 1, scaley = 1) {
const data = {
name: name,
x: x,
y: y,
scalex: scalex,
scaley: scaley,
farme: 0,
};
core.plugin.playingattack.add(data);
}
function playinganimate() {
let time = 0;
if (core.plugin.playingattack.size === 0) return Promise.resolve();
return new Promise((resolve) => {
core.registerAnimationFrame("animateenemyattack", true, (timestamp) => {
if (timestamp - time > 1000 / 60) {
time = timestamp;
core.clearMap(ctx6);
core.plugin.playingattack.forEach((one) => {
const data = flags["animate_" + one.name];
if (!data) {
core.plugin.playingattack.delete(one);
if (core.plugin.playingattack.size === 0) {
core.unregisterAnimationFrame("animateenemyattack");
resolve();
}
} else {
data.imageList.forEach(function (image) {
if (
one.farme >= (image.beforefarme ?? 0) &&
one.farme <= (image.afterfarme ?? data.allFarme)
) {
const img = core.material.images.images?.[image.image];
if (img) {
const gla = image.globalAlpha ?? 100;
const agla = image.aglobalAlpha ?? gla,
beforefarme = image.beforefarme ?? 0;
const afterfarme = image.afterfarme ?? data.allFarme;
ctx6.globalAlpha =
(gla +
((agla - gla) * (one.farme - beforefarme)) /
(afterfarme - beforefarme || 1)) /
100;
const cx =
(image.cx ?? 0) +
(((image.acx ?? 0) - (image.cx ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
cy =
(image.cy ?? 0) +
(((image.acy ?? 0) - (image.cy ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
cw =
(image.cw ?? img.width) +
(((image.acw ?? img.width) -
(image.cw ?? img.width)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
ch =
(image.ch ?? img.height) +
(((image.acw ?? img.height) -
(image.cw ?? img.height)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
x =
(image.x ?? 0) +
(((image.ax ?? 0) - (image.x ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
y =
(image.y ?? 0) +
(((image.ay ?? 0) - (image.y ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
w =
(image.w ?? one.width) +
(((image.aw ?? one.width) - (image.w ?? one.width)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
h =
(image.h ?? one.height) +
(((image.aw ?? one.height) -
(image.w ?? one.height)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1),
angle =
(Math.PI *
((image.image.angle ?? 0) +
(((image.aangle ?? 0) -
(image.image.angle ?? 0)) *
(one.farme - beforefarme)) /
(afterfarme - beforefarme || 1))) /
180;
core.drawImage(
ctx6,
img,
cx,
cy,
cw,
ch,
one.x + (x - data.px) * one.scalex,
one.y + (y - data.py) * one.scaley,
w * one.scalex,
h * one.scaley,
angle
);
}
}
});
data.soundList.forEach(function (sound) {
const lisen =
sound.sound &&
core.sounds[sound.sound] &&
core.musicStatus.soundStatus;
if (one.farme == sound.startfarme && lisen) {
if (sound.stopbefore) core.stopSound();
core.playSound(sound.sound);
}
});
one.farme++;
ctx6.restore();
if (one.farme > data.allFarme) {
core.clearMap(ctx6);
core.plugin.playingattack.delete(one);
if (core.plugin.playingattack.size === 0) {
core.unregisterAnimationFrame("animateenemyattack");
resolve();
}
}
}
});
}
});
});
}
main.dom.gameDraw.appendChild(animateAttack);
const { lcm, gcd } = core.plugin.utils;
function animateonAttack(name, onenemy) {
if (onenemy) {
playanimate(name, 290, 180);
} else {
playanimate(name, 130, 180);
}
}
this.attackAnimate = function (
enemyId,
heroInfo,
enemyInfo,
equipInfo,
oneTurn,
heroDiffList,
enemyDiffList,
heroanimateList,
enemyanimateList
) {
//参数分别为怪物id、真实属性战斗信息特殊装备如火焰风衣属性特殊装备属性为以元组{equipId,oneDamage,speed,now:0}构成的数组(列出每个需要计算的特殊装备,没有则为空数组或不填)
core.lockControl();
core.clearMap(ctx);
core.status.event.id = "attackAnimate";
let turn = 0;
enemyInfo.id = enemyId;
enemyInfo.cls = core.getClsFromId(enemyId);
enemyInfo.name = core.material.enemys[enemyId].name;
let max = heroInfo.speed
if (enemyInfo.speed > max) max = enemyInfo.speed
equipInfo.forEach(v => { if (v.speed > max) max = v.speed })
let i = 1
while (oneTurn * i / max < 15) {
i++
}
if (heroInfo.onAttack) heroInfo.now *= i
if (enemyInfo.onAttack) enemyInfo.now *= i
equipInfo.forEach(v => { if (v.onAttack) v.now *= i })
oneTurn *= i
let time = 0,
farme = 0;
return new Promise((res) => {
core.plugin.battle_onclick = function (x, y, px, py) {
const makeBox = ([x, y], [w, h]) => {
return [
[x, y],
[x + w, y + h],
];
};
const inRect = ([x, y], [
[sx, sy],
[dx, dy]
]) => {
return sx <= x && x <= dx && sy <= y && y <= dy;
};
const pos = [px, py];
const easybox = makeBox([90, 232], [80, 22]),
easyclosebox = makeBox([290, 232], [40, 22]),
uneasybox = makeBox([265, 330], [65, 20]),
uneasyclosebox = makeBox([290, 350], [40, 20]);
if (inRect(pos, easybox) && easy) {
easy = false;
} else if (inRect(pos, uneasybox) && !easy) {
easy = true;
} else if (
(inRect(pos, easyclosebox) && easy) ||
(inRect(pos, uneasyclosebox) && !easy)
) {
flags.qukly = !flags.qukly
}
};
async function drawAttackAnimate(
heroInfo,
oneTurn,
enemyInfo,
equipInfo,
farme,
heroDiffList,
enemyDiffList,
heroanimateList,
enemyanimateList
) {
core.lockControl();
core.status.event.id = "battle";
let attack = false;
if (heroInfo.isAttack) attack = true;
if (enemyInfo.isAttack) attack = true;
equipInfo.forEach(function (v) {
if (v.isAttack) attack = true;
});
let onAttack = false;
if (heroInfo.onAttack) onAttack = true;
if (enemyInfo.onAttack) onAttack = true;
equipInfo.forEach(function (v) {
if (v.onAttack) onAttack = true;
});
core.clearMap(ctx);
let animate = Math.floor(farme / 15);
if (flags.qukly) {
while (true) {
let goattack = false
equipInfo.forEach(v => {
if (v.now >= oneTurn) goattack = true
});
if (enemyInfo.now >= oneTurn) goattack = true
if (heroInfo.now >= oneTurn) goattack = true
if (goattack) break;
enemyInfo.now += enemyInfo.speed
heroInfo.now += heroInfo.speed
equipInfo.forEach(function (v) {
v.now += v.speed
});
}
}
if (easy) {
core.fillRect(ctx, 64, 52, 288, 212, "rgba(0,0,0,0.5)");
core.strokeRect(ctx, 64, 52, 288, 212, "rgba(255,255,255,0.5)", 4);
core.setTextAlign(ctx, "center");
core.fillBoldText(
ctx,
hero.name,
127,
148,
"#FFFFFF",
"#000000",
"bold 14px Verdana"
);
core.setTextAlign(ctx, "left");
core.drawIcon(ctx, "hp", 70, 210, 16, 16);
core.fillBoldText(
ctx,
"生命 " + core.formatBigNumber(heroInfo.hp, true),
90,
225,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"详细模式",
90,
250,
"#FFFF60",
"#000000",
"bold 18px Verdana"
);
core.strokeRect(ctx, 112, 159, 32, 48, "rgba(255,255,255,1)", 1);
let img =
attack && heroDiffList[turn].hp < 0 ?
imagelighter(core.material.images.images["hero.webp"]) :
attack && heroDiffList[turn].hp > 0 ?
imagelighter(
core.material.images.images["hero.webp"],
"rgba(0, 255, 0, 0.5)"
) :
core.material.images.images["hero.webp"];
core.drawImage(
ctx,
img,
32 * (animate % 4),
0,
32,
48,
112,
159,
32,
48
);
core.setTextAlign(ctx, "center");
core.fillBoldText(
ctx,
enemyInfo.name,
289,
148,
"#FFFFFF",
"#000000",
"bold 14px Verdana"
);
core.setTextAlign(ctx, "right");
if (enemyInfo.cls === "enemys") {
core.strokeRect(ctx, 272, 175, 32, 32, "rgba(255,255,255,1)", 1);
let img =
attack && enemyDiffList[turn].hp < 0 ?
imagelighter(core.getBlockInfo(enemyInfo.id).image) :
attack && enemyDiffList[turn].hp > 0 ?
imagelighter(
core.getBlockInfo(enemyInfo.id).image,
"rgba(0, 255, 0, 0.5)"
) :
core.getBlockInfo(enemyInfo.id).image;
core.drawImage(
ctx,
img,
32 * (animate % 2),
core.getBlockInfo(enemyInfo.id).posY * 32,
32,
32,
272,
175,
32,
32
);
} else {
core.strokeRect(ctx, 272, 159, 32, 48, "rgba(255,255,255,1)", 1);
let img =
attack && enemyDiffList[turn].hp < 0 ?
imagelighter(core.getBlockInfo(enemyInfo.id).image) :
attack && enemyDiffList[turn].hp > 0 ?
imagelighter(
core.getBlockInfo(enemyInfo.id).image,
"rgba(0, 255, 0, 0.5)"
) :
core.getBlockInfo(enemyInfo.id).image;
core.drawImage(
ctx,
img,
32 * (animate % 4),
core.getBlockInfo(enemyInfo.id).posY * 48,
32,
48,
272,
159,
32,
48
);
}
core.drawIcon(ctx, "hp", 330, 210, 16, 16);
core.fillBoldText(
ctx,
core.formatBigNumber(enemyInfo.hp, true) + " 生命",
330,
225,
"#FFFFFF",
"#000000",
"bold 14px Verdana"
);
core.fillBoldText(
ctx,
flags.qukly ? "正常" : "极速",
330,
250,
"#FFFF60",
"#000000",
"bold 18px Verdana"
);
core.fillBoldText(
ctx,
"V",
219,
183,
"#FFFFFF",
"#000000",
"bold 48px pala"
);
core.fillBoldText(
ctx,
"s",
231,
183,
"#FFFFFF",
"#000000",
"bold 36px pala"
);
if (!attack && !onAttack && !flags.qukly) enemyInfo.now += enemyInfo.speed;
let enemynow = Math.min(100 + (enemyInfo.now / oneTurn) * 215, 315);
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
ctx.moveTo(enemynow, 120);
ctx.lineTo(enemynow + 5, 110);
ctx.lineTo(enemynow - 5, 110);
ctx.closePath();
ctx.fill();
if (enemyInfo.cls === "enemys") {
core.drawImage(
ctx,
core.getBlockInfo(enemyInfo.id).image,
32,
core.getBlockInfo(enemyInfo.id).posY * 32,
32,
32,
enemynow - 16,
74,
32,
32
);
} else {
core.drawImage(
ctx,
core.getBlockInfo(enemyInfo.id).image,
32,
core.getBlockInfo(enemyInfo.id).posY * 48,
32,
48,
enemynow - 16,
58,
32,
48
);
}
core.drawLine(ctx, 100, 125, 315, 125, "#FFFFFF", 5);
equipInfo.forEach(function (v) {
if (!attack && !onAttack) v.now += v.speed;
let vnow = Math.min(100 + (v.now / oneTurn) * 215, 315);
ctx.beginPath();
ctx.moveTo(vnow, 120);
ctx.lineTo(vnow + 5, 110);
ctx.lineTo(vnow - 5, 110);
ctx.closePath();
ctx.fill();
core.drawIcon(ctx, v.id, vnow - 16, 54, 32, 32);
});
if (!attack && !onAttack && !flags.qukly) heroInfo.now += hero.speed;
let heronow = Math.min(100 + (heroInfo.now / oneTurn) * 215, 315);
ctx.beginPath();
ctx.moveTo(heronow, 120);
ctx.lineTo(heronow + 5, 110);
ctx.lineTo(heronow - 5, 110);
ctx.closePath();
ctx.fill();
core.drawImage(
ctx,
"hero.webp",
0,
0,
32,
48,
heronow - 16,
58,
32,
48
);
} else {
core.fillRect(ctx, 64, 52, 288, 320, "rgba(0,0,0,0.5)");
core.strokeRect(ctx, 64, 52, 288, 320, "rgba(255,255,255,0.5)", 4);
core.setTextAlign(ctx, "center");
core.fillBoldText(
ctx,
hero.name,
127,
148,
"#FFFFFF",
"#000000",
"bold 14px Verdana"
);
core.setTextAlign(ctx, "left");
core.drawIcon(ctx, "hp", 70, 210, 16, 16);
core.drawIcon(ctx, "atk", 70, 230, 16, 16);
core.drawIcon(ctx, "def", 70, 250, 16, 16);
core.drawIcon(ctx, "I374", 70, 270, 16, 16);
core.drawIcon(ctx, "I375", 70, 290, 16, 16);
core.drawIcon(ctx, "mdef", 70, 310, 16, 16);
core.drawIcon(ctx, "amulet", 70, 330, 16, 16);
core.drawIcon(ctx, "jumpShoes", 70, 350, 16, 16);
core.fillBoldText(
ctx,
"生命 " + core.formatBigNumber(heroInfo.hp, true),
90,
225,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"攻击 " + core.formatBigNumber(heroInfo.atk),
90,
245,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"防御 " + core.formatBigNumber(heroInfo.def),
90,
265,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"法强 " + core.formatBigNumber(heroInfo.spell),
90,
285,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"法攻 " + core.formatBigNumber(heroInfo.matk),
90,
305,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"护盾 " + core.formatBigNumber(heroInfo.mhp),
90,
325,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"法抗 " + heroInfo.mdef + "%",
90,
345,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"速度 " + core.formatBigNumber(heroInfo.speed),
90,
365,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.strokeRect(ctx, 112, 159, 32, 48, "rgba(255,255,255,1)", 1);
let img =
attack && heroDiffList[turn].hp < 0 ?
imagelighter(core.material.images.images["hero.webp"]) :
attack && heroDiffList[turn].hp > 0 ?
imagelighter(
core.material.images.images["hero.webp"],
"rgba(0, 255, 0, 0.5)"
) :
core.material.images.images["hero.webp"];
core.drawImage(
ctx,
img,
32 * (animate % 4),
0,
32,
48,
112,
159,
32,
48
);
core.setTextAlign(ctx, "center");
core.fillBoldText(
ctx,
enemyInfo.name,
289,
148,
"#FFFFFF",
"#000000",
"bold 14px Verdana"
);
core.setTextAlign(ctx, "right");
if (enemyInfo.cls === "enemys") {
core.strokeRect(ctx, 272, 175, 32, 32, "rgba(255,255,255,1)", 1);
let img =
attack && enemyDiffList[turn].hp < 0 ?
imagelighter(core.getBlockInfo(enemyInfo.id).image) :
attack && enemyDiffList[turn].hp > 0 ?
imagelighter(
core.getBlockInfo(enemyInfo.id).image,
"rgba(0, 255, 0, 0.5)"
) :
core.getBlockInfo(enemyInfo.id).image;
core.drawImage(
ctx,
img,
32 * (animate % 2),
core.getBlockInfo(enemyInfo.id).posY * 32,
32,
32,
272,
175,
32,
32
);
} else {
core.strokeRect(ctx, 272, 159, 32, 48, "rgba(255,255,255,1)", 1);
let img =
attack && enemyDiffList[turn].hp < 0 ?
imagelighter(core.getBlockInfo(enemyInfo.id).image) :
attack && enemyDiffList[turn].hp > 0 ?
imagelighter(
core.getBlockInfo(enemyInfo.id).image,
"rgba(0, 255, 0, 0.5)"
) :
core.getBlockInfo(enemyInfo.id).image;
core.drawImage(
ctx,
img,
32 * (animate % 4),
core.getBlockInfo(enemyInfo.id).posY * 48,
32,
48,
272,
159,
32,
48
);
}
core.drawIcon(ctx, "hp", 330, 210, 16, 16);
core.drawIcon(ctx, "atk", 330, 230, 16, 16);
core.drawIcon(ctx, "def", 330, 250, 16, 16);
core.drawIcon(ctx, "I374", 330, 270, 16, 16);
core.drawIcon(ctx, "amulet", 330, 290, 16, 16);
core.drawIcon(ctx, "jumpShoes", 330, 310, 16, 16);
core.fillBoldText(
ctx,
core.formatBigNumber(enemyInfo.hp, true) + " 生命",
330,
225,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
core.formatBigNumber(enemyInfo.atk) + " 攻击",
330,
245,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
core.formatBigNumber(enemyInfo.def) + " 防御",
330,
265,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
(enemyInfo.spell ?? 0) + " 法强",
330,
285,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
(enemyInfo.mdef ?? 0) * 100 + "% 法抗",
330,
305,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
core.formatBigNumber(enemyInfo.speed) + " 速度",
330,
325,
"#FFFFFF",
"#000000",
"bold 14px Arial"
);
core.fillBoldText(
ctx,
"简易模式",
330,
345,
"#FFFF60",
"#000000",
"bold 16px Verdana"
);
core.fillBoldText(
ctx,
flags.qukly ? "正常" : "极速",
330,
365,
"#FFFF60",
"#000000",
"bold 16px Verdana"
);
core.fillBoldText(
ctx,
"V",
219,
183,
"#FFFFFF",
"#000000",
"bold 48px pala"
);
core.fillBoldText(
ctx,
"s",
231,
183,
"#FFFFFF",
"#000000",
"bold 36px pala"
);
if (!attack && !onAttack & !flags.qukly) enemyInfo.now += enemyInfo.speed;
let enemynow = Math.min(100 + (enemyInfo.now / oneTurn) * 215, 315);
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
ctx.moveTo(enemynow, 120);
ctx.lineTo(enemynow + 5, 110);
ctx.lineTo(enemynow - 5, 110);
ctx.closePath();
ctx.fill();
if (enemyInfo.cls === "enemys") {
core.drawImage(
ctx,
core.getBlockInfo(enemyInfo.id).image,
32,
core.getBlockInfo(enemyInfo.id).posY * 32,
32,
32,
enemynow - 16,
74,
32,
32
);
} else {
core.drawImage(
ctx,
core.getBlockInfo(enemyInfo.id).image,
32,
core.getBlockInfo(enemyInfo.id).posY * 48,
32,
19,
enemynow - 16,
58,
32,
48
);
}
core.drawLine(ctx, 100, 125, 315, 125, "#FFFFFF", 5);
equipInfo.forEach(function (v) {
if (!attack && !onAttack) v.now += v.speed;
let vnow = Math.min(100 + (v.now / oneTurn) * 215, 315);
ctx.beginPath();
ctx.moveTo(vnow, 120);
ctx.lineTo(vnow + 5, 110);
ctx.lineTo(vnow - 5, 110);
ctx.closePath();
ctx.fill();
core.drawIcon(ctx, v.id, vnow - 16, 54, 32, 32);
});
if (!attack && !onAttack && !flags.qukly) heroInfo.now += hero.speed;
let heronow = Math.min(100 + (heroInfo.now / oneTurn) * 215, 315);
ctx.beginPath();
ctx.moveTo(heronow, 120);
ctx.lineTo(heronow + 5, 110);
ctx.lineTo(heronow - 5, 110);
ctx.closePath();
ctx.fill();
core.drawImage(
ctx,
"hero.webp",
0,
0,
32,
48,
heronow - 16,
58,
32,
48
);
}
let nowattacking = false;
if (heroInfo.now >= oneTurn && !heroInfo.isAttack) {
heroInfo.onAttack = false;
heroInfo.isAttack = true;
nowattacking = true;
}
if (enemyInfo.now >= oneTurn && !enemyInfo.isAttack) {
enemyInfo.onAttack = false;
enemyInfo.isAttack = true;
nowattacking = true;
}
const equipanimate = [];
equipInfo.forEach((v) => {
if (v.now >= oneTurn && !v.isAttack) {
v.isAttack = true;
v.onAttack = false;
nowattacking = true;
equipanimate.push(v);
}
});
if (!attack && nowattacking) {
let herodamage = enemyDiffList[turn].hp;
if (herodamage > 0) herodamage = "+" + herodamage;
let text = herodamage === 0 ? "抵抗" : herodamage;
Dove.MorePerform.ShowDamagePop.PopDamage(
ctx, // 默认画布名称
270, // 英雄位置 x
160, // 英雄位置 y
text, // 伤害值
18, // 默认字体大小
"Arial", //默认字体
typeof text === "string" && text.startsWith("+") ?
"#22FF44" :
typeof text === "string" ?
"#FFFFFF" :
null, // 默认颜色
text === "抵抗" ? "#000000" : null, // 默认描边颜色
0, // 默认水平速度
-1, // 默认垂直速度
0, // 默认重力
90 // 默认显示时长(帧数)
);
for (const v in enemyDiffList[turn]) {
enemyInfo[v] += enemyDiffList[turn][v];
}
enemyanimateList[turn].forEach((v) => animateonAttack(v, true));
let enemydamage = heroDiffList[turn].hp;
if (enemydamage > 0) enemydamage = "+" + enemydamage;
text = enemydamage === 0 ? "抵抗" : enemydamage;
Dove.MorePerform.ShowDamagePop.PopDamage(
ctx, // 默认画布名称
110, // 英雄位置 x
160, // 英雄位置 y
text, // 伤害值
18, // 默认字体大小
"Arial", //默认字体
typeof text === "string" && text.startsWith("+") ?
"#22FF44" :
typeof text === "string" ?
"#FFFFFF" :
null, // 默认颜色
text === "抵抗" ? "#000000" : null, // 默认描边颜色
0, // 默认水平速度
-1, // 默认垂直速度
0, // 默认重力
90 // 默认显示时长(帧数)
);
for (const v in heroDiffList[turn]) {
heroInfo[v] += heroDiffList[turn][v];
}
heroanimateList[turn].forEach((v) => animateonAttack(v, false));
if (enemyDiffList[turn] < 0) enemyInfo.inAttack = true;
if (heroDiffList[turn] < 0) heroInfo.inAttack = true;
if (heroInfo.hp < 0) heroInfo.hp = 0;
if (enemyInfo.hp < 0) enemyInfo.hp = 0;
await Promise.all([
await playinganimate(),
new Promise((resolve) => {
if (heroInfo.isAttack) {
heroInfo.now = 0;
heroInfo.isAttack = false;
}
resolve();
}),
new Promise((resolve) => {
if (enemyInfo.isAttack) {
enemyInfo.now = 0;
enemyInfo.isAttack = false;
}
resolve();
}),
new Promise((resolve) => {
if (equipanimate.length > 0) {
equipanimate.forEach((v) => {
v.now = 0;
v.isAttack = false;
});
}
resolve();
}),
new Promise((resolve) => {
turn++;
resolve();
}),
]);
if (heroInfo.hp <= 0 || enemyInfo.hp <= 0) {
core.status.event.id = "";
core.unregisterAnimationFrame("attackAnimate");
core.clearMap(ctx);
core.closePanel();
res();
}
}
}
core.registerAnimationFrame("attackAnimate", true, (temptime) => {
if (temptime - time > 1000 / 60) {
time = temptime;
drawAttackAnimate(
heroInfo,
oneTurn,
enemyInfo,
equipInfo,
farme,
heroDiffList,
enemyDiffList,
heroanimateList,
enemyanimateList
);
farme++;
}
});
});
};
},
"剧情内容": function () {
// 每项为一个数组,第一项是名字,第二项是对话内容,第三项为音频文件名(没有则不需要第三项)
// 回放只会在同一个this下回放进入剧情前请以事件块声明进入哪个剧情数组
this.chapter000 = [
["", "这些天,街道不曾下雨。"],
["", "所以,那浸湿地面的,定是那些女孩们流落的鲜血无疑。"],
["", "我蹲在充斥着铁锈味般恶臭的小巷中,悠闲地如是想着。"],
["", "扑哧。"],
["", "耳旁再次响起象征着某个女孩子死去的声音。"],
["", "再一次——"],
["", "再一次。"],
["", "女子们被肢解成单纯的肉块。"],
["", "我任由流下的血浸满全身,屏住自己的呼吸。"],
["", "祈求自己能拥有从猎人手中逃脱的幸运。"],
["", "扑哧。"],
["", "直到刚才,我们还坐在去往娼馆的马车的路上。"],
["", "而在这之中的某些人,已经不在这个世上了。"],
["", "不,应该把“某些”换成“几乎所有”才更为恰当吧。"],
["", "恐怕,不久之后我也会变成小巷中血腥的装饰品。"],
["", "我是为了得到这种死法,才辛苦苟活至今的吗?"],
["", "来个人告诉我啊——"],
["", "谁都好。"],
["", "来人啊!!"],
["少女", "「呃······!?」", "aiy010000010.opus"], //小动物01
["", "漆黑的物体充斥了我的整个视野"],
["", "我很快意识到,那是只很大的脚。"],
["", "必须要出声求救。"],
["", "可是,耳中却只能听到自己的牙关不停交战的声音。"],
["", "我是如此的无助。"],
["", "逃跑也好,道歉也罢。"],
["", "就连抬头看一眼将要杀掉我的人的面孔都做不到。"],
["少女", "「······被杀」", "aiy010000020.opus"], //小动物02
["", "会被杀。"],
["", "会被杀!!"],
["", "来自内心深处的冰冷预感,渐渐地在体内蔓延开来。"],
["少女", "「不,不要······」", "aiy010000030.opus"], //小动物03
]
this.chapter001 = [
["", "浮游都市,《诺瓦斯·艾蒂尔》。"],
["", "《特别受灾地区》——"],
["", "通称,《牢狱》"],
["", "是被险峻的峭壁环绕,与世隔绝的,都市的最底部。"],
]
this.chapter002 = [
["年轻人", "「放开我!」", "aiy710000010.opus"], //龙套1-01
["年轻人", "「我只是在帮那个女人而已!」", "aiy710000020.opus"], //龙套1-02
["年轻人", "「你们没听到吗!?」", "aiy710000030.opus"], //龙套1-03
["年轻人", "「她是被受骗才会被卖到娼馆来的」", "aiy710000040.opus"], //龙套1-04
[
"年轻人",
"「用肮脏的手段把钱借给她父母的,就是你们这些家伙吧!?」",
"aiy710000050.opus",
], //龙套1-05
["年轻人", "「给我说些什么啊」", "aiy710000060.opus"], //龙套1-06
["凯伊姆", "「这些话等到了娼馆再说吧」", "aiy310000010.opus"], //男主01
["凯伊姆", "「我来抓你,只是受雇于人而已」", "aiy310000020.opus"], //男主02
["", "我走进娼馆《莉莉乌姆》的接待室。"],
["", "正在桌旁整理账簿的奥兹停下手头的工作,抬起头向我看来。"],
["奥兹", "「这不是凯伊姆先生吗,辛苦了」", "aiy350000010.opus"], //金锁高官01
["奥兹", "「委托已经完成了吗?」", "aiy350000020.opus"], //金锁高官02
["凯伊姆", "「啊啊,是这家伙没错吧」", "aiy310000030.opus"], //男主03
["", "奥兹用只要接触到就能杀人般的眼神在男人脸上搜过。"],
["奥兹", "「没错,就是这个人」", "aiy350000030.opus"], //金锁高官03
["凯伊姆", "「是么」", "aiy310000040.opus"], //男主04
["年轻人", "「你,你们要对我做什么」", "aiy710000070.opus"], //龙套1-07
["奥兹", "「······」", "aiy350000040.opus"], //金锁高官04
["", "奥兹用一个眼神,就让男人闭上了嘴。"],
["", "然后,向我这边转过身来。"],
["奥兹", "「抱歉啊,总是麻烦你去做这些无聊的事」", "aiy350000050.opus"], //金锁高官05
["奥兹", "「都怪我们这边的年轻人太没用」", "aiy350000060.opus"], //金锁高官06
["凯伊姆", "「客套话就免了」", "aiy310000050.opus"], //男主05
["奥兹", "「这还真是失礼了」", "aiy350000070.opus"], //金锁高官07
["奥兹", "「喂,来个人」", "aiy350000080.opus"], //金锁高官08
["光头男人", "「是」", "aiy820000010.opus"], //龙套2-01
["奥兹", "「凯伊姆先生做完工作回来了」", "aiy350000090.opus"], //金锁高官09
["光头男人", "「是,是,那个······」", "aiy820000020.opus"], //龙套2-02
["奥兹", "「我是要你拿些酒来,这个蠢材!」", "aiy350000100.opus"], //金锁高官10
["", "喀!"],
["", "奥兹扔出的烟灰缸砸中了手下的额头。"],
["", "鲜血四溅。"],
["凯伊姆", "「不用这么麻烦」", "aiy310000060.opus"], //男主06
["凯伊姆", "「我接下来要去《菲诺列塔》」", "aiy310000070.opus"], //男主07
["奥兹", "「喔唷」", "aiy350000110.opus"], //金锁高官11
[
"奥兹",
"「既然如此,我就不留您在这里喝难饮的劣质酒了」",
"aiy350000120.opus",
], //金锁高官12
["", "奥兹斜眼看着正捂住额头呻吟的手下,轻描淡写地说道。"],
["凯伊姆", "「用这些钱去买药」", "aiy310000080.opus"], //男主08
["", "我将几枚铜钱仍在那个手下的身前。"],
["奥兹", "「凯伊姆先生,不用对他们这么好」", "aiy350000130.opus"], //金锁高官13
["凯伊姆", "「无妨」", "aiy310000090.opus"], //男主09
["凯伊姆", "「话说回来,那个要落跑的女人呢?」", "aiy310000100.opus"], //男主10
[
"奥兹",
"「我把她交给那些年轻人了,现在应该正在体会人生的严苛吧」",
"aiy350000140.opus",
], //金锁高官14
[
"奥兹",
"「正好,趁此机会凯伊姆先生也来享受一番如何?」",
"aiy350000150.opus",
], //金锁高官15
["年轻人", "「你,你们这些家伙,要对她做什么!?」", "aiy710000080.opus"], //龙套1-08
["", "咣!"],
["", "奥兹给了他一拳。"],
["", "一击即倒。"],
["", "喀,咚,咯!"],
["", "奥兹毫不留情地向男人的脸上踩去。"],
["年轻人", "「咕······呃咳······」", "aiy710000090.opus"], //龙套1-09
["", "折断的牙齿伴着血泡被吐出。"],
["", "这份白色在鲜红色的液体中格外显眼。"],
[
"年轻人",
"「你们以为做出这种事······卫兵会坐视不理吗······」",
"aiy710000100.opus",
], //龙套1-10
["奥兹", "「啊啊,不会坐视不理的」", "aiy350000160.opus"], //金锁高官16
[
"奥兹",
"「应该会拿出你的钱包,和我们商量如何瓜分吧」",
"aiy350000170.opus",
], //金锁高官17
["年轻人", "「那,那种事······」", "aiy710000110.opus"], //龙套1-11
["", "这在牢狱是理所当然的事。"],
["奥兹", "「怎么,头一回来牢狱么?」", "aiy350000180.opus"], //金锁高官18
["", "男人点了点头。"],
[
"奥兹",
"「为了被骗的女人而来到牢狱,真是个规矩人啊」",
"aiy350000190.opus",
], //金锁高官19
["奥兹", "「······前提是,被骗的人不是你」", "aiy350000200.opus"], //金锁高官20
["年轻人", "「你说······我被骗了?」", "aiy710000120.opus"], //龙套1-12
["年轻人", "「那,那是怎么回事!?」", "aiy710000130.opus"], //龙套1-13
["奥兹", "「不用急,今天晚上会好好告诉你的」", "aiy350000210.opus"], //金锁高官21
["", "奥兹抓起男人的脸。"],
["", "为引诱客人的怜悯之心而装纯,是娼妇的惯用手段。"],
["", "双亲被骗而借钱,结果作为抵押而将自己卖到这里,这是典型的说法。"],
[
"",
"如果只是头脑发热而成为常客也就罢了,这次的男人热血过头,居然想出了要带女人私奔的计划。",
],
[
"",
"虽然女人半开玩笑地予以拒绝,但不知天高地厚的这家伙还是拉着她逃跑了。",
],
["", "不过,想要逃脱追击本来就是不可能的任务。"],
["", "但即便如此,这种事情还是会一再的出现。"],
["", "说谎的女人和被骗的男人。"],
["", "在娼馆街,这是令人看到生厌的日常的风景。"],
["凯伊姆", "「我要走了」", "aiy310000110.opus"], //男主11
["奥兹", "「好的,下次再麻烦您」", "aiy350000220.opus"], //金锁高官22
["奥兹", "「之后吉克先生会将谢礼交给您的」", "aiy350000230.opus"], //金锁高官23
["凯伊姆", "「啊啊」", "aiy310000120.opus"], //男主12
["", "我背向奥兹走出娼馆。"],
["凯伊姆", "「······?」", "aiy310000130.opus"], //男主13
["", "从远方传来微弱的歌声。"],
["", "是关卡广场的方向。"],
["", "对了。"],
["", "今天有觐见圣女的仪式。"],
["", "当代的圣女伊莲——"],
["", "俗称《盲眼之圣女》,据说即使在历代的圣女中,人气也是数一数二的。"],
["", "广场上的人估计相当多吧。"],
["", "虽然我也想去看看她长什么样,不过要在人潮中挤来挤去就免了。"],
["", "还是老老实实去菲诺列塔喝烧酒吧。"],
["", "正当我这样想着的时候,一个身影自小巷的那头走来。"],
["凯伊姆", "「艾莉斯」", "aiy310000140.opus"], //男主14
];
this.chapter01 = [
["艾莉斯", "「啊,凯伊姆」", "aiy020000005.opus"], //医生00.5
["艾莉斯", "「正好,我还想要去找你呢」", "aiy020000010.opus"], //医生01
[
"艾莉斯",
"「没想到凯伊姆会主动出现······这是命运吗?」",
"aiy020000020.opus",
], //医生02
["凯伊姆", "「显然不是吧」", "aiy310000150.opus"], //男主15
["艾莉斯", "「啊,是么」", "aiy020000030.opus"], //医生03
["", "艾莉斯挑了挑整齐的双眉,微微地哼了一声。"],
[
"",
"虽然是个相当引人注目的美人,但她这个将亲切儿子丢入无底深渊的性格,为自己扣了不少的分",
],
["", "给人印象最深的,就是那潭水般的双瞳。"],
["", "在漆黑的瞳孔中,完全看不出感情的波动。"],
["艾莉斯", "「喜欢我的眼睛吗?」", "aiy020000040.opus"], //医生04
["艾莉斯", "「如果想要的话就给你吧?」", "aiy020000050.opus"], //医生05
["凯伊姆", "「用不着」", "aiy310000160.opus"], //男主16
["艾莉斯", "「阿拉,可惜」", "aiy020000060.opus"], //医生06
["凯伊姆", "「那么,找我有什么事」", "aiy310000170.opus"], //男主17
["艾莉斯", "「梅尔特的钱好像被偷了」", "aiy020000070.opus"], //医生07
["凯伊姆", "「钱被偷了?都几岁了还这么没用」", "aiy310000180.opus"], //男主18
["艾莉斯", "「不要对我说啊」", "aiy020000080.opus"], //医生08
[
"凯伊姆",
"「那家伙,该不会说要让我去抓那个小偷吧?」",
"aiy310000190.opus",
], //男主19
["艾莉斯", "「就是这样」", "aiy020000090.opus"], //医生09
["凯伊姆", "「笨蛋吗」", "aiy310000200.opus"], //男主20
["凯伊姆", "「如果是小钱的话,就当做是买个教训吧」", "aiy310000210.opus"], //男主21
["艾莉斯", "「说起来,被盗的是这个月的上纳金」", "aiy020000100.opus"], //医生10
["凯伊姆", "「你说什么?」", "aiy310000220.opus"], //男主22
["艾莉斯", "「用这些钱买教训,也太过奢侈了呢」", "aiy020000110.opus"], //医生11
["凯伊姆", "「知道了,我去找」", "aiy310000230.opus"], //男主23
["凯伊姆", "「小偷的特征呢」", "aiy310000240.opus"], //男主24
["艾莉斯", "「男孩子」", "aiy020000120.opus"], //医生12
["艾莉斯", "「······而且,背后有翅膀」", "aiy020000130.opus"], //医生13
[
"艾莉斯",
"「虽然姑且是藏在身后,但是仔细观察的话是很明显的」",
"aiy020000140.opus",
], //医生14
["凯伊姆", "「羽化病吗」", "aiy310000250.opus"], //男主25
[
"艾莉斯",
"「那些人可是毫不留情的,所以即使是为了那个孩子,也要赶快抓到他」",
"aiy020000150.opus",
], //医生15
["凯伊姆", "「注意到他逃窜的方向了吗?」", "aiy310000260.opus"], //男主26
["艾莉斯", "「广场那边」", "aiy020000160.opus"], //医生16
[
"艾莉斯",
"「虽然刚才《不蚀金锁》的人去追了,不过多半是······」",
"aiy020000170.opus",
], //医生17
["凯伊姆", "「偏偏还是广场吗」", "aiy310000280.opus"], //男主28
["艾莉斯", "「今天是觐见圣女大人的日子」", "aiy020000180.opus"], //医生18
["凯伊姆", "「我知道」", "aiy310000290.opus"], //男主29
["凯伊姆", "「尽量找找看就好」", "aiy310000300.opus"], //男主30
];
this.chapter02 = [
["不蚀金锁成员", "「凯伊姆先生,凯伊姆先生」", "aiy860000010.ogg"],
[
"不蚀金锁成员",
"「您已经和艾莉斯大夫见过面了吗?」",
"aiy860000020.ogg",
],
["凯伊姆", "「啊啊,所以才会追过来的」", "aiy310000310.ogg"], //男主31
["凯伊姆", "「看到小偷了吗?」", "aiy310000320.ogg"], //男主32
[
"不蚀金锁成员",
"「没有,他向广场那边逃了过去,今天这么拥挤,我们也只能放弃了」",
"aiy860000030.ogg",
],
[
"不蚀金锁成员",
"「不过,我也只是刚好在店里所以才追了过去,并不是受人所托」",
"aiy860000040.ogg",
],
["不蚀金锁成员", "「我已经准备撤退了」", "aiy860000050.ogg"],
["不蚀金锁成员", "「凯伊姆先生还要继续追吗?」", "aiy860000060.ogg"],
["凯伊姆", "「啊啊」", "aiy310000330.ogg"], //男主33
["", "做完情报交换之后,我跟男人道别。"],
["凯伊姆", "「和我想的一样啊······」", "aiy310000340.ogg"], //男主34
["", "在牢狱中最大的广场上,聚集着看不到尽头的人群。"],
["", "就算是来参见圣女祈祷,这人数也太多了点吧。"],
["", "自然,我也找不到逃跑的孩子。"],
["", "是混杂到人群中了吧。"],
["", "如果已经从广场上脱身了的话,就更难发现了。"],
["", "只好赌他还在这里了。"],
["", "我先移动到了一个视野良好的地方。"],
["", "从这里,一眼就可以看到人群的变化。"],
["", "广场还是沸腾起来。"],
["", "抬头望去,原来是在天台之上出现了一个人影"],
["", "但是,与周围的期待不同,现身的是一名中年的神官。"],
["", "骂声四溢。"],
["", "神官则是笑着摆正衣领"],
["神官", "「从现在开始,举行谒见的仪式」", "aiy440000010.ogg"], //神官01
[
"神官",
"「在参见那位大人之前,我希望牢狱的诸位再次思考这个《诺瓦斯·艾蒂尔》存在的意义······」",
"aiy440000020.ogg",
], //神官02
[
"神官",
"「初代圣女伊莲大人,便是也难怪这崇高的祈祷之力,令《诺瓦斯·艾蒂尔》浮在空中,拯救了我们的祖先」",
"aiy440000030.ogg",
], //神官03
[
"神官",
"「这之后的几百年来,传承了初代大人力量的历代圣女伊莲大人,让这里留在了空中」",
"aiy440000040.ogg",
], //神官04
[
"神官",
"「这座都市是被圣女大人守护的人类最后的圣域,而我们则是被选召的虔诚的信徒」",
"aiy440000050.ogg",
], //神官05
[
"神官",
"「怀着对圣女的感激祈祷吧,感谢圣女伊莲吧!并献上祈祷!」",
"aiy440000060.ogg",
], //神官06
["圣女", "「不忘感谢与祈祷,神才会拯救我们」", "aiy030000010.ogg"], //圣女01
["圣女", "「与我一起,向神虔诚地祈祷吧」", "aiy030000020.ogg"], //圣女02
["", "广场上欢声雷动。"],
["", "圣女没有回应喧嚣的人声,而是静静地合上双眼面向广场。"],
["", "虽然感觉有些冷淡,但总比像个傻瓜似的笑着向这群人挥手要强。"],
["", "她掌握着这条街道,还有在这条街上生活的人的命运。"],
["", "比起揽得人气,她更想要为了街道的继续存在而献出全力。"],
["", "也是为了不让《大崩落》的惨剧再度发生。"],
["", "十几年前的那场悲剧。"],
[
"",
"虽然在我脑海中的记忆已经相当模糊,但哪怕只是稍有触及,不快的感觉都会在胸口蔓延开。",
],
["凯伊姆", "「······」", "aiy310000350.ogg"], //男主35
["", "这时我才想起,现在不是我在这里看圣女的时候。"],
["女声", "「——っ!?」", "aiy510000010.ogg"],
["围观的女人", "「羽,羽化病人!?」", "aiy510000020.ogg"],
["围观的中年人", "「喂,谁去叫下羽狩」", "aiy720000010.ogg"],
[
"惊慌的观众",
"「你这家伙不要靠近我,要是传染了可怎么办」",
"aiy730000010.ogg",
],
["粗鲁的观众", "「你这小鬼赶快滚开」", "aiy740000010.ogg"],
["凯伊姆", "「接下来」", "aiy310000360.ogg"], //男主36
["圣女", "「发生什么事了?看上去似乎很嘈杂」", "aiy030000030.ogg"], //圣女03
[
"随从",
"「似乎是某个人逃跑了······具体的我也不是很清楚」",
"aiy130000010.ogg",
], //侍从01
[
"神官",
"「圣女大人,继续待在天台上可能会出事,请您先回到室内吧」",
"aiy440000070.ogg",
], //神官07
[
"圣女",
"「不用在意我,比起那个,我更关心究竟发生了什么事」",
"aiy030000050.ogg",
], //圣女05
["神官", "「对不起,我真的不知道」", "aiy440000080.ogg"], //神官08
["圣女", "「······是吗」", "aiy030000060.ogg"], //圣女06
["男", "「恕我僭越,请准许我说明情况」", "aiy320000010.ogg"], //男主他哥01
["男", "「在来觐见的人群中出现了《羽化病》的患者」", "aiy320000020.ogg"], //男主他哥02
["男", "「围观的人群因而产生了骚动」", "aiy320000030.ogg"], //男主他哥03
[
"男",
"「现在,《防疫局》已经派遣了部队。我想不久之后,他们就会安静下来了」",
"aiy320000040.ogg",
], //男主他哥04
["圣女", "「羽化病······」", "aiy030000070.ogg"], //圣女07
["男", "「怎么了?」", "aiy320000050.ogg"], //男主他哥05
["圣女", "「没什么」", "aiy030000080.ogg"], //圣女08
["圣女", "「辛苦了,你的名字是?」", "aiy030000090.ogg"], //圣女09
[
"男",
"「属下是在防疫局任职的,鲁基乌斯· 迪斯·米利尤」",
"aiy320000060.ogg",
], //男主他哥06
[
"神官",
"「噢噢,阁下就是鲁基乌斯卿吗,我听说过你的传闻」",
"aiy440000090.ogg",
], //神官09
["神官", "「阁下是在工作上相当出色的人呢」", "aiy440000100.ogg"], //神官10
["鲁基乌斯", "「不敢当」", "aiy320000070.ogg"], //男主他哥07
[
"鲁基乌斯",
"「话说回来,这次是属下警备工作的失职。让圣女大人见到这不成体统的一面,请您见谅」",
"aiy320000080.ogg",
], //男主他哥08
[
"圣女",
"「即使是目不见物的我,也能感受到聚集于此的民众数量之多。警备工作难以展开也在情理之中」",
"aiy030000100.ogg",
], //圣女10
["鲁基乌斯", "「属下不胜惶恐」", "aiy320000090.ogg"], //男主他哥09
[
"鲁基乌斯",
"「接下来属下还要回到工作岗位上,在这里就先告退了」",
"aiy320000100.ogg",
], //男主他哥10
["圣女", "「鲁基乌斯先生」", "aiy030000110.ogg"], //圣女11
["鲁基乌斯", "「属下在」", "aiy320000110.ogg"], //男主他哥11
["圣女", "「你是怎样看待羽狩的工作的呢?」", "aiy030000120.ogg"], //圣女12
["神官", "「圣,圣女大人」", "aiy440000110.ogg"], //神官11
[
"鲁基乌斯",
"「防疫局的工作是国王陛下赐予的重要职务。属下非常荣幸能够为这个都市的繁荣尽一份微薄之力」",
"aiy320000120.ogg",
], //男主他哥12
["神官", "「不,不亏是鲁基乌斯卿,相当优秀的想法」", "aiy440000120.ogg"], //神官12
["圣女", "「是吗。辛苦你了」", "aiy030000130.ogg"], //圣女13
["随从", "「圣女大人······」", "aiy130000020.ogg"], //侍从02
["鲁基乌斯", "「······」", "aiy320000135.ogg"], //男主他哥13
["鲁基乌斯", "「那么,属下就回岗位去了」", "aiy320000140.ogg"], //男主他哥14
];
this.chapter03 = [
["", "从羽化病的少年纷乱的足音中,可以听得出相当的疲劳。"],
["", "显然,他并没有想到我会捷足先登吧。"],
["", "少年惶恐地回头看了一眼后,微微露出安心的表情,双手拄在膝盖上。。"],
["凯伊姆", "「辛苦你了」", "aiy310000370.ogg"], //男主37
["羽化病患少年", "「稀!?」", "aiy750000010.ogg"],
["凯伊姆", "「逃到贫民区是个不错的想法」", "aiy310000380.ogg"], //男主38
["羽化病患少年", "「你,你是,羽狩吗?」", "aiy750000020.ogg"],
["凯伊姆", "「不是」", "aiy310000390.ogg"], //男主39
[
"羽化病患少年",
"「什,什么啊······混蛋,不要吓我啊」",
"aiy750000030.ogg",
],
["凯伊姆", "「我对令你受惊这件事致以歉意」", "aiy310000400.ogg"], //男主40
["凯伊姆", "「作为回报,麻烦你把从店里偷的钱交出来吧」", "aiy310000410.ogg"], //男主41
["羽化病患少年", "「钱?你在说什么」", "aiy750000040.ogg"],
["凯伊姆", "「你要找的腰上的东西,掉在你身后了」", "aiy310000420.ogg"], //男主42
["羽化病患少年", "「哎?」", "aiy750000050.ogg"],
["羽化病患少年", "「呃呀」", "aiy750000060.ogg"],
["羽化病患少年", "「你······你这混蛋」", "aiy750000070.ogg"],
["凯伊姆", "「······」", "aiy310000430.ogg"], //男主43
["凯伊姆", "「把偷的钱交出来」", "aiy310000440.ogg"], //男主44
["羽化病患少年", "「我不知道你在······咕」", "aiy750000080.ogg"],
[
"羽化病患少年",
"「你,你说是我偷的······有什么证据吗」",
"aiy750000090.ogg",
],
["凯伊姆", "「你还挺倔的啊」", "aiy310000450.ogg"], //男主45
["凯伊姆", "「不过,给我听好了」", "aiy310000460.ogg"], //男主46
["凯伊姆", "「你偷的那些钱,是要上缴给《不蚀金锁》的上纳金」", "aiy310000470.ogg"], //男主47
["凯伊姆", "「而且,钱的主人是从前和吉克颇有渊源的女人」", "aiy310000480.ogg"], //男主48
["羽化病患少年", "「吉克?」", "aiy750000100.ogg"],
["凯伊姆", "「他是《不蚀金锁》的主人,这么说你就明白了吧」", "aiy310000490.ogg"], //男主49
["羽化病患少年", "「哎?哎?怎么会······」", "aiy750000110.ogg"],
["凯伊姆", "「再问你一遍,钱在哪里?」", "aiy310000510.ogg"], //男主51
["羽化病患少年", "「是,是,是,在我的怀里」", "aiy750000120.ogg"],
["凯伊姆", "「你没有擅自拿掉一部分吧」", "aiy310000520.ogg"], //男主52
["羽化病患少年", "「是,是的」", "aiy7500000130.ogg"],
[
"羽化病患少年",
"「那,那个,您是《不蚀金锁》的人吗?」",
"aiy750000140.ogg",
],
["凯伊姆", "「算是吧」", "aiy310000530.ogg"], //男主53
[
"羽化病患少年",
"「我什么都可以做,请您一定要帮帮我」",
"aiy750000150.ogg",
],
["凯伊姆", "「抱歉,我并没有兴趣去帮助他人」", "aiy310000540.ogg"], //男主54
[
"羽化病患少年",
"「我什么······什么,都会做的······」",
"aiy750000160.ogg",
],
["羽化病患少年", "「我一直都是生活在下层的」", "aiy750000170.ogg"],
[
"羽化病患少年",
"「可是,不知何时染上了羽化病······背后长出了翅膀······」",
"aiy750000180.ogg",
],
[
"羽化病患少年",
"「被寄宿工作的店赶了出来,只得流落到牢狱这里」",
"aiy750000190.ogg",
],
[
"羽化病患少年",
"「因为独自实在是饿的不行了,所以才会偷这些钱的」",
"aiy750000200.ogg",
],
[
"羽化病患少年",
"「我明明没有做任何坏事······为什么······会遇到这种事······」",
"aiy750000210.ogg",
],
["凯伊姆", "「谁知道」", "aiy310000550.ogg"], //男主55
[
"羽化病患少年",
"「呜······呜呜······接下来,要对我做什么?」",
"aiy750000220.ogg",
],
["凯伊姆", "「我要把你带到组织那里」", "aiy310000560.ogg"], //男主56
["羽化病患少年", "「怎,怎么这样」", "aiy750000230.ogg"],
["凯伊姆", "「不过,那样做的前提是你不是羽化病人」", "aiy310000570.ogg"], //男主57
["凯伊姆", "「组织也没有笨到把羽化病人招待到家里的程度」", "aiy310000580.ogg"], //男主58
["羽化病患少年", "「那么,是要放我逃走吗?」", "aiy750000240.ogg"],
["凯伊姆", "「我要让你学到教训」", "aiy310000590.ogg"], //男主59
["凯伊姆", "「如果换做是组织的制裁,至少要有断条胳膊的觉悟」", "aiy310000600.ogg"], //男主60
["凯伊姆", "「你的运气不错」", "aiy310000610.ogg"], //男主61
["羽化病患少年", "「唔······啊,是的······」", "aiy750000250.ogg"],
["凯伊姆", "「滚」", "aiy310000620.ogg"], //男主62
["羽化病患少年", "「非常感谢」", "aiy750000260.ogg"],
["羽化病患少年", "「唔啊!?」", "aiy750000270.ogg"],
["男", "「到这里就结束了,羽化病人」", "aiy430000010.ogg"], //兰格01
["男", "「确认他的翅膀」", "aiy430000020.ogg"], //兰格02
["", "趁还没有被卷入麻烦的事情之前,赶快离开这里吧"],
["羽狩的指挥者", "「那边的那个人」", "aiy430000030.ogg"], //兰格03
["凯伊姆", "「······有什么事?」", "aiy310000630.ogg"], //男主63
["羽狩的指挥者", "「可以稍微让我问几句话吗」", "aiy430000040.ogg"], //兰格04
["凯伊姆", "「······」", "aiy310000640.ogg"], //男主64
["凯伊姆", "「啊啊,无妨」", "aiy310000650.ogg"], //男主65
["羽狩的指挥者", "「感谢您的合作」", "aiy430000050.ogg"], //兰格05
["", "队长殷勤地致以谢礼。"],
["", "而在他的眼前,少年的衣服已经被他的补下们扯破。"],
["", "在瘦骨嶙峋的裸露后背上,长有纯白的羽翼。"],
["红发的羽狩", "「副队长,确认翅膀的持有了」"],
["羽狩的副队长", "「保护他」", "aiy430000060.ogg"], //兰格06
["羽化病患少年", "「不要······请原谅,我······」", "aiy750000280.ogg"],
[
"羽狩的副队长",
"「我们只是要带你去治愈院治疗羽化病,不是什么应该感到害怕的事情」",
"aiy430000070.ogg",
], //兰格07
["羽化病患少年", "「可是,可是」", "aiy750000290.ogg"],
["羽狩的副队长", "「没关系的」", "aiy430000080.ogg"], //兰格08
["羽化病患少年", "「······哥,哥哥」", "aiy750000300.ogg"],
["羽狩的副队长", "「你是羽化病人的亲属吗?」", "aiy430000090.ogg"], //兰格09
["凯伊姆", "「只是路人而已」", "aiy310000660.ogg"], //男主66
["凯伊姆", "「顺带一提,我也没有打算找你们的麻烦」", "aiy310000670.ogg"], //男主67
[
"羽狩的副队长",
"「前几天,有个和你说了同样的话的人,在我们背向他的瞬间对我们发动了袭击」",
"aiy430000100.ogg",
], //兰格10
["羽狩的副队长", "「我的一个部下就此永久失去了半截胳膊」", "aiy430000110.ogg"], //兰格11
["凯伊姆", "「我表示同情」", "aiy310000680.ogg"], //男主68
["凯伊姆", "「我马上就会消失的,这样就没问题了吧?」", "aiy310000690.ogg"], //男主69
["羽狩的副队长", "「嘛,不要这么慌张」", "aiy430000120.ogg"], //兰格12
["", "副队长看着羽化的少年。"],
["羽狩的副队长", "「你与这个人是什么关系?没有被他殴打吗?」", "aiy430000130.ogg"], //兰格13
["羽化病患少年", "「没,没有」", "aiy750000310.ogg"],
[
"羽狩的副队长",
"「如何对我们保持合作,你就可以在治愈院得到优先的治疗」",
"aiy430000140.ogg",
], //兰格14
["羽化病患少年", "「······」", "aiy750000320.ogg"],
[
"羽化病患少年",
"「那个人,是《不蚀金锁》的组织成员······」",
"aiy750000330.ogg",
],
[
"羽化病患少年",
"「突然说让我拿出钱来,我刚一拒绝他就打我」",
"aiy750000340.ogg",
],
["羽狩的副队长", "「原来如此······」", "aiy430000150.ogg"], //兰格15
[
"羽狩的副队长",
"「那位少年说你是《不蚀金锁》的一员,不知此事是否属实?」",
"aiy430000160.ogg",
], //兰格16
["凯伊姆", "「当然不是」", "aiy310000700.ogg"], //男主70
["凯伊姆", "「我只是从那里接受工作而已,并不是他们的成员」", "aiy310000710.ogg"], //男主71
["羽狩的副队长", "「你的意思是说,少年在说谎吗?」", "aiy430000170.ogg"], //兰格17
["凯伊姆", "「啊啊」", "aiy310000720.ogg"], //男主72
[
"凯伊姆",
"「如果你们和组织有关系的话,只要问问我是不是那里的成员就能明白事实了吧」",
"aiy310000730.ogg",
], //男主73
["羽狩的副队长", "「就算我去询问,也无法从他们那里得到事实」", "aiy430000180.ogg"], //兰格18
[
"羽狩的副队长",
"「《不蚀金锁》的那些人一向都不对我们合作,我对此深感困扰」",
"aiy430000190.ogg",
], //兰格19
["凯伊姆", "「真是辛苦啊」", "aiy310000740.ogg"], //男主74
["羽狩的副队长", "「说的是啊」", "aiy430000200.ogg"], //兰格20
["羽狩的副队长", "「其实,砍下我部下胳膊的似乎也是组织的成员呢」", "aiy430000210.ogg"], //兰格21
[
"羽狩的副队长",
"「无需如此警戒,我只是想在看守所向你咨询一些事情而已」",
"aiy430000220.ogg",
], //兰格22
[
"羽狩的副队长",
"「如果能够知晓牢狱与组织的事情,我们也可以尽可能地对更多的羽化病人进行保护」",
"aiy430000230.ogg",
], //兰格23
["羽狩的副队长", "「那和整条街道的和平也是紧密相关的吧?」", "aiy430000240.ogg"], //兰格24
["凯伊姆", "「我知道,你们有逮捕干扰狩猎羽化病人的权力」", "aiy310000750.ogg"], //男主75
[
"凯伊姆",
"「但是,我没有对你们做出任何干扰,为什么要对我如此纠缠不休呢」",
"aiy310000760.ogg",
], //男主76
["羽狩的副队长", "「那些话,我们会在看守所对你详细说明的」", "aiy430000250.ogg"], //兰格25
["凯伊姆", "「······」", "aiy310000770.ogg"], //男主77
["", "在这里起争执的话,就会被羽狩加害。"],
["", "就算能从这里脱身,今后只要碰面就会产生纠纷也是摆明的事情。"],
["", "就算逃跑,也没有好的结果。"],
["", "正当我想要打圆场的时候,刚才的气氛一瞬间产生了转变。"],
["", "发生了什么事······"],
["???", "「我认为,那位先生是正确的」", "aiy050000010.ogg"], //菲奥奈01
["", "羽狩们一起回头。"],
["", "而在他们视线的焦点处,"],
["", "伫立着一位女性。"],
["", "在端正的容颜下,代表着强烈意志的双眉十分显眼。"],
["", "身体的柔软与紧紧包裹在其身上的羽狩制服,两者显得十分的不搭配。"],
["", "我还是第一次看到女性的羽狩。"],
["羽狩的副队长", "「队长,这是获得《不蚀金锁》情报的好机会」", "aiy430000260.ogg"], //兰格26
[
"羽狩的队长",
"「兰格副队长,就算是为了获得情报,也不能做出恫吓的发言啊」",
"aiy050000020.ogg",
], //菲奥奈02
["兰格副队长", "「我并没有打算去恫吓他······」", "aiy430000270.ogg"], //兰格27
["羽狩的队长", "「告诉我那个被砍掉胳膊的队员的名字」", "aiy050000030.ogg"], //菲奥奈03
["羽狩的队长", "「我会去探望他的」", "aiy050000040.ogg"], //菲奥奈04
["兰格副队长", "「那个是······」", "aiy430000280.ogg"], //兰格28
["羽狩的队长", "「我知道,你一直在为有所收获而努力工作」", "aiy050000050.ogg"], //菲奥奈05
["羽狩的队长", "「但是,正因为我们的工作是为民众提供帮助」", "aiy050000060.ogg"], //菲奥奈06
["羽狩的队长", "「所以就更不能损害人与人之间的信赖」", "aiy050000070.ogg"], //菲奥奈07
["兰格副队长", "「我会铭记在心」", "aiy430000290.ogg"], //兰格29
["羽狩的队长", "「这位先生,我的部下失礼了」", "aiy050000080.ogg"], //菲奥奈08
["凯伊姆", "「只要不对我再来一次就好」", "aiy310000780.ogg"], //男主78
["羽狩的队长", "「请稍等」", "aiy050000090.ogg"], //菲奥奈09
["凯伊姆", "「有什么事?」", "aiy310000790.ogg"], //男主79
["羽狩的队长", "「我想确认一件事」", "aiy050000100.ogg"], //菲奥奈10
["羽狩的队长", "「你真的不是《不蚀金锁》的成员吗?」", "aiy050000110.ogg"], //菲奥奈11
["凯伊姆", "「真的」", "aiy310000800.ogg"], //男主80
["凯伊姆", "「如果我说是的话,你有什么打算?」", "aiy310000810.ogg"], //男主81
["羽狩的队长", "「我听过传闻,说他们是用依靠暴力而得的钱在生活」", "aiy050000120.ogg"], //菲奥奈12
["凯伊姆", "「······这样啊」", "aiy310000820.ogg"], //男主82
["凯伊姆", "「如果能有收获就好了啊」", "aiy310000830.ogg"], //男主83
];
this.chapter04 = [
["梅尔特", "「欢迎光临」", "aiy120000020.ogg"], //老板娘01文件序号是以2开始后续全部加1
["梅尔特", "「辛苦了」", "aiy120000030.ogg"], //老板娘02
["梅尔特", "「抱歉,又拜托给你了个这么麻烦的工作」", "aiy120000040.ogg"], //老板娘03
["凯伊姆", "「没什么,比想象中完成的更容易」", "aiy310000840.ogg"], //男主84
["梅尔特", "「那就好」", "aiy120000050.ogg"], //老板娘04
["梅尔特", "「这是我的一点谢意」", "aiy120000060.ogg"], //老板娘05
["凯伊姆", "「味道有些变化啊」", "aiy310000850.ogg"], //男主85
["梅尔特", "「啊,被发现了?」", "aiy120000070.ogg"], //老板娘06
["梅尔特", "「最近,没能到手什么好的原料呢」", "aiy120000080.ogg"], //老板娘07
["凯伊姆", "「去拜托吉克如何?」", "aiy310000860.ogg"], //男主86
[
"梅尔特",
"「话是这么说,但是总不能用店里采购的这种小事去麻烦他吧······」",
"aiy120000090.ogg"
], //老板娘08
["凯伊姆", "「那希望你也不要来麻烦我」", "aiy310000870.ogg"], //男主87
["梅尔特", "「那 是 两 码 事」", "aiy120000100.ogg"], //老板娘09
["梅尔特", "「再说,凯伊姆是靠着工作来生活的吧」", "aiy120000110.ogg"], //老板娘10
[
"梅尔特",
"「而且,自己的钱被偷了这么害羞的事,向凯伊姆以外的其他人都说不出口」",
"aiy120000120.ogg"
], //老板娘11
["凯伊姆", "「反正,也已经传到吉克的耳朵里了」", "aiy310000880.ogg"], //男主88
["梅尔特", "「这是面子问题啊,面子问题」", "aiy120000130.ogg"], //老板娘12
["凯伊姆", "「嘛,算了」", "aiy310000890.ogg"], //男主89
["凯伊姆", "「这样就好了吧?」", "aiy310000900.ogg"], //男主90
["梅尔特", "「这是钱包呢」", "aiy120000140.ogg"], //老板娘13
["梅尔特", "「嗯,东西没少」", "aiy120000150.ogg"], //老板娘14
["梅尔特", "「太好啦—这个月的上纳金,我可全部都放在里面了呢」", "aiy120000160.ogg"], //老板娘15
["梅尔特", "「如果没有找到的话,说不定就又会被送到娼馆里了呢」", "aiy120000170.ogg"], //老板娘16
["凯伊姆", "「在那边不是来钱更快吗?」", "aiy310000910.ogg"], //男主91
["梅尔特", "「阿拉,你是在说我还能有魅力吗?」", "aiy120000180.ogg"], //老板娘17
["凯伊姆", "「这是客套话而已」", "aiy310000920.ogg"], //男主92
["梅尔特", "「欺负人」", "aiy120000190.ogg"], //老板娘18
["梅尔特", "「总而言之,今天帮大忙了」", "aiy120000200.ogg"], //老板娘19
["梅尔特", "「谢礼嘛······」", "aiy120000210.ogg"], //老板娘20
["凯伊姆", "「就记在账单上吧」", "aiy310000930.ogg"], //男主93
["梅尔特", "「了解—盛谢惠顾了哦?」", "aiy120000220.ogg"], //老板娘21
["", "喀啷喀啷"],
["", "门铃响起"],
["", "喧哗瞬间安静下来。"],
["", "进来的人是吉克。"],
["", "是掌控着牢狱的组织之一,《不蚀金锁》的头目。"],
["", "不仅组织的成员,就连店内一般的客人也对他以注目礼表示敬意。"],
["吉克", "「各位继续吧」", "aiy340000010.ogg"], //吉克01
["", "仿佛停滞的时钟重新转动了一般,店内恢复了热闹的气氛。"],
["吉克", "「抱歉,今天拜托你去做了无聊的工作」。", "aiy340000020.ogg"], //吉克02
["凯伊姆", "「不用介意」", "aiy310000940.ogg"], //男主94
["", "吉克轻轻点了点头,在我右边坐了下来"],
["凯伊姆", "「逃跑的男人怎么样了?」", "aiy310000950.ogg"], //男主95
["吉克", "「嗯?已经不在这个世上了」。", "aiy340000030.ogg"], //吉克03
["吉克", "「有什么想要知道的事吗?」", "aiy340000040.ogg"], //吉克04
["凯伊姆", "「不,没什么」", "aiy310000960.ogg"], //男主96
["吉克", "「那个无聊的家伙,完全没有趣味呢」", "aiy340000050.ogg"], //吉克05
["吉克", "「真希望他也替我负责清扫的部下也考虑考虑」", "aiy340000060.ogg"], //吉克06
["凯伊姆", "「真是灾难啊」", "aiy310000970.ogg"], //男主97
["吉克", "「比起那个,我听说了哦。你去追羽化病人了啊」", "aiy340000070.ogg"], //吉克07
["凯伊姆", "「消息真灵通」", "aiy310000980.ogg"], //男主98
["吉克", "「梅尔特也注意点」", "aiy340000080.ogg"], //吉克08
["吉克", "「你丢钱已经不是一回两回了」", "aiy340000090.ogg"], //吉克09
["梅尔特", "「好的—我会注意的。」", "aiy120000230.ogg"], //老板娘22
["梅尔特", "「吉克还是平常的点单吧」", "aiy120000240.ogg"], //老板娘23
["梅尔特", "「凯伊姆要再来一杯吗?」", "aiy120000250.ogg"], //老板娘24
["", "我们用眼神点头示意后,梅尔特开始准备起酒来。"],
["", "悠然地吐出眼圈后,吉克取出一个纸包放在柜台上。"],
["吉克", "「这是抓捕逃跑男人的报酬」", "aiy340000100.ogg"], //吉克10
["凯伊姆", "「下次有什么事再告诉我」", "aiy310000990.ogg"], //男主99
["梅尔特", "「来,久等了」", "aiy120000260.ogg"], //老板娘25
["凯伊姆", "「话说回来梅尔特,为什么会被那种孩子偷到钱?」", "aiy310001000.ogg"], //男主100
["吉克", "「让我猜猜看」", "aiy340000110.ogg"], //吉克11
["吉克", "「是那个吧,看某个特立独行的男人入迷了,所以就有了空隙?」", "aiy340000120.ogg"], //吉克12
["梅尔特", "「可惜—」", "aiy120000270.ogg"], //老板娘26
["梅尔特", "「事实恰恰相反,是那家伙一直在纠缠我」", "aiy120000280.ogg"], //老板娘27
["凯伊姆", "「完全把你当成新进的女佣了么」", "aiy310001010.ogg"], //男主101
["梅尔特", "「我从前可是很有名的,不会被当成这种下人吧」", "aiy120000290.ogg"], //老板娘28
["梅尔特", "「······而且,我没法对对我这么钟情的人发火啊」", "aiy120000300.ogg"], //老板娘29
["凯伊姆&吉克", "「你傻啊」", "aiy310001027.ogg"], //男主102.7吉克13.5
["梅尔特", "「异口同声呢,不亏是兄弟」", "aiy120000310.ogg"], //老板娘30
["凯伊姆", "「别用这种称呼,怪恶心的」", "aiy310001030.ogg"], //男主103
["吉克", "「说得没错」", "aiy340000140.ogg"], //吉克14
["吉克", "「······说起来······」", "aiy340000150.ogg"], //吉克15
["梅尔特", "「怎么了?」", "aiy120000320.ogg"], //老板娘31
["吉克", "「有件事我一直很在意,我和凯伊姆,哪个是哥哥啊?」", "aiy340000160.ogg"], //吉克16
["凯伊姆", "「你也说这么无聊的话题」", "aiy310001040.ogg"], //男主104
["吉克", "「不,这是很重要的事情」", "aiy340000170.ogg"], //吉克17
["吉克", "「梅尔特,事实是怎么样的?」", "aiy340000180.ogg"], //吉克18
["梅尔特", "「啊~是怎么样的呢~」", "aiy120000330.ogg"], //老板娘32
["梅尔特", "「我忘记了」", "aiy120000340.ogg"], //老板娘33
["吉克", "「骗人」", "aiy340000190.ogg"], //吉克19
["梅尔特", "「我说真的」", "aiy120000350.ogg"], //老板娘34
["梅尔特", "「嘛,如果想起来了的话,就算是卸载艺术上我也会公诸于众的」", "aiy120000360.ogg"], //老板娘35
["吉克", "「喔唷」", "aiy340000200.ogg"], //吉克20
["吉克", "「那么,我就不能比你先死了啊」", "aiy340000210.ogg"], //吉克21
["梅尔特", "「蒙你费心」", "aiy120000370.ogg"], //老板娘36
["梅尔特", "「顺带一提,有传言说吃过我们这里的料理后可以长生不老哦?」", "aiy120000380.ogg"], //老板娘37
["吉克", "「好,来两份炖菜,记得加腊肠」", "aiy340000220.ogg"], //吉克22
["梅尔特", "「多谢惠顾」", "aiy120000390.ogg"], //老板娘38
["", "微微一笑后,梅尔特去厨房传达点单。"],
["凯伊姆", "「吉克······」", "aiy310001050.ogg"], //男主105
["吉克", "「啊,不好了」", "aiy340000230.ogg"], //吉克23
["凯伊姆", "「话题扯远了」", "aiy310001060.ogg"], //男主106
["梅尔特", "「什么话题来着?」", "aiy120000400.ogg"], //老板娘39
["凯伊姆", "「关于为什么你的钱会被偷这件事」", "aiy310001070.ogg"], //男主107
["凯伊姆", "「丢钱的时候以你来说,应该不会全无察觉吧?」", "aiy310001080.ogg"], //男主108
["梅尔特", "「算是吧,被偷的时候确实也想过要抓住他」", "aiy120000410.ogg"], //老板娘40
["梅尔特", "「但是,我注意到了那个孩子背后的鼓起呢」", "aiy120000420.ogg"], //老板娘41
["凯伊姆", "「所以就不由自主地放他逃跑了?」", "aiy310001090.ogg"], //男主109
["吉克", "「就算你想羽化病人施恩,也不会得到任何回报哦」", "aiy340000240.ogg"], //吉克24
["梅尔特", "「我知道」", "aiy120000430.ogg"], //老板娘42
["梅尔特", "「正因为知道,所以之后才会拜托凯伊姆去将钱取回的」", "aiy120000440.ogg"], //老板娘43
["梅尔特", "「可是,呢······」", "aiy120000450.ogg"], //老板娘44
["梅尔特", "「果然还是很矛盾呢」", "aiy120000460.ogg"], //老板娘45
["梅尔特", "「明明是自己放他逃跑的,之后又拜托别人去抓他」", "aiy120000470.ogg"], //老板娘46
["梅尔特", "「但是,在那一刹那······应该说是,突然露出了真心吧」", "aiy120000480.ogg"], //老板娘47
["梅尔特", "「真的,只是自我满足而已」", "aiy120000490.ogg"], //老板娘48
["凯伊姆", "「对于那个孩子来说不是很幸运么」", "aiy310001100.ogg"], //男主110
["凯伊姆", "「在被《不蚀金锁》抓到之前,就被羽狩保护了」", "aiy310001110.ogg"], //男主111
["凯伊姆", "「现在应该已经躺在治愈院的床上了」", "aiy310001120.ogg"], //男主112
["吉克", "「如果被我们抓到的话,嘛,至少也会断掉一根胳膊吧」", "aiy340000250.ogg"], //吉克25
["凯伊姆", "「比起失去胳膊,这不是个很好地结局吗」", "aiy310001130.ogg"], //男主113
["吉克", "「多亏梅尔特的一念之善,那个小子的胳膊被救下来了」", "aiy340000260.ogg"], //吉克26
["吉克", "「对我来说,不能去管教他稍微有点可惜就是了」", "aiy340000270.ogg"], //吉克27
["凯伊姆", "「我姑且是给了他两三拳」", "aiy310001140.ogg"], //男主114
["吉克", "「你这不是很善解人意么」", "aiy340000280.ogg"], //吉克28
["梅尔特", "「没有帮他的忙啊」", "aiy120000500.ogg"], //老板娘49
["凯伊姆", "「你给我用常识考虑考虑」", "aiy310001150.ogg"], //男主115
["梅尔特", "「本来,就算羽化病人不被羽狩带走而导致羽化病扩散,在牢狱里死亡的理由也要多少就有多少」", "aiy120000510.ogg"], //老板娘50
["梅尔特", "「没事到如今多一个病人,也不会有什么改变的吧」", "aiy120000520.ogg"], //老板娘51
["梅尔特", "「那么,那么些羽化病人为什么就不能和我们一起开心地生活呢」", "aiy120000530.ogg"], //老板娘52
["吉克", "「像梅尔特这样思考的人少之又少」", "aiy340000290.ogg"], //吉克29
["凯伊姆", "「就算早晚都会死去,为了今日的苟活而努力拼搏也是人之常情」", "aiy310001160.ogg"], //男主116
["梅尔特", "「我知道」", "aiy120000540.ogg"], //老板娘53
["梅尔特", "「别介意,我刚才只是顺口说说」", "aiy120000550.ogg"], //老板娘54
["", "喀啷喀啷"],
["梅尔特", "「辛苦了,艾莉斯」", "aiy120000560.ogg"], //老板娘55
["艾莉斯", "「嗯」", "aiy020000190.ogg"], //医生19
["", "用不可爱的声音随口回应后,艾莉斯理所当然般地在我左边的座位坐下"],
["", "梅尔特什么都没有问,就开始准备起茶来"],
["梅尔特", "「多亏你去帮忙叫凯伊姆了呢」", "aiy120000570.ogg"], //老板娘56
["艾莉斯", "「不客气」", "aiy020000200.ogg"], //医生20
["艾莉斯", "「我从店里出去以后不久就看到他了,这是我们被命运之绳紧紧相连的证明呢」", "aiy020000210.ogg"], //医生21
["凯伊姆", "「那还真是糟糕」", "aiy310001170.ogg"], //男主117
["艾莉斯", "「用不着这么害羞的」", "aiy020000220.ogg"], //医生22
["凯伊姆", "「用茶堵上你的嘴吧」", "aiy310001180.ogg"], //男主118
["艾莉斯", "「好」", "aiy020000230.ogg"], //医生23
["梅尔特", "「司空见惯的风景呢」", "aiy120000580.ogg"], //老板娘57
["凯伊姆", "「你在那之后去做什么了?」", "aiy310001190.ogg"], //男主119
["艾莉斯", "「去莉莉乌姆照顾患病的女孩子了」", "aiy020000240.ogg"], //医生24
["艾莉斯", "「好像是毒品上瘾,费了很大劲才止住她的胡闹」", "aiy020000250.ogg"], //医生25
["艾莉斯", "「希望你能好好管理下自己店里的娼妇呢」", "aiy020000260.ogg"], //医生26
["吉克", "「抱歉啊」", "aiy340000300.ogg"], //吉克30
["吉克", "「为表歉意,就让我来给艾莉斯诊疗一下吧」", "aiy340000310.ogg"], //吉克31
["艾莉斯", "「去死吧」", "aiy020000270.ogg"], //医生27
["梅尔特", "「是什么药?」", "aiy120000590.ogg"], //老板娘58
["吉克", "「是最近上市的一种药」", "aiy340000320.ogg"], //吉克32
["吉克", "「虽然中毒的可能性很低,但只要中毒便会一发而不可收拾」", "aiy340000330.ogg"], //吉克33
["艾莉斯", "「知道了」", "aiy020000280.ogg"], //医生28
["吉克", "「虽然我也许没有时间去管教她,不过我还是会注意的」", "aiy340000340.ogg"], //吉克34
["艾莉斯", "「毒药的主人是吉克吗?」", "aiy020000290.ogg"], //医生29
["吉克", "「《不蚀金锁》不卖药,这是从先代定下的规矩」", "aiy340000350.ogg"], //吉克35
["艾莉斯", "「抱歉,开玩笑的」", "aiy020000300.ogg"], //医生30
["吉克", "「关于药的出处,这边也会调查的」", "aiy340000360.ogg"], //吉克36
["吉克", "「如果有什么传闻的话记得告诉我」", "aiy340000370.ogg"], //吉克37
["艾莉斯", "「知道了」", "aiy020000310.ogg"], //医生31
["艾莉斯", "「麻药中毒的人真麻烦呢,又不能完全治好」", "aiy020000320.ogg"], //医生32
["艾莉斯", "「既然要悄悄的打,那就也悄悄地去死不就好了」", "aiy020000330.ogg"], //医生33
["", "喀啷喀啷"],
["奥兹", "「吉克大人,抱歉打扰你开心的时光」", "aiy350000240.ogg"], //金锁高官24
["吉克", "「怎么了」", "aiy340000380.ogg"], //吉克38
["奥兹", "「请将耳朵凑过来」", "aiy350000250.ogg"], //金锁高官25
["吉克", "「······知道了」", "aiy340000390.ogg"], //吉克39
["吉克", "「凯伊姆,一会到娼馆来一趟」", "aiy340000400.ogg"], //吉克40
["凯伊姆", "「啊啊」", "aiy310001200.ogg"], //男主120
["吉克", "「噢,我都给忘了」", "aiy340000410.ogg"], //吉克41
["吉克", "「让这里的大家都开心一下」", "aiy340000420.ogg"], //吉克42
["梅尔特", "「收到」", "aiy120000600.ogg"], //老板娘59
["吉克", "「我走了」", "aiy340000430.ogg"], //吉克43
["奥兹", "「好」", "aiy350000260.ogg"], //金锁高官26
["", "梆梆"],
["梅尔特", "「大家静静,有个好消息」", "aiy120000610.ogg"], //老板娘60
["梅尔特", "「今天有个『大人物』要请客哦」", "aiy120000620.ogg"], //老板娘61
["艾莉斯", "「吉克还是一如既往的大方」", "aiy020000340.ogg"], //医生34
["梅尔特", "「果然,头目就应该像这样呢」", "aiy120000630.ogg"], //老板娘62
["凯伊姆", "「他的做法是继承先代的」", "aiy310001210.ogg"], //男主121
["梅尔特", "「又来了又来了」", "aiy120000640.ogg"], //老板娘63
["梅尔特", "「他想要赶上先代还早了十年呢」", "aiy120000650.ogg"], //老板娘64
["凯伊姆", "「有些太严厉了吧」", "aiy310001220.ogg"], //男主122
["艾莉斯", "「说起来,吉克有什么事」", "aiy020000350.ogg"], //医生35
["梅尔特", "「既然要叫上凯伊姆,再怎么说也不是什么小事吧?」", "aiy120000670.ogg"], //老板娘66
["凯伊姆", "「说的也是」", "aiy310001230.ogg"], //男主123
["艾莉斯", "「我期待看到你光荣负伤的样子」", "aiy020000360.ogg"], //医生36
["凯伊姆", "「不要为人的不幸祈求啊」", "aiy310001240.ogg"], //男主124
["梅尔特", "「来,喝完这杯就去加油工作吧」", "aiy120000680.ogg"], //老板娘67
["凯伊姆", "「啊啊」", "aiy310001250.ogg"], //男主125
["凯伊姆", "「我走了」", "aiy310001260.ogg"], //男主126
["艾莉斯", "「加油(受伤)吧」", "aiy020000370.ogg"], //医生37
["梅尔特", "「一路走好」", "aiy120000690.ogg"], //老板娘68
];
this.chapter05 = [
["", "今晚的娼馆街比平常来得更加热闹"],
["", "视线中可以看到很多生客。"],
["莉莎", "「呀——!」", "aiy150000010.ogg"], //三小只C01
["莉莎", "「呐,凯伊姆凯伊姆凯伊姆」", "aiy150000020.ogg"], //三小只C02
["莉莎", "「我难道没有魅力吗?」", "aiy150000030.ogg"], //三小只C03
["凯伊姆", "「突然之间怎么了」", "aiy310001270.ogg"], //男主127
["莉莎", "「因为因为因为,明明这么热闹,却只有我没有接到客人」", "aiy150000040.ogg"], //三小只C04
["莉莎", "「亏我还对他们说,我会用超绝的技巧让他们舒服得如同化作夜明的流星般呢」", "aiy150000050.ogg"], //三小只C05
["凯伊姆", "「那个台词太糟糕了」", "aiy310001280.ogg"], //男主128
["莉莎", "「哎—什么啊,这可是我昨天花了一晚上考虑出来的呢」", "aiy150000060.ogg"], //三小只C06
["凯伊姆", "「去睡觉,不要用你那空空如也的脑袋去考虑这种无聊事」", "aiy310001290.ogg"], //男主129
["莉莎", "「好过分!?」", "aiy150000070.ogg"], //三小只C07
["凯伊姆", "「你看看周围,今天有很多新客人吧?」", "aiy310001300.ogg"], //男主130
["莉莎", "「嗯~~」", "aiy150000080.ogg"], //三小只C08
["莉莎", "「啊,你这么一说还真是」", "aiy150000090.ogg"], //三小只C09
["凯伊姆", "「他们心里都很紧张,不可能会被你这种强拉的手法钓上钩吧」", "aiy310001310.ogg"], //男主131
["莉莎", "「这样啊,是不想欲仙欲死吗?」", "aiy150000100.ogg"], //三小只C10
["凯伊姆", "「不,没有男人不会希望那样的」", "aiy310001320.ogg"], //男主132
["凯伊姆", "「不过,强来是不行的。去让他们被温柔地包容着化作流星吧」", "aiy310001330.ogg"], //男主133
["莉莎", "「原—来如此」", "aiy150000110.ogg"], //三小只C11
["莉莎", "「凯伊姆肯定很有娼妇的才能哦」", "aiy150000120.ogg"], //三小只C12
["凯伊姆", "「没有」", "aiy310001340.ogg"], //男主134
["凯伊姆", "「好了,你赶紧去赚钱吧」", "aiy310001350.ogg"], //男主135
["莉莎", "「收到—」", "aiy150000130.ogg"], //三小只C13
["", "莉莎精神满满地回答后,就啪嗒啪嗒地跑开了。"],
["莉莎", "「呐—那边的大哥哥,和我一起化作星光吧—」", "aiy150000140.ogg"], //三小只C14
["", "这家伙"],
["", "非要用这么麻烦的说法么。"],
["", "咚"],
["莉莎", "「呀」", "aiy150000150.ogg"], //三小只C15
["大胡子醉汉", "「好疼」", "aiy800000010.ogg"],
["", "突然,莉莎与一个男人相撞。"],
["", "虽然男人可以摆出派头,但从那虚浮的脚步可以看出他相当的醉意。"],
["莉莎", "「好疼疼,对不起」", "aiy150000160.ogg"], //三小只C16
["大胡子醉汉", "「这不是道歉就能了事的事吧,大姐—」", "aiy800000020.ogg"],
["大胡子醉汉", "「哟,穿得相当清凉啊,喂」", "aiy800000030.ogg"],
["大肚子的醉汉", "「喂—怎么了?」", "aiy810000010.ogg"],
["大胡子醉汉", "「这位小姐特意往我身上撞呢」", "aiy800000040.ogg"],
["大肚子的醉汉", "「喂喂喂喂,你是要做什么」", "aiy810000020.ogg"],
["莉莎", "「呜哎—所以我都说对不起了啊」", "aiy150000170.ogg"], //三小只C17
["莉莎", "「对,对了。作为冲撞的补偿,我会给您提供很好的服务的,怎么样?」", "aiy150000180.ogg"], //三小只C18
["大胡子醉汉", "「是免费的吧?」", "aiy800000050.ogg"],
["莉莎", "「那个······这,这姑且也是工作······免费就太」", "aiy150000190.ogg"], //三小只C19
["大肚子的醉汉", "「真是的,娼妇就是这种人。钱,钱,钱,完全没有做人的诚意」", "aiy810000030.ogg"],
["大胡子醉汉", "「好—你这家伙。我给你钱,你现在就在这里给我服务」", "aiy800000060.ogg"],
["莉莎", "「这,这该怎么说呢,稍微有些过了吧,在这里做很害羞的」", "aiy150000200.ogg"], //三小只C20
["大胡子醉汉", "「你除了晃腰以外还有用得着脑子的地方吗?别说这种像个人说的话啊」", "aiy800000070.ogg"],
["大肚子的醉汉", "「啊啊?这些钱不够吗?」", "aiy810000040.ogg"],
["大胡子醉汉", "「来赶快把脚张开让我O你们最喜欢这种事了吧」", "aiy800000080.ogg"],
["莉莎", "「是,是认真的吗······这些人」", "aiy150000210.ogg"], //三小只C21
["大肚子的醉汉", "「赶快做」", "aiy810000050.ogg"],
["莉莎", "「呃······」", "aiy150000220.ogg"], //三小只C22
["凯伊姆", "「喂」", "aiy310001360.ogg"], //男主136
["大肚子的醉汉", "「啊?」", "aiy810000060.ogg"],
["凯伊姆", "「即便是娼妇,也有不能出售的东西」", "aiy310001370.ogg"], //男主137
["", "喀"],
["", "我一拳击中男人被酒精染红的脸,"],
["大胡子醉汉", "「咦」", "aiy800000090.ogg"],
["", "嘎"],
["", "再一拳击中另一个人的腹部。"],
["大肚子的醉汉", "「咕······」", "aiy810000070.ogg"],
["大胡子醉汉", "「唔······」", "aiy800000100.ogg"],
["", "两人像两只毛虫般躺在地上蠕动着挣扎"],
["", "一时半会应该起不来了吧"],
["莉莎", "「凯伊姆先生,太迟了太迟了太迟了」", "aiy150000230.ogg"], //三小只C23
["凯伊姆", "「都是你闯出的祸吧」", "aiy310001380.ogg"], //男主138
["莉莎", "「话是,那么说,可是」", "aiy150000240.ogg"], //三小只C24
["凯伊姆", "「赶快回莉莉乌姆去」", "aiy310001390.ogg"], //男主139
["莉莎", "「唔,嗯。谢了,凯伊姆。再见」", "aiy150000250.ogg"], //三小只C25
["", "久留无用"],
];
this.chapter06 = [
["", "漂浮的紫烟与甜美香醇的空气。"],
];
},
"动画及周期装备映射": function () {
// 在此增加新插件
this.equip = {
//所有回合中的装备及速度
sword1: { id: "sword1", speed: 10 },
};
this.heroanimate = {
//勇士武器对应的动画(key为主手武器IDvalue为帧动画名)
sword1: "sword",
};
this.enemyanimate = {
//怪物对应的动画(key为怪物IDvalue为帧动画名)
bat: "sword",
};
this.equipanimate = {
//勇士周期性装备对应的动画(key为装备Idvalue为帧动画名)
shield0: "sword",
};
},
"勇士法抗乘算叠加": function () {
// 在此增加新插件
items.prototype.compareEquipment = function (
compareEquipId,
beComparedEquipId
) {
var result = { value: {}, percentage: {} };
var first = core.material.items[compareEquipId],
second = core.material.items[beComparedEquipId];
for (var one in result) {
for (var name in core.status.hero) {
if (name === "mdef" && one === "percentage") {
var ans = 1;
if (first?.equip?.[one]?.[name])
ans *= 1 - (first.equip[one][name] || 0) / 100;
if (second?.equip?.[one]?.[name])
ans /= 1 - (second.equip[one][name] || 0) / 100;
if (ans != 1) result[one][name] = ans;
} else if (name === "mdef" && one === "value") {
var ans = 0;
if (first) ans -= ((first.equip || {})[one] || {})[name] || 0;
if (second) ans += ((second.equip || {})[one] || {})[name] || 0;
if (ans != 0) result[one][name] = ans;
} else {
if (typeof core.status.hero[name] == "number") {
var ans = 0;
if (first) ans += ((first.equip || {})[one] || {})[name] || 0;
if (second) ans -= ((second.equip || {})[one] || {})[name] || 0;
if (ans != 0) result[one][name] = ans;
}
}
}
}
return result;
};
let a = 1;
items.prototype._loadEquipEffect = function (equipId, unloadEquipId) {
// 比较能力值
var result = core.compareEquipment(equipId, unloadEquipId);
for (var name in result.percentage) {
if (name === "mdef") {
a *= result.percentage[name];
core.setBuff(name, 1 - a);
} else {
core.addBuff(name, result.percentage[name] / 100);
}
}
for (var name in result.value)
core.status.hero[name] += result.value[name];
};
},
"攻速临界": function () {
// 在此增加新插件
//临界表
core.ui._drawBookDetail_turnAndCriticals = function (enemy, floorId, texts) {
// 临界表
var criticals = core.enemys.nextCriticals(enemy.id, 8, enemy.x, enemy.y, floorId).map(function (v) {
return core.formatBigNumber(v[0]) + ":" + core.formatBigNumber(v[1]);
});
while (criticals[0] == '0:0') criticals.shift();
texts.push("\r[#FF6A6A]\\d攻击临界表\\d\r[]" + JSON.stringify(criticals));
var criticals_spell = core.nextCriticals_spell(enemy.id, 8, enemy.x, enemy.y, floorId).map(function (v) {
return core.formatBigNumber(v[0]) + ":" + core.formatBigNumber(v[1]);
});
while (criticals_spell[0] == '0:0') criticals_spell.shift();
texts.push("\r[#FF6A6A]\\d法强临界表\\d\r[]" + JSON.stringify(criticals_spell));
var criticals_speed = core.nextCriticals_speed(enemy.id, 8, enemy.x, enemy.y, floorId).map(function (v) {
return core.formatBigNumber(v[0]) + ":" + core.formatBigNumber(v[1]);
});
while (criticals_speed[0] == '0:0') criticals_speed.shift();
texts.push("\r[#FF6A6A]\\d速度临界表\\d\r[]" + JSON.stringify(criticals_speed));
}
//攻速临界计算
core.nextCriticals_speed = function (enemy, number, x, y, floorId) {
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
number = number || 1;
var info = core.getDamageInfo(enemy, null, x, y, floorId);
if (info == null) { // 如果未破防...
return [
['?', '?']
];
}
if (typeof info == 'number') {
return [
[0, 0]
];
}
return core.enemys._nextSpeedCriticals_useBinarySearch(enemy, info, number, x, y, floorId);
}
enemys.prototype._nextSpeedCriticals_useBinarySearch = function (enemy, info, number, x, y, floorId) {
var mon_hp = info.mon_hp,
hero_speed = core.status.hero.speed,
mon_def = info.mon_def,
pre = info.damage;
var list = [];
var start_speed = hero_speed;
var calNext = function (currSpeed, maxSpeed) {
var start = Math.floor(currSpeed),
end = Math.floor(maxSpeed);
if (start > end) return null;
while (start < end) {
var mid = Math.floor((start + end) / 2);
if (mid - start > end - mid) mid--;
var nextInfo = core.enemys.getDamageInfo(enemy, { "speed": mid }, x, y, floorId);
if (nextInfo == null || (typeof nextInfo == 'number')) return null;
if (pre > nextInfo.damage) end = mid;
else start = mid + 1;
}
var nextInfo = core.enemys.getDamageInfo(enemy, { "speed": start }, x, y, floorId);
return nextInfo == null || (typeof nextInfo == 'number') || nextInfo.damage >= pre ? null : [start, nextInfo.damage];
}
var currSpeed = start_speed;
while (true) {
var next = calNext(currSpeed + 1, Number.MAX_SAFE_INTEGER, pre);
if (next == null) break;
currSpeed = next[0];
pre = next[1];
list.push([currSpeed - hero_speed, info.damage - pre]);
if (pre <= 0 && !core.flags.enableNegativeDamage) break;
if (list.length >= number) break;
}
if (list.length == 0) list.push([0, 0]);
return list;
}
//法强临界计算
core.nextCriticals_spell = function (enemy, number, x, y, floorId) {
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
number = number || 1;
var info = core.getDamageInfo(enemy, null, x, y, floorId);
if (info == null) { // 如果未破防...
return [
['?', '?']
];
}
if (typeof info == 'number') {
return [
[0, 0]
];
}
return core.enemys._nextSpellCriticals_useBinarySearch(enemy, info, number, x, y, floorId);
}
enemys.prototype._nextSpellCriticals_useBinarySearch = function (enemy, info, number, x, y, floorId) {
var mon_hp = info.mon_hp,
hero_spell = core.status.hero.spell,
mon_def = info.mon_def,
turn = info.mon_turn,
pre = info.damage;
var list = [];
var start_spell = hero_spell;
var calNext = function (currSpell, maxSpell) {
var start = Math.floor(currSpell),
end = Math.floor(maxSpell);
if (start > end) return null;
while (start < end) {
var mid = Math.floor((start + end) / 2);
if (mid - start > end - mid) mid--;
var nextInfo = core.enemys.getDamageInfo(enemy, { "spell": mid }, x, y, floorId);
if (nextInfo == null || (typeof nextInfo == 'number')) return null;
if (turn > nextInfo.mon_turn) end = mid;
else start = mid + 1;
}
var nextInfo = core.enemys.getDamageInfo(enemy, { "spell": start }, x, y, floorId);
return nextInfo == null || (typeof nextInfo == 'number') || nextInfo.mon_turn >= turn ? null : [start, nextInfo.damage];
}
var currSpell = start_spell;
while (true) {
var next = calNext(currSpell + 1, Number.MAX_SAFE_INTEGER, pre, turn);
if (next == null) break;
currSpell = next[0];
pre = next[1];
list.push([currSpell - hero_spell, info.damage - pre]);
if (pre <= 0 && !core.flags.enableNegativeDamage) break;
if (list.length >= number) break;
}
if (list.length == 0) list.push([0, 0]);
return list;
}
},
"手册(临时)": function () {
// 在此增加新插件
ui.prototype._drawBook_drawContent = function (index, enemy, top, left) {
var width = core._PX_ - left; // 9 : 8 : 8 划分三列
this._drawBook_drawRow1(index, enemy, top, left, width, top + 20);
this._drawBook_drawRow4(index, enemy, top, left, width, top + 38);
this._drawBook_drawRow2(index, enemy, top, left, width, top + 56);
this._drawBook_drawRow3(index, enemy, top, left, width, top + 74);
this._drawBook_drawRow5(index, enemy, top, left, width, top + 90);
}
ui.prototype._drawBook_drawRow4 = function (index, enemy, top, left, width, position) {
// 绘制第一行
core.setTextAlign('ui', 'left');
var b13 = this._buildFont(13, true),
f13 = this._buildFont(13, false);
var col1 = left,
col2 = left + width * 9 / 25,
col3 = left + width * 17 / 25;
core.fillText('ui', core.getStatusLabel('speed'), col1, position, '#DDDDDD', f13);
core.fillText('ui', core.formatBigNumber(enemy.speed || 0), col1 + 30, position, null, b13);
core.fillText('ui', core.getStatusLabel('spell'), col2, position, null, f13);
core.fillText('ui', core.formatBigNumber(enemy.spell || 0), col2 + 30, position, null, b13);
core.fillText('ui', core.getStatusLabel('mdef'), col3, position, null, f13);
core.fillText('ui', core.formatBigNumber(enemy.mdef || 0) + "%", col3 + 30, position, null, b13);
}
ui.prototype._drawBook_drawRow5 = function (index, enemy, top, left, width, position) {
// 绘制第一行
core.setTextAlign('ui', 'left');
var b13 = this._buildFont(13, true),
f13 = this._buildFont(13, false);
var col1 = left,
col2 = left + width * 13 / 25;
core.fillText('ui', '速度临界', col1 - 120, position, '#DDDDDD', f13);
core.fillText('ui', `[${core.formatBigNumber(enemy.criticalSpeed?.[0] || 0)},${core.formatBigNumber(enemy.criticalSpeed?.[1] || 0)}]`, col1 - 60, position, null, b13);
core.fillText('ui', "勇士出手次数", col1, position, '#DDDDDD', f13);
core.fillText('ui', core.getDamageInfo(enemy, null) ? core.getDamageInfo(enemy, null).hero_turn : '???', col1 + 80, position, null, b13);
core.fillText('ui', "怪物出手次数", col2, position, null, f13);
core.fillText('ui', core.getDamageInfo(enemy, null) ? core.getDamageInfo(enemy, null).mon_turn : '???', col2 + 80, position, null, b13);
}
ui.prototype._drawBook_pageinfo = function () {
var per_page = 4;
var padding_top = 12; // 距离顶端像素
var per_height = (core._PY_ - 32 - padding_top) / per_page;
return { per_page: per_page, padding_top: padding_top, per_height: per_height };
}
},
"新怪物手册": function () {
// 在此增加新插件
const book = document.createElement("canvas"); //创建怪物手册画布
book.style.position = "absolute";
book.style.zIndex = 400;
book.style.display = "none";
book.id = "book";
main.dom.gameGroup.insertAdjacentElement("afterend", book);
book.style.top = "50%";
book.style.left = "50%";
book.style.transform = "translate(-50%,-50%)";
const ctx = book.getContext("2d");
main.dom.book = book
book.onclick = function (e) { //画布点击监听
try {
e.preventDefault();
const left = core.dom.gameGroup.offsetLeft;
const top = core.dom.gameGroup.offsetTop;
const px = Math.floor((e.clientX - left) / core.domStyle.scale),
py = Math.floor((e.clientY - top) / core.domStyle.scale);
let x, y;
if (core.domStyle.isVertical) { //对竖屏进行坐标转换
x = py * 3;
y = 1248 - px * 3;
} else {
x = px * 3;
y = py * 3;
}
bookclick(x, y)
} catch (ee) {
main.log(ee);
}
}
function bookclick(x, y) { //点击画布(x,y)触发的效果
const makeBox = ([x, y], [w, h]) => { //创建点击检测区域
return [
[x, y],
[x + w, y + h],
];
};
const pos = [x, y]; //创建点击坐标pos
const inRect = ([x, y], [ //检测点击坐标是否在检测区域中
[sx, sy],
[dx, dy]
]) => {
return sx <= x && x <= dx && sy <= y && y <= dy;
};
/**
* 使用范例
*const backbox = makeBox([15, 35], [210, 90]);
*if(inRectpos,backbox)){}
*/
}
this.drawBook = function (floorId = core.status.floorId) {
if (core.domStyle.isVertical) { //对竖屏进行绘制坐标变换,只需要考虑横屏绘制,竖屏将自适应转置
ctx.canvas.width = 1248;
ctx.canvas.height = 2028;
ctx.save(); //保存设置
ctx.translate(1248, 0); //重新定位右上角为基准
ctx.rotate(Math.PI / 2); //旋转90度
} else {
ctx.canvas.width = 2028;
ctx.canvas.height = 1248;
}
//在这里写绘制内容,只写横屏就行
ctx.restore(); //恢复变换前的坐标,否则将连续转置
}
},
"scrollingText": function () {
// 本插件用于绘制在线留言
// 说明https://h5mota.com/bbs/thread/?tid=1017
// 目前使用core.http代替帖子中提到的axios
////// 绘制提示同时播放成功音效 //////
ui.prototype.drawSuccessTip = function (text, id, frame) {
this.drawTip(text, id, frame);
core.playSound('gem.mp3');
}
core.plugin.aniMap = new Map()
////// 绘制提示同时播放错误音效 //////
ui.prototype.drawFailTip = function (text, id, frame) {
this.drawTip(text, id, frame);
core.playSound('error.mp3');
}
/** 塔的英文名 */
const towerName = core.firstData.name;
let [W, H] = [core.__SIZE__, core.__SIZE__];
let [WIDTH, HEIGHT] = [core.__PIXELS__, core.__PIXELS__];
//#region 弹幕的收发
this.getComment = function () {
if (core.isReplaying()) return;
let form = new FormData();
form.append('type', 1);
form.append('towername', towerName);
utils.prototype.http(
'POST',
'https://h5mota.com/backend/tower/barrage.php',
form,
function (res) {
try {
res = JSON.parse(res);
console.log(res);
core.drawTip('接收成功!')
core.playSound('item.mp3');
let commentCollection = {};
const commentList = res?.list;
const isEmpty = /^\s*$/;
for (let i = 0, l = commentList.length; i <= l - 1; i++) {
if (isEmpty.test(commentList[i]?.comment)) continue;
const commentTagsList = commentList[i].tags.split(',');
const [cFloorId, cX, cY] = commentTagsList;
if (0 <= cX && cX <= W - 1 && 0 <= cY && cY <= H - 1 && core.floorIds.includes(cFloorId)) {
if (!commentCollection.hasOwnProperty(cFloorId)) { commentCollection[cFloorId] = {}; }
const str = cX + ',' + cY;
if (!commentCollection[cFloorId].hasOwnProperty(str)) { commentCollection[cFloorId][str] = []; }
commentCollection[cFloorId][str].push(commentList[i]?.comment);
}
}
core.setFlag('commentCollection', commentCollection);
} catch (err) {
core.drawFailTip('接收失败!' + err.message);
}
},
function (err) {
err = JSON.parse(err);
console.error(err);
core.drawFailTip('接收失败' + err?.message);
},
null, null, null, 1000
);
}
this.postComment = function (comment, tags) {
if (core.isReplaying()) return;
const isEmpty = /^\s*$/;
if (isEmpty.test(comment)) {
core.drawFailTip('您输入的消息为空,请重发!');
return;
}
let form = new FormData();
form.append('type', 2);
form.append('towername', towerName);
form.append('comment', comment);
form.append('tags', tags);
form.append('userid', 2324);
form.append('password', '77c8fd5ff49c370342e4472ebdda5903');
utils.prototype.http(
'POST',
'https://h5mota.com/backend/tower/barrage.php',
form,
function (res) {
try {
res = JSON.parse(res);
console.log(res);
if (res?.code === 0) {
core.drawTip('提交成功!')
} else {
core.drawTip('提交失败!' + res?.message);
}
} catch (err) {
core.drawFailTip('提交失败!' + err.message);
}
},
function (err) {
err = JSON.parse(err);
console.error(err);
core.drawFailTip('提交失败!' + err?.message);
},
null, null, null, 1000
);
}
//#endregion
/** 若变量comment为真在每层切换时在地上有弹幕的地方显示相应图标。 */
this.drawCommentSign = function () {
if (!core.hasFlag('comment') || core.isReplaying()) return;
let commentCollection = core.getFlag('commentCollection', {}),
floorId = core.status.floorId;
core.createCanvas('sign', 0, 0, WIDTH, HEIGHT, 61);
core.setOpacity('sign', 0.6);
if (commentCollection.hasOwnProperty(floorId)) {
for (let pos in commentCollection[floorId]) {
const l = commentCollection[floorId][pos].length;
for (let i = 0; i <= l - 1; i++) {
const [x, y] = pos.split(',');
core.drawIcon('sign', 'postman', 32 * x, 32 * y);
break;
}
}
}
}
/** 立即清除楼层的弹幕图标。关闭弹幕相关设置时调用。 */
this.clearCommentSign = function () {
core.deleteCanvas('sign');
}
/** 默认一次显示的弹幕数 */
const showNum = 5;
// 每走一步或瞬移,调用该函数,若目标点有弹幕,显示之
this.showComment = function (x, y) {
if (!core.getFlag('comment') || core.isReplaying()) return;
const commentCollection = core.getFlag('commentCollection', {});
const floorId = core.status.floorId,
str = x + ',' + y;
if (commentCollection.hasOwnProperty(floorId) &&
commentCollection[floorId].hasOwnProperty(str)) {
let commentArr = commentCollection[floorId][str].concat();
const commentArrPicked = pickComment(commentArr, showNum);
drawComment(commentArrPicked);
}
}
/** 返回从commentArr中挑选showNum个comment组成的数组*/
function pickComment(commentArr, showNum) {
let showList = [];
if (commentArr.length <= showNum) {
showList = commentArr;
} else {
for (let i = 0; i <= showNum - 1; i++) {
const l = commentArr.length,
n = core.plugin.dice(l - 1);
showList.push(commentArr[n]);
commentArr.splice(n, 1);
}
}
return showList;
}
function drawComment(commentArr) {
const l = commentArr.length;
let yList = generateCommentYList(20, HEIGHT - 20, showNum);
if (l < showNum) yList = getRandomElements(yList, l);
for (let i = 0; i <= l - 1; i++) {
drawCommentStr(commentArr[i], WIDTH + 20 * Math.random(),
yList[i], Math.random() * 0.1 + 0.1);
}
}
/** 生成count个随机数范围从min到max作为弹幕的y坐标*/
function generateCommentYList(min, max, count) {
let yList = Array(count).fill(0);
const distance = (max - min) / (count + 1);
for (let i = 0; i < count; i++) {
yList[i] = min + distance * (i + 1) + (Math.random() - 0.5) * (distance / 2);
}
return yList;
}
function getRandomElements(arr, count) {
let result = [...arr];
let len = result.length;
count = Math.min(len, count);
for (let i = len - 1; i > len - 1 - count; i--) {
let j = Math.floor(Math.random() * (i + 1));
[result[i], result[j]] = [result[j], result[i]];
}
return result.slice(len - count);
}
//#region 弹幕绘制部分
const { Animation, linear, Ticker } = core.plugin.animate ?? {};
const ctxName = 'scrollingText';
if (Ticker) {
const ticker = new Ticker();
ticker.add(() => {
if (core.isReplaying()) return;
core.createCanvas(ctxName, 0, 0, core.__PIXELS__, core.__PIXELS__, 136); //每帧重绘该画布
});
}
/**
* 绘制弹幕
* @example
* drawCommentStr('OK', 450, 200, 0.1);
* @param {string} content 弹幕的内容
* @param {number} x 弹幕的初始x坐标
* @param {number} y 弹幕的初始y坐标
* @param {number} vx 弹幕的横向滚动速度
*/
function drawCommentStr(content, x, y, vx) {
if (core.isReplaying() || !Animation) return;
const ani = new Animation();
core.plugin.aniMap.set(ani, (ani) => ani.ticker.destroy());
ani.ticker.add(() => {
core.fillText(ctxName, content, x + ani.x, y, 'white', '16px Verdana');
})
// 弹幕的最大长度5600再长属于异常数据
const aim = 600 + Math.min(core.calWidth(ctxName, content, '16px Verdana'), 5000);
ani.mode(linear())
.time(aim / vx)
.absolute()
.move(-aim, 0)
ani.all().then(() => {
ani.ticker.destroy();
});
}
//#endregion
}
}