From fbb28f8c69b6be81c87a6175aac70adc073c844f Mon Sep 17 00:00:00 2001 From: lizhuoyuan <2820814112@qq.com> Date: Fri, 7 Feb 2025 17:09:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E8=87=AA=E5=AE=9A=E4=B9=89=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E7=95=8C=E9=9D=A2=E6=B7=BB=E5=8A=A0=E9=94=AE=E7=9B=98?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project/plugins.js | 3380 +++++++++++++++++++++++--------------------- 1 file changed, 1732 insertions(+), 1648 deletions(-) diff --git a/project/plugins.js b/project/plugins.js index a277440b..65377fda 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -1,7 +1,7 @@ /// -var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = +var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { - "init": function () { + "init": function () { console.log("插件编写测试"); @@ -19,7 +19,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); // 从V2.6开始,插件中用this.XXX方式定义的函数也会被转发到core中,详见文档-脚本-函数的转发。 }, - "shop": function () { + "shop": function () { // 【全局商店】相关的功能 // // 打开一个全局商店 @@ -214,7 +214,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = return false; }, 60); }, - "removeMap": function () { + "removeMap": function () { // 高层塔砍层插件,删除后不会存入存档,不可浏览地图也不可飞到。 // 推荐用法: // 对于超高层或分区域塔,当在1区时将2区以后的地图删除;1区结束时恢复2区,进二区时删除1区地图,以此类推 @@ -301,7 +301,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }); } }, - "fiveLayers": function () { + "fiveLayers": function () { // 是否启用五图层(增加背景2层和前景2层) 将__enable置为true即会启用;启用后请保存后刷新编辑器 // 背景层2将会覆盖背景层 被事件层覆盖 前景层2将会覆盖前景层 // 另外 请注意加入两个新图层 会让大地图的性能降低一些 @@ -455,7 +455,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }; } }, - "itemShop": function () { + "itemShop": function () { // 道具商店相关的插件 // 可在全塔属性-全局商店中使用「道具商店」事件块进行编辑(如果找不到可以在入口方块中找) @@ -761,7 +761,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = } }, - "enemyLevel": function () { + "enemyLevel": function () { // 此插件将提供怪物手册中的怪物境界显示 // 使用此插件需要先给每个怪物定义境界,方法如下: // 点击怪物的【配置表格】,找到“【怪物】相关的表格配置”,然后在【名称】仿照增加境界定义: @@ -849,7 +849,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = }, - "multiHeros": function () { + "multiHeros": function () { // 多角色插件 // Step 1: 启用本插件 // Step 2: 定义每个新的角色各项初始数据(参见下方注释) @@ -995,7 +995,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = core.setFlag("heroId", toHeroId); // 保存切换到的角色ID } }, - "heroFourFrames": function () { + "heroFourFrames": function () { // 样板的勇士/跟随者移动时只使用2、4两帧,观感较差。本插件可以将四帧全用上。 // 是否启用本插件 @@ -1048,7 +1048,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = return false; } }, - "startCanvas": function () { + "startCanvas": function () { // 使用本插件可以将自绘的标题界面居中。仅在【标题开启事件化】后才有效。 // 由于一些技术性的原因,标题界面事件化无法应用到覆盖状态栏的整个界面。 // 这是一个较为妥协的插件,会在自绘标题界面时隐藏状态栏、工具栏和边框,并将画布进行居中。 @@ -1134,569 +1134,569 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = _loadData.call(core.control, data, callback); } }, - "advancedAnimation": function () { - // -------------------- 插件说明 -------------------- // - // github仓库:https://github.com/unanmed/animate - // npm包名:mutate-animate - // npm地址:https://www.npmjs.com/package/mutate-animate + "advancedAnimation": function () { + // -------------------- 插件说明 -------------------- // + // github仓库:https://github.com/unanmed/animate + // npm包名:mutate-animate + // npm地址:https://www.npmjs.com/package/mutate-animate - if (main.replayChecking) return core.plugin.animate = {} + if (main.replayChecking) return core.plugin.animate = {} - // 保存所有Ticker的引用 - const tickersMap = new Map(); + // 保存所有Ticker的引用 + const tickersMap = new Map(); - /** 摧毁指定名字的ticker */ - this.deleteTicker = function (name) { - const ticker = tickersMap.get(name); - if (!ticker) return; - ticker.destroy(); - tickersMap.delete(name); - } - - /** 摧毁所有有名字的ticker */ - this.deleteAllTickers = function () { - tickersMap.forEach((ticker) => { + /** 摧毁指定名字的ticker */ + this.deleteTicker = function (name) { + const ticker = tickersMap.get(name); if (!ticker) return; ticker.destroy(); - }) - tickersMap.clear(); - } + tickersMap.delete(name); + } - this.getAllTickers = () => tickersMap; + /** 摧毁所有有名字的ticker */ + this.deleteAllTickers = function () { + tickersMap.forEach((ticker) => { + if (!ticker) return; + ticker.destroy(); + }) + tickersMap.clear(); + } - 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); - } + this.getAllTickers = () => tickersMap; + + 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); - }; - 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); + 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); + } } - 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(name) { - o(this, "timing"); - o(this, "relation", "absolute"); - o(this, "easeTime", 0); - o(this, "applying", {}); - o(this, "getTime", Date.now); - const ticker = new I(); - o(this, "ticker", ticker); - o(this, "value", {}); - o(this, "listener", {}); - this.timing = (i) => i; - if (typeof name === 'string') tickersMap.set(name, ticker); - } - 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]; + class F { + constructor(name) { + o(this, "timing"); + o(this, "relation", "absolute"); + o(this, "easeTime", 0); + o(this, "applying", {}); + o(this, "getTime", Date.now); + const ticker = new I(); + o(this, "ticker", ticker); + o(this, "value", {}); + o(this, "listener", {}); + this.timing = (i) => i; + if (typeof name === 'string') tickersMap.set(name, ticker); + } + 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); }); - }; - return this.ticker.add(c), this.animateFn.custom["@@bind"] = c, this.targetValue.system["@@bind"] = u, this; + } + 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); + } } - 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); + + function y(n) { + return n != null; } - 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); + async function R(n) { + return new Promise((i) => setTimeout(i, n)); } - 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( + 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"); + 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 - }); + 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]); + } } - mode(t) { - return this.timing = t, this; + 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; } - 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"); + + 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); }; - 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)) + 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( - "The first parameter of power timing function only allow integer." + "Unexpected parameters are delivered in trigo timing function." ); - 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); + + 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); } - 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); + 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." + ); } - if (n === "tan") { - const t = (s) => Math.atan(s * 5) / P; - return p(i, (s) => 1 - t(1 - s), t); + + 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." + ); } - 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 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 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)]; - }; - } + 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)]; + }; + } - core.plugin.animate = { - Animation: j, - AnimationBase: F, - Ticker: I, - Transition: O, - bezier: q, - bezierPath: H, - circle: D, - hyper: G, - inverseTrigo: N, - linear: Y, - power: C, - shake: B, - sleep: R, - trigo: U, - } + core.plugin.animate = { + Animation: j, + AnimationBase: F, + Ticker: I, + Transition: O, + bezier: q, + bezierPath: H, + circle: D, + hyper: G, + inverseTrigo: N, + linear: Y, + power: C, + shake: B, + sleep: R, + trigo: U, + } -}, - "drawItemDetail": function () { + }, + "drawItemDetail": function () { /* 宝石血瓶左下角显示数值 - * 需要将 变量:itemDetail改为true才可正常运行 - * 请尽量减少勇士的属性数量,否则可能会出现严重卡顿(划掉,现在你放一万个属性也不会卡) + * 需要将 变量:itemDetail改为true才可正常运行 + * 请尽量减少勇士的属性数量,否则可能会出现严重卡顿(划掉,现在你放一万个属性也不会卡) * 注意:这里的属性必须是core.status.hero里面的,flag无法显示 - * 如果不想显示,可以core.setFlag("itemDetail", false); + * 如果不想显示,可以core.setFlag("itemDetail", false); * 然后再core.getItemDetail(); * 如有bug在大群或造塔群@古祠 */ @@ -1846,277 +1846,277 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = } } }, - "autoGet": function () { - // 在此增加新插件 - /** - * --------------- 使用说明 --------------- - * 变量autoGet控制自动拾取开关 - * 变量autoBattle控制自动清怪开关 - */ + "autoGet": function () { + // 在此增加新插件 + /** + * --------------- 使用说明 --------------- + * 变量autoGet控制自动拾取开关 + * 变量autoBattle控制自动清怪开关 + */ - const { Transition, hyper, Ticker } = core.plugin.animate ?? {}; + const { Transition, hyper, Ticker } = core.plugin.animate ?? {}; - // 磁吸特效的时长,单位毫秒 - const transitionTime = 600; + // 磁吸特效的时长,单位毫秒 + const transitionTime = 600; - const transitionList = []; + const transitionList = []; - const ctxName = 'globalAnimate'; + const ctxName = 'globalAnimate'; - if (!main.replayChecking) { - const ticker = new Ticker(); - ticker.add(() => { - if (!core.isPlaying()) return; - const ctx = core.getContextByName(ctxName); - if (!has(ctx)) return; - core.clearMap(ctx); - }); - } - - // 每走一步后自动拾取的判定要放在阻击结算之后 - - control.prototype.moveDirectly = function (destX, destY, ignoreSteps) { - const res = this.controldata.moveDirectly( - destX, - destY, - ignoreSteps - ); - core.plugin.autoClear(); - return res; - }; - - this.autoClear = function () { - if (core.isReplaying()) return; - auto(); - for (let i = 0; i < transitionList.length; i++) { - const t = transitionList[i]; - let { x, y } = core.status.hero.loc; - t.value.x = x * 32 - core.bigmap.offsetX; - t.value.y = y * 32 - core.bigmap.offsetY; + if (!main.replayChecking) { + const ticker = new Ticker(); + ticker.add(() => { + if (!core.isPlaying()) return; + const ctx = core.getContextByName(ctxName); + if (!has(ctx)) return; + core.clearMap(ctx); + }); } - } - function willLvUp(exp){ - const nextExp = core.getNextLvUpNeed(); - if (typeof exp === 'number' && typeof nextExp === 'number' && exp >= nextExp) return true; - return false; - } + // 每走一步后自动拾取的判定要放在阻击结算之后 - /** - * 是否清这个怪,可以修改这里来实现对不同怪的不同操作 - * @param {string} enemy - * @param {number} x - * @param {number} y - */ - function canBattle(enemy, x, y) { - const loc = `${x},${y}`; - const floor = core.floors[core.status.floorId]; - const e = core.material.enemys[enemy]; - const hasEvent = - has(floor.afterBattle[loc]) || has(floor.beforeBattle[loc]) - || has(e.beforeBattle) || has(e.afterBattle) - || has(floor.events[loc] || willLvUp(e.exp) // 防止有升级后事件 + control.prototype.moveDirectly = function (destX, destY, ignoreSteps) { + const res = this.controldata.moveDirectly( + destX, + destY, + ignoreSteps ); - // 有事件,不清 - if (hasEvent) return false; - if (core.hasSpecial(e.special, 19) || core.hasSpecial(e.special, 21) - || core.hasSpecial(e.special, 29)) return false; // 有特定特殊属性的怪不清 - const damage = core.getDamageInfo(enemy, void 0, x, y)?.damage; - // 0伤或负伤,清 - if (has(damage) && damage <= 0) return true; - return false; - } + core.plugin.autoClear(); + return res; + }; - /** - * 判断一个点是否能遍历 - */ - function judge(block, nx, ny, tx, ty, dir, floorId, autoBattle, autoGet) { - if (!has(block)) return {}; - const cls = block.event.cls; - const loc = `${tx},${ty}`; - const floor = core.floors[floorId]; - const changeFloor = floor.changeFloor[loc]; - const isEnemy = autoBattle && cls.startsWith('enemy'), - isItem = autoGet && cls === 'items'; + this.autoClear = function () { + if (core.isReplaying()) return; + auto(); + for (let i = 0; i < transitionList.length; i++) { + const t = transitionList[i]; + let { x, y } = core.status.hero.loc; + t.value.x = x * 32 - core.bigmap.offsetX; + t.value.y = y * 32 - core.bigmap.offsetY; + } + } - if (has(changeFloor)) { - if (!core.noPass(tx, ty, floorId) && !core.canMoveHero(nx, ny, dir)) { - return false; - } - if (changeFloor.ignoreChangeFloor ?? core.flags.ignoreChangeFloor) { - return true; - } + function willLvUp(exp) { + const nextExp = core.getNextLvUpNeed(); + if (typeof exp === 'number' && typeof nextExp === 'number' && exp >= nextExp) return true; return false; } - if (has(core.floors[floorId].events[loc])) return false; + /** + * 是否清这个怪,可以修改这里来实现对不同怪的不同操作 + * @param {string} enemy + * @param {number} x + * @param {number} y + */ + function canBattle(enemy, x, y) { + const loc = `${x},${y}`; + const floor = core.floors[core.status.floorId]; + const e = core.material.enemys[enemy]; + const hasEvent = + has(floor.afterBattle[loc]) || has(floor.beforeBattle[loc]) + || has(e.beforeBattle) || has(e.afterBattle) + || has(floor.events[loc] || willLvUp(e.exp) // 防止有升级后事件 + ); + // 有事件,不清 + if (hasEvent) return false; + if (core.hasSpecial(e.special, 19) || core.hasSpecial(e.special, 21) + || core.hasSpecial(e.special, 29)) return false; // 有特定特殊属性的怪不清 + const damage = core.getDamageInfo(enemy, void 0, x, y)?.damage; + // 0伤或负伤,清 + if (has(damage) && damage <= 0) return true; + return false; + } - if (isEnemy || isItem) - return { - isEnemy, - isItem, + /** + * 判断一个点是否能遍历 + */ + function judge(block, nx, ny, tx, ty, dir, floorId, autoBattle, autoGet) { + if (!has(block)) return {}; + const cls = block.event.cls; + const loc = `${tx},${ty}`; + const floor = core.floors[floorId]; + const changeFloor = floor.changeFloor[loc]; + const isEnemy = autoBattle && cls.startsWith('enemy'), + isItem = autoGet && cls === 'items'; + + if (has(changeFloor)) { + if (!core.noPass(tx, ty, floorId) && !core.canMoveHero(nx, ny, dir)) { + return false; + } + if (changeFloor.ignoreChangeFloor ?? core.flags.ignoreChangeFloor) { + return true; + } + return false; + } + + if (has(core.floors[floorId].events[loc])) return false; + + if (isEnemy || isItem) + return { + isEnemy, + isItem, + }; + + return false; + } + + /** + * 是否捡拾这个物品 + */ + function canGetItem(item, loc, floorId) { + // 可以用于检测道具是否应该被捡起,例如如果捡起后血量超过80%则不捡起可以这么写: + // if (item.cls === 'items') { + // 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); + + // eval(item.itemEffect); + + // core.status.hero = before; + // window.hero = before; + // window.flags = before.flags; + // if ( + // diff.hp && + // diff.hp + core.status.hero.hp > core.status.hero.hpmax * 0.8 + // ) + // return false; + // } + return true; + } + + /** + * @template T + * @param {T} v + * @returns {v is NonNullable} + */ + function has(v) { + return v !== null && v !== undefined; + } + + function hasBlockDamage(loc) { + const checkblockInfo = core.status.checkBlock; + const damage = checkblockInfo.damage[loc]; + const ambush = checkblockInfo.ambush[loc]; + const repulse = checkblockInfo.repulse[loc]; + const chase = checkblockInfo.chase[loc]; + return (has(damage) && damage > 0) || has(ambush) || has(repulse) || has(chase); + } + + /** + * 广搜,搜索可以到达的需要清的怪 + * @param {string} floorId + */ + function bfs(floorId, deep = Infinity) { + core.extractBlocks(floorId); + const objs = core.getMapBlocksObj(floorId); + const { x, y } = core.status.hero.loc; + /** @type {[direction, number, number][]} */ + const dir = Object.entries(core.utils.scan).map(v => [v[0], v[1].x, v[1].y]); + const floor = core.status.maps[floorId]; + + /** @type {[number, number][]} */ + const queue = [[x, y]]; + const mapped = { + [`${x},${y}`]: true }; - return false; - } + const autoBattle = core.getFlag('autoBattle', false), + autoGet = core.getFlag('autoGet', false); + if (!autoGet && !autoBattle) return; - /** - * 是否捡拾这个物品 - */ - function canGetItem(item, loc, floorId) { - // 可以用于检测道具是否应该被捡起,例如如果捡起后血量超过80%则不捡起可以这么写: - // if (item.cls === 'items') { - // 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); - - // eval(item.itemEffect); - - // core.status.hero = before; - // window.hero = before; - // window.flags = before.flags; - // if ( - // diff.hp && - // diff.hp + core.status.hero.hp > core.status.hero.hpmax * 0.8 - // ) - // return false; - // } - return true; - } - - /** - * @template T - * @param {T} v - * @returns {v is NonNullable} - */ - function has(v) { - return v !== null && v !== undefined; - } - - function hasBlockDamage(loc) { - const checkblockInfo = core.status.checkBlock; - const damage = checkblockInfo.damage[loc]; - const ambush = checkblockInfo.ambush[loc]; - const repulse = checkblockInfo.repulse[loc]; - const chase = checkblockInfo.chase[loc]; - return (has(damage) && damage > 0) || has(ambush) || has(repulse) || has(chase); - } - - /** - * 广搜,搜索可以到达的需要清的怪 - * @param {string} floorId - */ - function bfs(floorId, deep = Infinity) { - core.extractBlocks(floorId); - const objs = core.getMapBlocksObj(floorId); - const { x, y } = core.status.hero.loc; - /** @type {[direction, number, number][]} */ - const dir = Object.entries(core.utils.scan).map(v => [v[0], v[1].x, v[1].y]); - const floor = core.status.maps[floorId]; - - /** @type {[number, number][]} */ - const queue = [[x, y]]; - const mapped = { - [`${x},${y}`]: true - }; - - const autoBattle = core.getFlag('autoBattle', false), - autoGet = core.getFlag('autoGet', false); - if (!autoGet && !autoBattle) return; - - while (queue.length > 0 && deep > 0) { - const [nx, ny] = queue.shift(); - dir.forEach(v => { - const [tx, ty] = [nx + v[1], ny + v[2]]; - if (tx < 0 || ty < 0 || tx >= floor.width || ty >= floor.height) { - return; - } - const loc = `${tx},${ty}`; - if (mapped[loc]) return; - const block = objs[loc]; - mapped[loc] = true; - const type = judge(block, nx, ny, tx, ty, v[0], floorId, autoBattle, autoGet); - if (type === false) return; - const { isEnemy, isItem } = type; - - if (isEnemy) { - if (canBattle(block.event.id, tx, ty) && !block.disable) { - core.battle(block.event.id, tx, ty); - core.updateCheckBlock(); - } else { + while (queue.length > 0 && deep > 0) { + const [nx, ny] = queue.shift(); + dir.forEach(v => { + const [tx, ty] = [nx + v[1], ny + v[2]]; + if (tx < 0 || ty < 0 || tx >= floor.width || ty >= floor.height) { return; } - } else if (isItem) { - const item = core.material.items[block.event.id]; - if (canGetItem(item, loc, floorId)) { - core.getItem(item.id, 1, tx, ty); - if (!core.isReplaying()) { - let px = tx * 32 - core.bigmap.offsetX; - let py = ty * 32 - core.bigmap.offsetY; - const t = new Transition(); - t.mode(hyper('sin', 'out')) - .time(transitionTime) - .absolute() - .transition('x', px) - .transition('y', py); - let { x, y } = core.status.hero.loc; - t.value.x = x * 32 - core.bigmap.offsetX; - t.value.y = y * 32 - core.bigmap.offsetY; - transitionList.push(t); - t.ticker.add(() => { - core.drawIcon(ctxName, item.id, t.value.x, t.value.y, 32, 32); - let { x, y } = core.status.hero.loc; - if (Math.abs(t.value.x - x * 32 + core.bigmap.offsetX) < 0.05 && - Math.abs(t.value.y - y * 32 + core.bigmap.offsetY) < 0.05 - ) { - t.ticker.destroy(); - const index = transitionList.findIndex(v => v === t); - transitionList.splice(index, 1); - } - }); + const loc = `${tx},${ty}`; + if (mapped[loc]) return; + const block = objs[loc]; + mapped[loc] = true; + const type = judge(block, nx, ny, tx, ty, v[0], floorId, autoBattle, autoGet); + if (type === false) return; + const { isEnemy, isItem } = type; + + if (isEnemy) { + if (canBattle(block.event.id, tx, ty) && !block.disable) { + core.battle(block.event.id, tx, ty); + core.updateCheckBlock(); + } else { + return; + } + } else if (isItem) { + const item = core.material.items[block.event.id]; + if (canGetItem(item, loc, floorId)) { + core.getItem(item.id, 1, tx, ty); + if (!core.isReplaying()) { + let px = tx * 32 - core.bigmap.offsetX; + let py = ty * 32 - core.bigmap.offsetY; + const t = new Transition(); + t.mode(hyper('sin', 'out')) + .time(transitionTime) + .absolute() + .transition('x', px) + .transition('y', py); + let { x, y } = core.status.hero.loc; + t.value.x = x * 32 - core.bigmap.offsetX; + t.value.y = y * 32 - core.bigmap.offsetY; + transitionList.push(t); + t.ticker.add(() => { + core.drawIcon(ctxName, item.id, t.value.x, t.value.y, 32, 32); + let { x, y } = core.status.hero.loc; + if (Math.abs(t.value.x - x * 32 + core.bigmap.offsetX) < 0.05 && + Math.abs(t.value.y - y * 32 + core.bigmap.offsetY) < 0.05 + ) { + t.ticker.destroy(); + const index = transitionList.findIndex(v => v === t); + transitionList.splice(index, 1); + } + }); + } + } else { + return; } - } else { - return; } - } - if (hasBlockDamage(loc)) return; - queue.push([tx, ty]); - }); - deep--; + if (hasBlockDamage(loc)) return; + queue.push([tx, ty]); + }); + deep--; + } } - } - function auto() { - const before = flags.__forbidSave__; - // 如果勇士当前点有地图伤害,只清周围,如果有事件,直接不清了 - const { x, y } = core.status.hero.loc; - const floor = core.floors[core.status.floorId]; - const loc = `${x},${y}`; - const hasEvent = has(floor.events[loc]); - if (hasEvent) return; - let deep = Infinity; - if (hasBlockDamage(loc) && core.flags.enableGentleClick) { // 有地图伤害允许轻点附近1格 - deep = 1; - } - flags.__forbidSave__ = true; - flags.__statistics__ = true; - const ctx = core.getContextByName(ctxName); - if (!ctx) core.createCanvas(ctxName, 0, 0, core.__PIXELS__, core.__PIXELS__, 75); - bfs(core.status.floorId, deep); - flags.__statistics__ = false; - flags.__forbidSave__ = before; - core.updateStatusBar(); - }; -}, - "newBackpackLook": function () { + function auto() { + const before = flags.__forbidSave__; + // 如果勇士当前点有地图伤害,只清周围,如果有事件,直接不清了 + const { x, y } = core.status.hero.loc; + const floor = core.floors[core.status.floorId]; + const loc = `${x},${y}`; + const hasEvent = has(floor.events[loc]); + if (hasEvent) return; + let deep = Infinity; + if (hasBlockDamage(loc) && core.flags.enableGentleClick) { // 有地图伤害允许轻点附近1格 + deep = 1; + } + flags.__forbidSave__ = true; + flags.__statistics__ = true; + const ctx = core.getContextByName(ctxName); + if (!ctx) core.createCanvas(ctxName, 0, 0, core.__PIXELS__, core.__PIXELS__, 75); + bfs(core.status.floorId, deep); + flags.__statistics__ = false; + flags.__forbidSave__ = before; + core.updateStatusBar(); + }; + }, + "newBackpackLook": function () { // 注:///// *** 裹起来的区域: 该区域内参数可以随意更改调整ui绘制 不会影响总体布局 // 请尽量修改该区域而不是其他区域 修改的时候最好可以对照现有ui修改 @@ -3167,7 +3167,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = ani.all().then(() => { ani.ticker.destroy(); }); } }, - "Menu": function () { + "Menu": function () { // 本插件定义了一些用于绘制的基类 class ButtonBase { constructor(x, y, w, h) { @@ -3176,8 +3176,6 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = this.w = w; this.h = h; this.disable = false; - /** 按下此按钮后是否重绘所在Menu */ - this.redraw = true; /** 所在的Menu,用于触发重绘等事件 */ this.menu; @@ -3194,14 +3192,11 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = this.btnList = new Map(); this.keyEvent = () => { }; this.clickEvent = (x, y, px, py) => { - let hasClick = false; this.btnList.forEach((btn) => { if (btn.disable) return; if (px >= btn.x && px <= btn.x + btn.w && py > btn.y && py <= btn.y + btn.h) { btn.event(x, y, px, py); - if (btn.redraw) hasClick = true; } - if (hasClick) this.drawContent(); }); } } @@ -3227,7 +3222,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = beginListen() { core.registerAction('keyDown', this.name, this.keyEvent, 100); core.registerAction('ondown', this.name, this.clickEvent, 100); - + } endListen() { @@ -3248,7 +3243,15 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = class MenuPage extends MenuBase { constructor(pageList, currPage, ctx) { super(ctx); + /** + * 当前页面列表 + * @type {Array} + */ this.pageList = pageList; + /** + * 当前页的序号 + * @type {number} + */ this.currPage = currPage | 0; } @@ -3282,836 +3285,924 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = this.MenuBase = { ButtonBase, MenuBase, MenuPage }; }, - "scrollingText": function () { - // 本插件用于绘制在线留言 + "scrollingText": function () { + // 本插件用于绘制在线留言 - /** 塔的英文名 */ - const towerName = core.firstData.name; + /** 塔的英文名 */ + const towerName = core.firstData.name; - let [W, H] = [core.__SIZE__, core.__SIZE__]; - let [WIDTH, HEIGHT] = [core.__PIXELS__, core.__PIXELS__]; + 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.drawTip('接收失败!' + err.message); - core.playSound('error.mp3'); - } - }, - function (err) { - err = JSON.parse(err); - console.error(err); - core.drawTip('接收失败' + err?.message); - core.playSound('error.mp3'); - }, - null, null, null, 1000 - ); - } - - this.postComment = function (comment, tags) { - if (core.isReplaying()) return; - const isEmpty = /^\s*$/; - if (isEmpty.test(comment)){ - core.drawTip('您输入的消息为空,请重发!'); - core.playSound('error.mp3'); - return; - } - let form = new FormData(); - form.append('type', 2); - form.append('towername', towerName); - form.append('comment', comment); - form.append('tags', tags); - 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('提交成功!') + //#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'); - } else { - core.drawTip('提交失败!' + res?.message); + 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.drawTip('接收失败!' + err.message); core.playSound('error.mp3'); } - } - catch (err) { - core.drawTip('提交失败!' + err.message); + }, + function (err) { + err = JSON.parse(err); + console.error(err); + core.drawTip('接收失败' + err?.message); core.playSound('error.mp3'); - } - }, - function (err) { - err = JSON.parse(err); - console.error(err); - core.drawTip('提交失败!' + err?.message); + }, + null, null, null, 1000 + ); + } + + this.postComment = function (comment, tags) { + if (core.isReplaying()) return; + const isEmpty = /^\s*$/; + if (isEmpty.test(comment)) { + core.drawTip('您输入的消息为空,请重发!'); core.playSound('error.mp3'); - }, - null, null, null, 1000 - ); - } - //#endregion - - this.drawCommentSign = function () { - if (!core.getFlag('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.drawImage('sign', 'sign.png', 32 * x, 32 * y); - break; - } + return; } - } - } - - this.clearCommentSign = function () { - core.deleteCanvas('sign'); - } - - /** 返回从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; - } - - /** 生成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); - } - - /** 默认一次显示的弹幕数 */ - const showNum = 5; - - 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++) { - core.plugin.drawCommentStr(commentArr[i], WIDTH + 20 * Math.random(), - yList[i], Math.random() * 0.1 + 0.1); - } - } - - 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); - } - } - -}, - "setting": function () { - // 设置界面绘制 - // core.openSettings = ... - - const { ButtonBase, MenuBase, MenuPage } = this.MenuBase; - - class Setting { - /** - * @param {(ctx:string, x:number, y:number, w:number, h:number)=>void} draw - */ - constructor(name, effect, text, replay, draw) { - /** 获取选项界面显示的名称 */ - this.getName = name; - /** 执行该选项的效果 */ - this.effect = effect; - /** 该选项在框中的说明文字 */ - this.text = text; - /** 该选项是否计入录像 - * @type {boolean} - */ - this.replay = replay; - /** 除名称外的绘制内容 - * @type {(ctx:string, x:number, y:number, w:number, h:number)=>void} - */ - this.draw = draw; - } - } - - function invertFlag(name) { - core.setFlag(name, !core.getFlag(name, false)); - } - - const settingMap = new Map([ - ['autoGet', new Setting( - () => '自动拾取:' + (core.getFlag('autoGet', false) ? '开' : '关'), - () => invertFlag('autoGet'), - '每走一步,自动拾取当前层可获得的道具。', - true, - )], - ['autoBattle', new Setting( - () => '自动清怪:' + (core.getFlag('autoBattle', false) ? '开' : '关'), - () => invertFlag('autoBattle'), - '每走一步,自动和当前层可到达位置伤害为0的敌人战斗。对部分特殊敌人无效。', - true, - )], - ['potionRouting', new Setting( - () => '血瓶绕路:' + core.getFlag('__potionNoRouting__', false) ? '开' : '关', - () => invertFlag('__potionNoRouting__'), - '系统设置。开启后自动寻路时将绕过血瓶和绿宝石。', - true, - )], - ['clickMove', new Setting( - () => '单击瞬移:' + (core.getFlag('__noClickMove__', false) ? '开' : '关'), - () => invertFlag('__noClickMove__'), - '系统设置。单击即可触发瞬移。', - true, - )], - ['itemDetail', new Setting( - () => '物品显示数据:' + (core.getFlag('itemDetail', false) ? '开' : '关'), - () => invertFlag('itemDetail'), - '在地图上显示即捡即用道具和装备增加的属性值。', - true, - )], - ['zoomIn', new Setting( - () => ' < 放缩:' + Math.max(core.domStyle.scale, 1) + 'x', - () => core.actions._clickSwitchs_display_setSize(-1), - '放缩', - false, // 录像中不可录入任何DOM操作 - )], - ['zoomOut', new Setting( - () => ' > ', - () => core.actions._clickSwitchs_display_setSize(1), - '放缩', - false, - )], - ['HDCanvas', new Setting( - () => '高清画面:' + (core.flags.enableHDCanvas ? '开' : '关'), - core.actions._clickSwitchs_display_enableHDCanvas, - '高清画面', - false, - )], - ['enableEnemyPoint', new Setting( - () => '定点怪显:' + (core.flags.enableEnemyPoint ? '开' : '关'), - core.actions._clickSwitchs_display_enableEnemyPoint, - '怪物属性定点显示功能,即属性不同的怪物会在怪物手册单列。', - false, - )], - ['displayEnemyDamage', new Setting( - () => '怪物显伤:' + (core.flags.displayEnemyDamage ? '开' : '关'), - core.actions._clickSwitchs_display_enemyDamage, - '怪物显伤', - false, - )], - ['displayCritical', new Setting( - () => '临界显伤:' + (core.flags.displayCritical ? '开' : '关'), - core.actions._clickSwitchs_display_critical, - '临界显伤', - false, - )], - ['displayExtraDamage', new Setting( - () => '领域显伤:' + (core.flags.displayExtraDamage ? '开' : '关'), - core.actions._clickSwitchs_display_extraDamage, - '领域显伤', - false, - )], - ['extraDamageType', new Setting( - () => '领域模式:' + (core.flags.extraDamageType == 2 ? '[最简]' : core.flags.extraDamageType == 1 ? '[半透明]' : '[完整]'), - core.actions._clickSwitchs_display_extraDamageType, - '领域模式', - false, - )], - ['autoScale', new Setting( - () => '自动放缩:' + (core.getLocalStorage('autoScale') ? '开' : '关'), - () => core.setLocalStorage('autoScale', core.getLocalStorage('autoScale') ? false : true), - '自动放缩', - false, - )], - ['bgm', new Setting( - () => '音乐:' + (core.musicStatus.bgmStatus ? '开' : '关'), - core.actions._clickSwitchs_sounds_bgm, - '播放背景音乐', - false, - )], - ['se', new Setting( - () => '音效:' + (core.musicStatus.soundStatus ? '开' : '关'), - core.actions._clickSwitchs_sounds_se, - '播放音效', - false, - )], - ['decreaseVolume', new Setting( - () => " < 音量:" + Math.round(Math.sqrt(100 * core.musicStatus.userVolume)), - () => core.actions._clickSwitchs_sounds_userVolume(-1), - '减小音量', - false, - )], - ['increaseVolume', new Setting( - () => ' > ', - () => core.actions._clickSwitchs_sounds_userVolume(1), - '增大音量', - false, - )], - ['leftHand', new Setting( - () => '左手模式:' + (core.flags.leftHandPrefer ? '开' : '关'), - () => core.flags.leftHandPrefer = !core.flags.leftHandPrefer, - '系统设置。左手模式下WASD将用于移动角色,IJKL对应于原始的WASD进行存读档等操作。', - true, - )], - ['setHotKey', new Setting( - () => '', - function(num){ - core.utils.myprompt('输入物品名。名称(例如:破墙镐)或英文ID(例如:pickaxe)均可。', null, (value) => { - const itemInfo = core.material.items; - if (itemInfo) { - const aimItem = Object.values(itemInfo).find((item) => item.name === value || item.id === value); - if (aimItem) { - if (['constants','tools'].includes(aimItem.cls)) { - core.setFlag('hotkey' + num, aimItem.id); - this.menu.drawContent(); - } - else core.drawFailTip('错误:该类型的物品不支持快捷使用!'); - } - else core.drawFailTip('错误:找不到该名称的物品!'); - } - else core.drawFailTip('未知错误:core.material.items不存在!'); - }); - }, - '给选定的数字键绑定一个可快捷使用的物品。', - true, - function (ctx) { - const num = this.eventArgs[0]; - const item = core.getFlag('hotkey' + num, null); - let icon, itemName; - if (item && core.material.items.hasOwnProperty(item)) { - icon = item; - itemName = core.material.items.item.name; - } - else { - switch (num) { - case 1: - icon = 'pickaxe'; - itemName = '破墙镐'; - break; - case 2: - icon = 'bomb'; - itemName = '炸弹'; - break; - case 3: - icon = 'centerFly'; - itemName = '中心飞'; - break; - case 4: - itemName = '杂物'; - break; - case 5: - itemName = '回退一步'; - break; - case 6: - itemName = '撤销回退'; - break; - case 7: - itemName = '轻按'; - break; - } - } - let text = '\\i[btn' + num + ']: '; - if (icon) text += '\\i[' + icon + ']'; - text += itemName; - core.ui.drawTextContent(ctx, text, { - left: this.x, top: this.y + 2, maxWidth: 200, fontSize: 16, - }); - } - )], - ['clearHotKeys', new Setting( - () => '', - function () { - for (let i = 1; i <= 7; i++) { - core.setFlag('hotkey' + i, null); - } - this.menu.drawContent(); - core.drawSuccessTip('快捷键已重置到默认状态。') - }, - '重置本页面所有快捷键到默认状态。', - true, - function (ctx) { - core.fillRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #D3D3D3'); - core.strokeRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #888888'); - core.fillText(ctx, '重置', this.x + 5, this.y + this.h / 2 + 5, ' #333333', '16px Verdana'); - }, - )], - ['wallHacking', new Setting( - () => ' 穿墙:' + (core.hasFlag('debug_wallHacking') ? '开' : '关'), - () => { - core.setFlag('debug', true); - invertFlag('debug_wallHacking'); - }, - '开启时将始终穿墙并无视各种事件,无论是否按下Ctrl。', - false, - )], - ['debug_statusName', new Setting( - () => core.getFlag('debug_statusName', '??'), - function () { - const dictionary = { - '体力': 'hp', '血量': 'hp', '生命': 'hp', '血': 'hp', - '体力上限': 'hpmax', '血量上限': 'hpmax', '生命上限': 'hpmax', '血限': 'hpmax', - '攻击': 'atk', '攻': 'atk', '防御': 'def', '防': 'def', - '魔防': 'mdef', '护盾': 'mdef', 'mf': 'mdef', - '金币': 'money', '金钱': 'money', '钱': 'money', '经验': 'exp', - '魔力': 'mana', '蓝': 'mana', - } - core.utils.myprompt('输入要修改的属性名称', null, (value) => { - const heroStatus = core.status.hero; - if (dictionary.hasOwnProperty(value)) { - value = dictionary[value]; - } - if (heroStatus && heroStatus.hasOwnProperty(value) - && ['hp', 'hpmax', 'atk', 'def', 'mdef', 'money', 'exp', 'mana', 'manamax'].includes(value)) { - core.setFlag('debug_statusName', value); - this.menu.drawContent(); - } - else { - core.drawFailTip('错误:不合法的名称!'); - } - }); - }, - '', - false, - function (ctx) { - core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); - }, - )], - ['debug_statusValue', new Setting( - () => { - let value = core.getFlag('debug_statusValue', '??'); - if (typeof value === 'number') return core.formatBigNumber(value, 5); - else return value; - }, - function () { - core.utils.myprompt('输入要修改到的值', null, (value) => { - value = parseInt(value); - if (!Number.isNaN(value)) { - core.setFlag('debug_statusValue', value); - this.menu.drawContent(); - } - else { - core.drawFailTip('错误:不合法的值!'); - } - }); - }, - '', - false, - function (ctx) { - core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); - }, - )], - ['debug_setStatus', new Setting( - () => '', - () => { - const name = core.getFlag('debug_statusName'), - value = core.getFlag('debug_statusValue'); - if (!(name && core.status.hero && core.status.hero.hasOwnProperty(name))) { - core.drawFailTip('错误:不合法的名称!'); - return; - } - if (!Number.isInteger(value)) { - core.drawFailTip('错误:不合法的值!'); - return; - } - core.setFlag('debug', true); - core.setStatus(name, value); - core.updateStatusBar(); - core.drawSuccessTip('设置成功!'); - }, - '将角色状态设为相应值。', - false, - function (ctx) { - core.fillRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #D3D3D3'); - core.strokeRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #888888'); - core.fillText(ctx, '执行', this.x + 5, this.y + this.h / 2 + 5, ' #333333', '16px Verdana'); - }, - )], - ['debug_itemName', new Setting( - () => core.getFlag('debug_itemName', '??'), - function () { - core.utils.myprompt('输入要修改的物品名称', null, (value) => { - const itemInfo = core.material.items; - if (itemInfo) { - const aimItem = Object.values(itemInfo).find((item) => item.name === value || item.id === value); - if (aimItem) { - core.setFlag('debug_itemName', aimItem.id); - this.menu.drawContent(); - return; - } - } - core.drawFailTip('错误:不合法的名称!'); - }); - }, - '', - false, - function (ctx) { - core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); - }, - )], - ['debug_itemValue', new Setting( - () => core.getFlag('debug_itemValue', '??'), - function () { - core.setFlag('debug', true); - core.utils.myprompt('输入要修改到的值', null, (value) => { - value = parseInt(value); - if (!Number.isNaN(value)) { - core.setFlag('debug_itemValue', value); - this.menu.drawContent(); - } - else { - core.drawFailTip('错误:不合法的值!'); - } - }); - }, - '', - false, - function (ctx) { - core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); - }, - )], - ['debug_setItem', new Setting( - () => '', - () => { - const name = core.getFlag('debug_itemName'), - value = core.getFlag('debug_itemValue'); - const itemInfo = core.material.items; - - if (name && itemInfo) { - let itemExist = Object.values(itemInfo).some((item) => item.id === name); - if (!itemExist) { - core.drawFailTip('错误:不合法的名称!'); - return; - } - } - else { - core.drawFailTip('错误:不合法的名称!'); - return; - } - - if (!Number.isInteger(value)) { - core.drawFailTip('错误:不合法的值!'); - return; - } - core.setFlag('debug', true); - core.setItem(name, value); - core.updateStatusBar(); - core.drawSuccessTip('设置成功!'); - }, - '将道具数设为相应值。', - false, - function (ctx) { - core.fillRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #D3D3D3'); - core.strokeRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #888888'); - core.fillText(ctx, '执行', this.x + 5, this.y + this.h / 2 + 5, ' #333333', '16px Verdana'); - }, - )], - ['debug_flagName', new Setting( - () => core.getFlag('debug_flagName', '??'), - function () { - core.setFlag('debug', true); - core.utils.myprompt('输入要修改的变量名。注意:如果您不了解修改变量的后果,请勿尝试。', null, (value) => { - if (!value.startsWith('debug')) { - core.setFlag('debug_flagName', value); - this.menu.drawContent(); - } - else { - core.drawFailTip('错误:不合法的名称!'); - } - }); - }, - '', - false, - function (ctx) { - core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); - }, - )], - ['debug_flagValue', new Setting( - () => core.getFlag('debug_flagValue', '??'), - function () { - core.setFlag('debug', true); - core.utils.myprompt('输入要修改到的值。注意:如果您不了解修改变量的后果,请勿尝试。', null, (value) => { - let newValue; - try { - newValue = JSON.parse(value.trim()); - } - catch { - core.drawFailTip('错误:不合法的值,无法解析!'); - return; - } - core.setFlag('debug_flagValue', newValue); - this.menu.drawContent(); - }); - }, - '', - false, - function (ctx) { - core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); - }, - )], - ['debug_setFlag', new Setting( - () => '', - () => { - const name = core.getFlag('debug_flagName'), - value = core.getFlag('debug_flagValue'); - if (!name) { - core.drawFailTip('错误:不合法的变量名称!'); - return; - } - core.setFlag('debug', true); - core.setFlag(name, value); - core.updateStatusBar(); - core.drawSuccessTip('设置成功!'); - }, - '将变量设为相应值。', - false, - function (ctx) { - core.fillRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #D3D3D3'); - core.strokeRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #888888'); - core.fillText(ctx, '执行', this.x + 5, this.y + this.h / 2 + 5, ' #333333', '16px Verdana'); - }, - )], - ]) - - class SettingButton extends ButtonBase { - /** - * @param {unknown[]} eventArgs - */ - constructor(x, y, w, h, name, eventArgs) { - super(x, y, w, h); - this.name = name; - /** - * @type {Array} - */ - this.eventArgs = eventArgs || []; - /** - * @type {Setting} - */ - this.setting = settingMap.get(name); - this.draw = (ctx) => { - if (this.disable) return; - // 取消注释下面这一句将显示所有按钮的判定框 - // core.strokeRect(ctx, this.x, this.y, this.w, this.h, 'yellow'); - core.ui.fillText(ctx, this.setting.getName(), - this.x , this.y + this.h / 2 + 5, 'white', '16px Verdana'); - const drawFunc = this.setting.draw; - if (drawFunc) drawFunc.apply(this, [ctx]); - } - this.event = () => { - if (this.disable) return; - this.setting.effect.apply(this, eventArgs); - this.menu.drawContent(); - if (this.setting.replay) core.status.route.push('cSet:' + name); - } - } - } - - core.registerReplayAction('cSet', (action) => { - const strArr = action.split(':'); - if (strArr[0] !== 0) return false; - const btn = settingMap.get(strArr[1]); - btn.effect(); - core.status.route.push(action); - core.replay(); - return true; - }) - - function drawSetting(ctx) { - core.setAlpha(ctx, 0.85); - core.strokeRoundRect(ctx, 0, 0, core.__PIXELS__, core.__PIXELS__, 5, "#fff", 2); - core.fillRoundRect(ctx, 0, 0, core.__PIXELS__, core.__PIXELS__, 5, "gray"); - core.setAlpha(ctx, 1); - - // 绘制设置说明的文本框 - core.strokeRoundRect(ctx, 20, 70, core.__PIXELS__ - 40, 70, 3, "white"); - core.fillRoundRect(ctx, 21, 71, core.__PIXELS__ - 42, 68, 3, " #555555"); - - // 绘制设置的框体 - core.strokeRoundRect(ctx, 20, 150, core.__PIXELS__ - 40, 240, 3, "white"); - core.fillRoundRect(ctx, 21, 151, core.__PIXELS__ - 42, 238, 3, " #999999"); - - core.setTextAlign(ctx, 'center'); - core.ui.fillText(ctx, "设置", core.__PIXELS__ / 2, 25, 'white', '20px Verdana'); - } - - class ChoiceButton extends ButtonBase { - constructor(x, y, w, h, text, index) { - super(x, y, w, h); - this.index = index; - this.draw = (ctx) => { - core.setTextAlign(ctx, 'center'); - if (this.status === 'clicked') { - core.fillRoundRect(ctx, x, y, w, h, 3, ' #ADD8E6'); - core.strokeRoundRect(ctx, x, y, w, h, 3, ' #FFFF00'); - core.fillText(ctx, text, x + w / 2, y + h / 2 + 5, ' #555555', '16px Verdana'); + let form = new FormData(); + form.append('type', 2); + form.append('towername', towerName); + form.append('comment', comment); + form.append('tags', tags); + 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('提交成功!') + core.playSound('item.mp3'); } else { - core.fillRoundRect(ctx, x, y, w, h, 3, ' #D3D3D3'); - core.strokeRoundRect(ctx, x, y, w, h, 3, ' #888888'); - core.fillText(ctx, text, x + w / 2, y + h / 2 + 5, ' #333333', '16px Verdana'); + core.drawTip('提交失败!' + res?.message); + core.playSound('error.mp3'); } - }; + } + catch (err) { + core.drawTip('提交失败!' + err.message); + core.playSound('error.mp3'); + } + }, + function (err) { + err = JSON.parse(err); + console.error(err); + core.drawTip('提交失败!' + err?.message); + core.playSound('error.mp3'); + }, + null, null, null, 1000 + ); + } + //#endregion + + this.drawCommentSign = function () { + if (!core.getFlag('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.drawImage('sign', 'sign.png', 32 * x, 32 * y); + break; + } + } + } + } + + this.clearCommentSign = function () { + core.deleteCanvas('sign'); + } + + /** 返回从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; + } + + /** 生成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); + } + + /** 默认一次显示的弹幕数 */ + const showNum = 5; + + 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++) { + core.plugin.drawCommentStr(commentArr[i], WIDTH + 20 * Math.random(), + yList[i], Math.random() * 0.1 + 0.1); + } + } + + 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); + } + } + + }, + "setting": function () { + // 自绘设置界面 + // 抽象的不好,很后悔,还是功底太差 + + const { ButtonBase, MenuBase, MenuPage } = this.MenuBase; + + class Setting { + /** + * @param {(ctx:string, x:number, y:number, w:number, h:number)=>void} draw + */ + constructor(name, effect, text, replay, draw) { + /** 获取选项界面显示的名称 */ + this.getName = name; + /** 执行该选项的效果 */ + this.effect = effect; + /** 该选项在框中的说明文字 */ + this.text = text; + /** 该选项是否计入录像 + * @type {boolean} + */ + this.replay = replay; + /** 除名称外的绘制内容 + * @type {(ctx:string, x:number, y:number, w:number, h:number)=>void} + */ + this.draw = draw; + } + } + + function invertFlag(name) { + core.setFlag(name, !core.getFlag(name, false)); + } + + const settingMap = new Map([ + ['autoGet', new Setting( + () => '自动拾取:' + (core.getFlag('autoGet', false) ? '开' : '关'), + () => invertFlag('autoGet'), + '每走一步,自动拾取当前层可获得的道具。', + true, + )], + ['autoBattle', new Setting( + () => '自动清怪:' + (core.getFlag('autoBattle', false) ? '开' : '关'), + () => invertFlag('autoBattle'), + '每走一步,自动和当前层可到达位置伤害为0的敌人战斗。对部分特殊敌人无效。', + true, + )], + ['potionRouting', new Setting( + () => '血瓶绕路:' + core.getFlag('__potionNoRouting__', false) ? '开' : '关', + () => invertFlag('__potionNoRouting__'), + '系统设置。开启后自动寻路时将绕过血瓶和绿宝石。', + true, + )], + ['clickMove', new Setting( + () => '单击瞬移:' + (core.getFlag('__noClickMove__', false) ? '开' : '关'), + () => invertFlag('__noClickMove__'), + '系统设置。单击即可触发瞬移。', + true, + )], + ['itemDetail', new Setting( + () => '物品显示数据:' + (core.getFlag('itemDetail', false) ? '开' : '关'), + () => invertFlag('itemDetail'), + '在地图上显示即捡即用道具和装备增加的属性值。', + true, + )], + ['zoomIn', new Setting( + () => ' < 放缩:' + Math.max(core.domStyle.scale, 1) + 'x', + () => core.actions._clickSwitchs_display_setSize(-1), + '放缩', + false, // 录像中不可录入任何DOM操作 + )], + ['zoomOut', new Setting( + () => ' > ', + () => core.actions._clickSwitchs_display_setSize(1), + '放缩', + false, + )], + ['HDCanvas', new Setting( + () => '高清画面:' + (core.flags.enableHDCanvas ? '开' : '关'), + core.actions._clickSwitchs_display_enableHDCanvas, + '高清画面', + false, + )], + ['enableEnemyPoint', new Setting( + () => '定点怪显:' + (core.flags.enableEnemyPoint ? '开' : '关'), + core.actions._clickSwitchs_display_enableEnemyPoint, + '怪物属性定点显示功能,即属性不同的怪物会在怪物手册单列。', + false, + )], + ['displayEnemyDamage', new Setting( + () => '怪物显伤:' + (core.flags.displayEnemyDamage ? '开' : '关'), + core.actions._clickSwitchs_display_enemyDamage, + '怪物显伤', + false, + )], + ['displayCritical', new Setting( + () => '临界显伤:' + (core.flags.displayCritical ? '开' : '关'), + core.actions._clickSwitchs_display_critical, + '临界显伤', + false, + )], + ['displayExtraDamage', new Setting( + () => '领域显伤:' + (core.flags.displayExtraDamage ? '开' : '关'), + core.actions._clickSwitchs_display_extraDamage, + '领域显伤', + false, + )], + ['extraDamageType', new Setting( + () => '领域模式:' + (core.flags.extraDamageType == 2 ? '[最简]' : core.flags.extraDamageType == 1 ? '[半透明]' : '[完整]'), + core.actions._clickSwitchs_display_extraDamageType, + '领域模式', + false, + )], + ['autoScale', new Setting( + () => '自动放缩:' + (core.getLocalStorage('autoScale') ? '开' : '关'), + () => core.setLocalStorage('autoScale', core.getLocalStorage('autoScale') ? false : true), + '自动放缩', + false, + )], + ['bgm', new Setting( + () => '音乐:' + (core.musicStatus.bgmStatus ? '开' : '关'), + core.actions._clickSwitchs_sounds_bgm, + '播放背景音乐', + false, + )], + ['se', new Setting( + () => '音效:' + (core.musicStatus.soundStatus ? '开' : '关'), + core.actions._clickSwitchs_sounds_se, + '播放音效', + false, + )], + ['decreaseVolume', new Setting( + () => " < 音量:" + Math.round(Math.sqrt(100 * core.musicStatus.userVolume)), + () => core.actions._clickSwitchs_sounds_userVolume(-1), + '减小音量', + false, + )], + ['increaseVolume', new Setting( + () => ' > ', + () => core.actions._clickSwitchs_sounds_userVolume(1), + '增大音量', + false, + )], + ['leftHand', new Setting( + () => '左手模式:' + (core.flags.leftHandPrefer ? '开' : '关'), + () => core.flags.leftHandPrefer = !core.flags.leftHandPrefer, + '系统设置。左手模式下WASD将用于移动角色,IJKL对应于原始的WASD进行存读档等操作。', + true, + )], + ['setHotKey', new Setting( + () => '', + function (num) { + core.utils.myprompt('输入物品名。名称(例如:破墙镐)或英文ID(例如:pickaxe)均可。', null, (value) => { + const itemInfo = core.material.items; + if (itemInfo) { + const aimItem = Object.values(itemInfo).find((item) => item.name === value || item.id === value); + if (aimItem) { + if (['constants', 'tools'].includes(aimItem.cls)) { + core.setFlag('hotkey' + num, aimItem.id); + this.menu.drawContent(); + } + else core.drawFailTip('错误:该类型的物品不支持快捷使用!'); + } + else core.drawFailTip('错误:找不到该名称的物品!'); + } + else core.drawFailTip('未知错误:core.material.items不存在!'); + }); + }, + '给选定的数字键绑定一个可快捷使用的物品。', + true, + function (ctx) { + const num = this.eventArgs[0]; + const item = core.getFlag('hotkey' + num, null); + let icon, itemName; + if (item && core.material.items.hasOwnProperty(item)) { + icon = item; + itemName = core.material.items[item].name; + } + else { + switch (num) { + case 1: + icon = 'pickaxe'; + itemName = '破墙镐'; + break; + case 2: + icon = 'bomb'; + itemName = '炸弹'; + break; + case 3: + icon = 'centerFly'; + itemName = '中心飞'; + break; + case 4: + itemName = '杂物'; + break; + case 5: + itemName = '回退一步'; + break; + case 6: + itemName = '撤销回退'; + break; + case 7: + itemName = '轻按'; + break; + } + } + let text = '\\i[btn' + num + ']: '; + if (icon) text += '\\i[' + icon + ']'; + text += itemName; + core.ui.drawTextContent(ctx, text, { + left: this.x, top: this.y + 2, maxWidth: 200, fontSize: 16, + }); + } + )], + ['clearHotKeys', new Setting( + () => '', + function () { + for (let i = 1; i <= 7; i++) { + core.setFlag('hotkey' + i, null); + } + this.menu.drawContent(); + core.drawSuccessTip('快捷键已重置到默认状态。') + }, + '重置本页面所有快捷键到默认状态。', + true, + function (ctx) { + core.fillRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #D3D3D3'); + core.strokeRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #888888'); + core.fillText(ctx, '重置', this.x + 5, this.y + this.h / 2 + 5, ' #333333', '16px Verdana'); + }, + )], + ['wallHacking', new Setting( + () => ' 穿墙:' + (core.hasFlag('debug_wallHacking') ? '开' : '关'), + () => { + core.setFlag('debug', true); + invertFlag('debug_wallHacking'); + }, + '开启时将始终穿墙并无视各种事件,无论是否按下Ctrl。', + false, + )], + ['debug_statusName', new Setting( + () => core.getFlag('debug_statusName', '??'), + function () { + const dictionary = { + '体力': 'hp', '血量': 'hp', '生命': 'hp', '血': 'hp', + '体力上限': 'hpmax', '血量上限': 'hpmax', '生命上限': 'hpmax', '血限': 'hpmax', + '攻击': 'atk', '攻': 'atk', '防御': 'def', '防': 'def', + '魔防': 'mdef', '护盾': 'mdef', 'mf': 'mdef', + '金币': 'money', '金钱': 'money', '钱': 'money', '经验': 'exp', + '魔力': 'mana', '蓝': 'mana', + } + core.utils.myprompt('输入要修改的属性名称', null, (value) => { + const heroStatus = core.status.hero; + if (dictionary.hasOwnProperty(value)) { + value = dictionary[value]; + } + if (heroStatus && heroStatus.hasOwnProperty(value) + && ['hp', 'hpmax', 'atk', 'def', 'mdef', 'money', 'exp', 'mana', 'manamax'].includes(value)) { + core.setFlag('debug_statusName', value); + this.menu.drawContent(); + } + else { + core.drawFailTip('错误:不合法的名称!'); + } + }); + }, + '', + false, + function (ctx) { + core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); + }, + )], + ['debug_statusValue', new Setting( + () => { + let value = core.getFlag('debug_statusValue', '??'); + if (typeof value === 'number') return core.formatBigNumber(value, 5); + else return value; + }, + function () { + core.utils.myprompt('输入要修改到的值', null, (value) => { + value = parseInt(value); + if (!Number.isNaN(value)) { + core.setFlag('debug_statusValue', value); + this.menu.drawContent(); + } + else { + core.drawFailTip('错误:不合法的值!'); + } + }); + }, + '', + false, + function (ctx) { + core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); + }, + )], + ['debug_setStatus', new Setting( + () => '', + () => { + const name = core.getFlag('debug_statusName'), + value = core.getFlag('debug_statusValue'); + if (!(name && core.status.hero && core.status.hero.hasOwnProperty(name))) { + core.drawFailTip('错误:不合法的名称!'); + return; + } + if (!Number.isInteger(value)) { + core.drawFailTip('错误:不合法的值!'); + return; + } + core.setFlag('debug', true); + core.setStatus(name, value); + core.updateStatusBar(); + core.drawSuccessTip('设置成功!'); + }, + '将角色状态设为相应值。', + false, + function (ctx) { + core.fillRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #D3D3D3'); + core.strokeRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #888888'); + core.fillText(ctx, '执行', this.x + 5, this.y + this.h / 2 + 5, ' #333333', '16px Verdana'); + }, + )], + ['debug_itemName', new Setting( + () => core.getFlag('debug_itemName', '??'), + function () { + core.utils.myprompt('输入要修改的物品名称', null, (value) => { + const itemInfo = core.material.items; + if (itemInfo) { + const aimItem = Object.values(itemInfo).find((item) => item.name === value || item.id === value); + if (aimItem) { + core.setFlag('debug_itemName', aimItem.id); + this.menu.drawContent(); + return; + } + } + core.drawFailTip('错误:不合法的名称!'); + }); + }, + '', + false, + function (ctx) { + core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); + }, + )], + ['debug_itemValue', new Setting( + () => core.getFlag('debug_itemValue', '??'), + function () { + core.setFlag('debug', true); + core.utils.myprompt('输入要修改到的值', null, (value) => { + value = parseInt(value); + if (!Number.isNaN(value)) { + core.setFlag('debug_itemValue', value); + this.menu.drawContent(); + } + else { + core.drawFailTip('错误:不合法的值!'); + } + }); + }, + '', + false, + function (ctx) { + core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); + }, + )], + ['debug_setItem', new Setting( + () => '', + () => { + const name = core.getFlag('debug_itemName'), + value = core.getFlag('debug_itemValue'); + const itemInfo = core.material.items; + + if (name && itemInfo) { + let itemExist = Object.values(itemInfo).some((item) => item.id === name); + if (!itemExist) { + core.drawFailTip('错误:不合法的名称!'); + return; + } + } + else { + core.drawFailTip('错误:不合法的名称!'); + return; + } + + if (!Number.isInteger(value)) { + core.drawFailTip('错误:不合法的值!'); + return; + } + core.setFlag('debug', true); + core.setItem(name, value); + core.updateStatusBar(); + core.drawSuccessTip('设置成功!'); + }, + '将道具数设为相应值。', + false, + function (ctx) { + core.fillRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #D3D3D3'); + core.strokeRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #888888'); + core.fillText(ctx, '执行', this.x + 5, this.y + this.h / 2 + 5, ' #333333', '16px Verdana'); + }, + )], + ['debug_flagName', new Setting( + () => core.getFlag('debug_flagName', '??'), + function () { + core.setFlag('debug', true); + core.utils.myprompt('输入要修改的变量名。注意:如果您不了解修改变量的后果,请勿尝试。', null, (value) => { + if (!value.startsWith('debug')) { + core.setFlag('debug_flagName', value); + this.menu.drawContent(); + } + else { + core.drawFailTip('错误:不合法的名称!'); + } + }); + }, + '', + false, + function (ctx) { + core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); + }, + )], + ['debug_flagValue', new Setting( + () => core.getFlag('debug_flagValue', '??'), + function () { + core.setFlag('debug', true); + core.utils.myprompt('输入要修改到的值。注意:如果您不了解修改变量的后果,请勿尝试。', null, (value) => { + let newValue; + try { + newValue = JSON.parse(value.trim()); + } + catch { + core.drawFailTip('错误:不合法的值,无法解析!'); + return; + } + core.setFlag('debug_flagValue', newValue); + this.menu.drawContent(); + }); + }, + '', + false, + function (ctx) { + core.strokeRect(ctx, this.x, this.y, this.w, this.h, ' #708090'); + }, + )], + ['debug_setFlag', new Setting( + () => '', + () => { + const name = core.getFlag('debug_flagName'), + value = core.getFlag('debug_flagValue'); + if (!name) { + core.drawFailTip('错误:不合法的变量名称!'); + return; + } + core.setFlag('debug', true); + core.setFlag(name, value); + core.updateStatusBar(); + core.drawSuccessTip('设置成功!'); + }, + '将变量设为相应值。', + false, + function (ctx) { + core.fillRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #D3D3D3'); + core.strokeRoundRect(ctx, this.x, this.y, this.w, this.h, 3, ' #888888'); + core.fillText(ctx, '执行', this.x + 5, this.y + this.h / 2 + 5, ' #333333', '16px Verdana'); + }, + )], + ]) + + class SettingButton extends ButtonBase { + /** + * @param {unknown[]} eventArgs + */ + constructor(x, y, w, h, name, eventArgs) { + super(x, y, w, h); + this.name = name; + /** + * @type {Array} + */ + this.eventArgs = eventArgs || []; + /** + * @type {Setting} + */ + this.setting = settingMap.get(name); + this.draw = (ctx) => { + if (this.disable) return; + // 取消注释下面这一句将显示所有按钮的判定框 + // core.strokeRect(ctx, this.x, this.y, this.w, this.h, 'yellow'); + core.ui.fillText(ctx, this.setting.getName(), + this.x, this.y + this.h / 2 + 5, 'white', '16px Verdana'); + const drawFunc = this.setting.draw; + if (drawFunc) drawFunc.apply(this, [ctx]); + } + this.event = () => { + if (this.disable) return; + this.setting.effect.apply(this, eventArgs); + this.menu.drawContent(); + if (this.setting.replay) core.status.route.push('cSet:' + name); + } + } + } + + core.registerReplayAction('cSet', (action) => { + const strArr = action.split(':'); + if (strArr[0] !== 0) return false; + const btn = settingMap.get(strArr[1]); + btn.effect(); + core.status.route.push(action); + core.replay(); + return true; + }) + + function drawSetting(ctx) { + core.setAlpha(ctx, 0.85); + core.strokeRoundRect(ctx, 0, 0, core.__PIXELS__, core.__PIXELS__, 5, "#fff", 2); + core.fillRoundRect(ctx, 0, 0, core.__PIXELS__, core.__PIXELS__, 5, "gray"); + core.setAlpha(ctx, 1); + + // 绘制设置说明的文本框 + core.strokeRoundRect(ctx, 20, 70, core.__PIXELS__ - 40, 70, 3, "white"); + core.fillRoundRect(ctx, 21, 71, core.__PIXELS__ - 42, 68, 3, " #555555"); + + // 绘制设置的框体 + core.strokeRoundRect(ctx, 20, 150, core.__PIXELS__ - 40, 240, 3, "white"); + core.fillRoundRect(ctx, 21, 151, core.__PIXELS__ - 42, 238, 3, " #999999"); + + core.setTextAlign(ctx, 'center'); + core.ui.fillText(ctx, "设置", core.__PIXELS__ / 2, 25, 'white', '20px Verdana'); + } + + class ChoiceButton extends ButtonBase { + constructor(x, y, w, h, text, index) { + super(x, y, w, h); + this.index = index; + this.draw = (ctx) => { + core.setTextAlign(ctx, 'center'); + if (this.status === 'clicked') { + core.fillRoundRect(ctx, x, y, w, h, 3, ' #ADD8E6'); + core.strokeRoundRect(ctx, x, y, w, h, 3, ' #FFFF00'); + core.fillText(ctx, text, x + w / 2, y + h / 2 + 5, ' #555555', '16px Verdana'); + } else { + core.fillRoundRect(ctx, x, y, w, h, 3, ' #D3D3D3'); + core.strokeRoundRect(ctx, x, y, w, h, 3, ' #888888'); + core.fillText(ctx, text, x + w / 2, y + h / 2 + 5, ' #333333', '16px Verdana'); + } + }; + } + } + + class SettingOnePage extends MenuBase { + constructor(name) { + super(name); + this.text = ''; + this.selectedPos; + this.selectedBtn; + this.clickEvent = (x, y, px, py) => { + this.btnList.forEach((btn, pos) => { + if (btn.disable) return; + if (px >= btn.x && px <= btn.x + btn.w && py > btn.y && py <= btn.y + btn.h) { + if (this.selectedBtn === btn) btn.event(x, y, px, py); + else { + this.focus(btn, pos); + } + } + }); + } + this.keyEvent = (keyCode) => { + let x, y; + const changePos = (newPos) => { + if (this.btnList.has(newPos)) { + const button = this.btnList.get(newPos); + this.focus(button, newPos); + } + } + if ([37, 38, 39, 40].includes(keyCode)) { + if (!this.selectedBtn) { + const button = this.btnList.get('1,1'); + if (button) this.focus(button, '1,1'); + return; + } + else { + [x, y] = this.selectedPos.split(',').map((x) => parseInt(x)); + if (keyCode === 37) x--; + if (keyCode === 38) y--; + if (keyCode === 39) x++; + if (keyCode === 40) y++; + let newPos = x + ',' + y; + + // 逻辑:左右,查找不到对应坐标就不动。 + // 上下,查找不到对应坐标,只要该列存在第一个元素,就会移到该列。 + + if (keyCode === 37 || keyCode === 39) { + changePos(newPos); + } + if (keyCode === 38 || keyCode === 40) { + if (this.btnList.has(newPos)) { + const button = this.btnList.get(newPos); + this.focus(button, newPos); + } + else { + newPos = '1,' + y; + changePos(newPos); + } + } + } + } + else { + switch (keyCode) { + case 13: + case 32: // Enter/Space + if (this.selectedBtn) this.selectedBtn.event(); + break; + } + } } } - class SettingOnePage extends MenuBase { - constructor(name) { - super(name); - this.text = ''; - this.selectedBtn; - this.clickEvent = (x, y, px, py) => { - this.btnList.forEach((btn) => { - if (btn.disable) return; - if (px >= btn.x && px <= btn.x + btn.w && py > btn.y && py <= btn.y + btn.h) { - if (this.selectedBtn === btn) btn.event(x, y, px, py); - else { - this.selectedBtn = btn; - this.text = btn.setting.text; - this.drawEventSelector(); - this.drawContent(); - } - } - }); - } - } + focus(button, pos) { + this.selectedPos = pos; + this.selectedBtn = button; + this.text = button.setting.text; + this.drawEventSelector(); + this.drawContent(); + } - drawEventSelector() { - if (core.isset(this.selectedBtn)) { - core.drawUIEventSelector(0, "winskin.png", this.selectedBtn.x, this.selectedBtn.y, - this.selectedBtn.w, this.selectedBtn.h, 137); - } + drawEventSelector() { + if (core.isset(this.selectedBtn)) { + core.drawUIEventSelector(0, "winskin.png", this.selectedBtn.x, this.selectedBtn.y, + this.selectedBtn.w, this.selectedBtn.h, 137); } + } - drawContent() { - super.drawContent(); - if (this.text && this.text.length > 0) { - core.ui.drawTextContent(this.name, this.text, { - left: 30, top: 78, bold: false, color: "white", + drawContent() { + super.drawContent(); + if (this.text && this.text.length > 0) { + core.ui.drawTextContent(this.name, this.text, { + left: 30, top: 78, bold: false, color: "white", + align: "left", fontSize: 14, maxWidth: 350 + }); + } + switch (this.name) { + case 'gamePlay': + core.fillText(this.name, '-- 自动 --', 40, 175, ' #FFE4B5', '18px Verdana'); + core.fillText(this.name, '-- 瞬移 --', 40, 225, ' #FFE4B5', '18px Verdana'); + break; + case 'gameView': + core.fillText(this.name, '-- 显示 --', 40, 175, ' #FFE4B5', '18px Verdana'); + core.fillText(this.name, '-- 音效 --', 40, 295, ' #FFE4B5', '18px Verdana'); + break; + case 'key': + core.fillText(this.name, '-- 快捷键设置 --', 40, 205, ' #FFE4B5', '18px Verdana'); + break; + case 'console': + const consoleWarnText = + "本页面的功能仅供调试用。使用后相应存档将变红,录像不能通过,且无法提交。请读档到普通存档后正常游玩方可提交。"; + core.ui.drawTextContent(this.name, consoleWarnText, { + left: 30, top: 158, bold: false, color: " #FFC0CB", align: "left", fontSize: 14, maxWidth: 350 }); - } - switch (this.name) { - case 'gamePlay': - core.fillText(this.name, '-- 自动 --', 40, 175, ' #FFE4B5', '18px Verdana'); - core.fillText(this.name, '-- 瞬移 --', 40, 225, ' #FFE4B5', '18px Verdana'); - break; - case 'gameView': - core.fillText(this.name, '-- 显示 --', 40, 175, ' #FFE4B5', '18px Verdana'); - core.fillText(this.name, '-- 音效 --', 40, 295, ' #FFE4B5', '18px Verdana'); - break; - case 'key': - core.fillText(this.name, '-- 快捷键设置 --', 40, 205, ' #FFE4B5', '18px Verdana'); - break; - case 'console': - const consoleWarnText = - "本页面的功能仅供调试用。使用后相应存档将变红,录像不能通过,且无法提交。请读档到普通存档后正常游玩方可提交。"; - core.ui.drawTextContent(this.name, consoleWarnText, { - left: 30, top: 158, bold: false, color: " #FFC0CB", - align: "left", fontSize: 14, maxWidth: 350 - }); - core.fillText(this.name, "属性", 45, 264, 'white', '16px Verdana'); - core.fillText(this.name, "设为", 170, 264, 'white', '16px Verdana'); - core.fillText(this.name, "物品", 45, 290, 'white', '16px Verdana'); - core.fillText(this.name, "数量设为", 170, 290, 'white', '16px Verdana'); - core.fillText(this.name, "变量", 45, 316, 'white', '16px Verdana'); - core.fillText(this.name, "设为", 170, 316, 'white', '16px Verdana'); - break; - } - this.drawEventSelector(); - } - - clear() { - core.clearUIEventSelector(0); - super.clear(); + core.fillText(this.name, "属性", 45, 264, 'white', '16px Verdana'); + core.fillText(this.name, "设为", 170, 264, 'white', '16px Verdana'); + core.fillText(this.name, "物品", 45, 290, 'white', '16px Verdana'); + core.fillText(this.name, "数量设为", 170, 290, 'white', '16px Verdana'); + core.fillText(this.name, "变量", 45, 316, 'white', '16px Verdana'); + core.fillText(this.name, "设为", 170, 316, 'white', '16px Verdana'); + break; } + this.drawEventSelector(); } - class SettingMenu extends MenuPage { - constructor(pageList, currPage, name) { - super(pageList, currPage, name); - } - - drawContent() { - core.createCanvas(this.name, 0, 0, core.__PIXELS__, core.__PIXELS__, 136); - drawSetting(this.name); - super.drawButtonContent(); - this.initOnePage(); - } + clear() { + core.clearUIEventSelector(0); + super.clear(); } + } - class TextButton extends ButtonBase { - constructor(x, y, w, h, text) { - super(x, y, w, h); - this.draw = (ctx) => { - if (this.disable) return; - core.ui.fillText(ctx, text, - this.x + this.w / 2, this.y + this.h / 2 + 5, 'white', '16px Verdana'); + class SettingMenu extends MenuPage { + constructor(pageList, currPage, name) { + super(pageList, currPage, name); + this.keyEvent = (keyCode) => { + if (keyCode === 33) this.pageUp(); + if (keyCode === 34) this.pageDown(); + if ([33, 34].includes(keyCode)) { + const btn = this.btnList.get(this.currPage); + this.changeBtn(btn); + this.drawContent(); + } + if (keyCode === 27) { + this.btnList.get('quit').event(); } } } + initBtnList(arr) { + super.initBtnList(arr); + this.btnList.forEach((btn) => { + if (btn instanceof ChoiceButton) { + btn.event = function () { + this.menu.changePage(this.index); + this.menu.changeBtn(this); + this.menu.drawContent(); + } + } + }); + } + + drawContent() { + core.createCanvas(this.name, 0, 0, core.__PIXELS__, core.__PIXELS__, 136); + drawSetting(this.name); + super.drawButtonContent(); + this.initOnePage(); + } + + changeBtn(aimBtn) { + this.btnList.forEach((btn) => { + if (btn instanceof ChoiceButton) { + btn.status = aimBtn === btn ? 'clicked' : 'pending'; + } + }) + } + } + + class TextButton extends ButtonBase { + constructor(x, y, w, h, text) { + super(x, y, w, h); + this.draw = (ctx) => { + if (this.disable) return; + core.ui.fillText(ctx, text, + this.x + this.w / 2, this.y + this.h / 2 + 5, 'white', '16px Verdana'); + } + } + } + this.openSetting = function () { if (core.isReplaying()) return; core.lockControl(); @@ -4120,53 +4211,53 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = // 每个页面的按钮 const gamePlayMenu = new SettingOnePage('gamePlay'); gamePlayMenu.initBtnList([ - ['autoGet', new SettingButton(40, 180, 150, 30, 'autoGet')], - ['autoBattle', new SettingButton(220, 180, 150, 30, 'autoBattle')], - ['clickMove', new SettingButton(40, 230, 150, 30, 'clickMove')] + ['1,1', new SettingButton(40, 180, 150, 30, 'autoGet')], + ['2,1', new SettingButton(220, 180, 150, 30, 'autoBattle')], + ['1,2', new SettingButton(40, 230, 150, 30, 'clickMove')] ]); const gameViewMenu = new SettingOnePage('gameView'); gameViewMenu.initBtnList([ - ['itemDetail', new SettingButton(40, 180, 150, 25, 'itemDetail')], - ['HDCanvas', new SettingButton(40, 205, 150, 25, 'HDCanvas')], - ['displayEnemyDamage', new SettingButton(40, 230, 150, 25, 'displayEnemyDamage')], - ['displayExtraDamage', new SettingButton(40, 255, 150, 25, 'displayExtraDamage')], - ['bgm', new SettingButton(40, 300, 150, 25, 'bgm')], - ['decreaseVolume', new SettingButton(40, 325, 25, 25, 'decreaseVolume')], - ['increaseVolume', new SettingButton(140, 325, 25, 25, 'increaseVolume')], + ['1,1', new SettingButton(40, 180, 150, 25, 'itemDetail')], + ['1,2', new SettingButton(40, 205, 150, 25, 'HDCanvas')], + ['1,3', new SettingButton(40, 230, 150, 25, 'displayEnemyDamage')], + ['1,4', new SettingButton(40, 255, 150, 25, 'displayExtraDamage')], + ['1,5', new SettingButton(40, 300, 150, 25, 'bgm')], + ['1,6', new SettingButton(40, 325, 25, 25, 'decreaseVolume')], + ['2,6', new SettingButton(140, 325, 25, 25, 'increaseVolume')], - ['zoomIn', new SettingButton(220, 180, 25, 25, 'zoomIn')], - ['zoomOut', new SettingButton(320, 180, 25, 25, 'zoomOut')], - ['enableEnemyPoint', new SettingButton(220, 205, 150, 25, 'enableEnemyPoint')], - ['displayCritical', new SettingButton(220, 230, 150, 25, 'displayCritical')], - ['se', new SettingButton(220, 300, 150, 25, 'se')], + ['2,1', new SettingButton(220, 180, 25, 25, 'zoomIn')], + ['3,1', new SettingButton(320, 180, 25, 25, 'zoomOut')], + ['2,2', new SettingButton(220, 205, 150, 25, 'enableEnemyPoint')], + ['2,3', new SettingButton(220, 230, 150, 25, 'displayCritical')], + ['2,5', new SettingButton(220, 300, 150, 25, 'se')], ]); - + const keyMenu = new SettingOnePage('key'); keyMenu.initBtnList([ - ['leftHand', new SettingButton(40, 160, 150, 25, 'leftHand')], - ['hotKey1', new SettingButton(40, 220, 150, 25, 'setHotKey', [1])], - ['hotKey2', new SettingButton(220, 220, 150, 25, 'setHotKey', [2])], - ['hotKey3', new SettingButton(40, 250, 150, 25, 'setHotKey', [3])], - ['hotKey4', new SettingButton(220, 250, 150, 25, 'setHotKey', [4])], - ['hotKey5', new SettingButton(40, 280, 150, 25, 'setHotKey', [5])], - ['hotKey6', new SettingButton(220, 280, 150, 25, 'setHotKey', [6])], - ['hotKey7', new SettingButton(40, 310, 150, 25, 'setHotKey', [7])], - ['clearHotKeys', new SettingButton(300, 350, 42, 25, 'clearHotKeys')], + ['1,1', new SettingButton(40, 160, 150, 25, 'leftHand')], + ['1,2', new SettingButton(40, 220, 150, 25, 'setHotKey', [1])], + ['2,2', new SettingButton(220, 220, 150, 25, 'setHotKey', [2])], + ['1,3', new SettingButton(40, 250, 150, 25, 'setHotKey', [3])], + ['2,3', new SettingButton(220, 250, 150, 25, 'setHotKey', [4])], + ['1,4', new SettingButton(40, 280, 150, 25, 'setHotKey', [5])], + ['2,4', new SettingButton(220, 280, 150, 25, 'setHotKey', [6])], + ['1,5', new SettingButton(40, 310, 150, 25, 'setHotKey', [7])], + ['1,6', new SettingButton(300, 350, 42, 25, 'clearHotKeys')], ]); const consoleMenu = new SettingOnePage('console'); consoleMenu.initBtnList([ - ['wallHacking', new SettingButton(40, 220, 150, 25, 'wallHacking')], - ['debug_statusName', new SettingButton(80, 250, 80, 20, 'debug_statusName')], - ['debug_statusValue', new SettingButton(210, 250, 80, 20, 'debug_statusValue')], - ['debug_setStatus', new SettingButton(340, 250, 40, 20, 'debug_setStatus')], - ['debug_itemName', new SettingButton(80, 276, 80, 20, 'debug_itemName')], - ['debug_itemValue', new SettingButton(240, 276, 80, 20, 'debug_itemValue')], - ['debug_setItem', new SettingButton(340, 276, 40, 20, 'debug_setItem')], - ['debug_flagName', new SettingButton(80, 302, 80, 20, 'debug_flagName')], - ['debug_flagValue', new SettingButton(210, 302, 80, 20, 'debug_flagValue')], - ['debug_setFlag', new SettingButton(340, 302, 40, 20, 'debug_setFlag')], + ['1,1', new SettingButton(40, 220, 150, 25, 'wallHacking')], + ['1,2', new SettingButton(80, 250, 80, 20, 'debug_statusName')], + ['2,2', new SettingButton(210, 250, 80, 20, 'debug_statusValue')], + ['3,2', new SettingButton(340, 250, 40, 20, 'debug_setStatus')], + ['1,3', new SettingButton(80, 276, 80, 20, 'debug_itemName')], + ['2,3', new SettingButton(240, 276, 80, 20, 'debug_itemValue')], + ['3,3', new SettingButton(340, 276, 40, 20, 'debug_setItem')], + ['1,4', new SettingButton(80, 302, 80, 20, 'debug_flagName')], + ['2,4', new SettingButton(210, 302, 80, 20, 'debug_flagValue')], + ['3,4', new SettingButton(340, 302, 40, 20, 'debug_setFlag')], ]); // 在此处添加新的菜单页面 @@ -4178,37 +4269,30 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = keyBtn = new ChoiceButton(152, 40, 46, 24, '按键', 2), consoleBtn = new ChoiceButton(212, 40, 66, 24, '控制台', 3); const quit = new TextButton(360, 10, 45, 25, '[退出]'); - quit.redraw = false; quit.event = () => { settingMenu.clear(); setTimeout(core.unlockControl, 0); // 消抖,防止点击关闭按钮的一瞬间触发瞬移。 } - settingMenu.btnList = new Map([['gamePlayBtn', gamePlayBtn], ['gameViewBtn', gameViewBtn], - ['keyBtn', keyBtn], ['consoleBtn', consoleBtn], + settingMenu.initBtnList([[0, gamePlayBtn], [1, gameViewBtn], + [2, keyBtn], [3, consoleBtn], ['quit', quit]]); - function changeBtn(aimBtn) { - settingMenu.btnList.forEach((btn) => { - if (btn instanceof ChoiceButton) { - btn.status = aimBtn === btn ? 'clicked' : 'pending'; - } - }) - } - settingMenu.btnList.forEach((btn) => { - if (btn instanceof ChoiceButton) { - btn.event = function () { - changeBtn(this); - settingMenu.changePage(this.index); - } - } - }); // 设置初始时选中的按键为第一个按键 - changeBtn(gamePlayBtn); + settingMenu.changeBtn(gamePlayBtn); settingMenu.init(); } + // todolist 自定义设置界面添加键盘支持 + // todolist 剧情全skip功能 文字-文字+演出(跳跃) // todolist 批量使用:您当前选定了:xxx。请勿选定不适合批量使用的道具,请勿输入过大的数字。 + // todolist 道具栏分页,可设定隐藏的道具,及自动查看显隐藏 + // todolist 内置ATRI 解决连通性问题 + // todolist 血瓶宝石显示数据 解决浏览地图和楼传显示错误 引入自动配置值的块 + // todolist 存读档过程保存图块连通性(可选) + // todolist 清怪检测,重开杖,吸噬 + // todolist 修复已知的插件bug + // todolist 添加鸽窝样板的快速读取撤回 和 优化美工 } } \ No newline at end of file