var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { "init": function () { this._afterLoadResources = function () { // 本函数将在所有资源加载完毕后,游戏开启前被执行 core.ui.statusBar.init(); core.registerEvent("setanimate", function (data) { data.px = data.px ?? 0; data.py = data.py ?? 0; core.setanimate( data.name, data.px, data.py, data.width, data.height, data.allFarme, data.imageList, data.soundList ); core.doAction(); }); core.registerEvent("animatemove", function (data) { core.animatemove(data.id, data.px, data.py, data.relative, data.time, data.style); core.doAction(); }); core.registerEvent("moveAnimate", function (data) { core.maps.moveAnimate(data.id, data.px, data.py, data.relative, data.time, data.style); core.doAction(); }); core.registerEvent("animateloop", function (data) { core.animateloop(data.id, data.loop); core.doAction(); }); core.registerEvent("animatereverse", function (data) { core.animatereverse(data.id, data.reverse); core.doAction(); }); core.registerEvent("animatepause", function (data) { core.animatepause(data.id, data.pause); core.doAction(); }); core.registerEvent("clearanimate", function (data) { core.animateclear(data.id); core.doAction(); }); core.registerEvent("deleteanimate", function (data) { core.deleteanimate(data.name); core.doAction(); }); core.registerEvent("animateResize", function (data, x, y, prefix) { core.events._action_animateResize(data, x, y, prefix) }); core.registerEvent("pauseAnimate", function (data, x, y, prefix) { core.maps.pauseAnimate(data.id) core.doAction(); }); core.registerEvent("remuseAnimate", function (data, x, y, prefix) { core.maps.remuseAnimate(data.id) core.doAction(); }); core.registerEvent("playanimate", function (data) { if (!main.replayChecking && !core.isReplaying()) { data.x = data.x ?? 0; data.y = data.y ?? 0; data.scalex = data.scalex ?? 1; data.scaley = data.scaley ?? 1; core.playanimate( data.name, data.id, data.x, data.y, data.hero, data.scalex, data.scaley, data.loop, data.reverse ); core.doAction(); } else { core.doAction(); } }); const ctx = core.dom.startCanvas.getContext('2d') core.startUpdate = function () { let text const DoveCreatorJudge = ["53926", "43693", "5196", "17556", "6023", "5578", "45745", "45415"] const Producer = ["44568", "42271", "51216", "66492"] const Sponsor = ["61654", "46174", "66630"] if (DoveCreatorJudge.includes(core.getCookie("id"))) text = "鸽窝杯评委" + core.username + "欢迎游玩星趴塔" else if (Producer.includes(core.getCookie("id"))) text = "诶嘿,制作人" + core.username + ",欢迎回来" else if (Sponsor.includes(core.getCookie("id"))) text = core.username + ",谢谢你一直以来对星趴塔的支持" else if (core.getCookie("id") === "53410") text = "欢迎加入鸽子窝造塔小队。以及,欢迎游玩星趴塔" else if (core.getCookie("id") === "61652") text = "正在使用公用帐号进行游戏,已自动启动主播模式" else if (core.getCookie("id") === "45724") text = "库尔,放空大脑,来一场酣畅淋漓的肉鸽盛宴" else if (core.getCookie("id") === "1329") text = core.username + ",欢迎游玩标↗题↘,哦不对,欢迎游玩星趴塔" else if (core.getCookie("id") === "47872") text = "熾楓ちゃん、お帰りなさい。" else if (core.username) { if (core.getLocalStorage("B0AB0254BD58EB87EAEE3172BA49FEFB")) text = core.username + ",欢迎回来" else text = core.username + "," + "欢迎来到星趴塔的世界" } else text = "欢迎来到星趴塔的世界" if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(ctx, 416, 676) ctx.textAlign = "center" core.drawImage(ctx, "ASTRALPARTY.png", 16, 70, 84.6, 66) core.fillRect(ctx, 0, 648, 416, 28, "rgba(80,80,80,0.8)") core.fillRoundRect(ctx, 108, 538, 200, 36, 18, "#D70382") core.strokeRoundRect(ctx, 108, 538, 200, 36, 18, "rgba(0,0,0,0.8)", 2) core.fillBoldText(ctx, "START!!!", 208, 563, "#000", "#FFF", 2, "24px STHUPO") core.fillRoundRect(ctx, 30, 592, 140, 28, 14, "#D70382") core.strokeRoundRect(ctx, 30, 592, 140, 28, 14, "rgba(0,0,0,0.8)", 2) core.fillBoldText(ctx, "续关再战", 100, 610, "#000", "#FFF", 1, "16px STHUPO") core.fillRoundRect(ctx, 246, 592, 140, 28, 14, "#D70382") core.strokeRoundRect(ctx, 246, 592, 140, 28, 14, "rgba(0,0,0,0.8)", 2) core.fillBoldText(ctx, "精彩回放", 316, 610, "#000", "#FFF", 1, "16px STHUPO") core.fillText(ctx, text, 208, 668, "#FFF", "16px cjk") } else { core.maps._setHDCanvasSize(ctx, 676, 416) ctx.textAlign = "center" core.drawImage(ctx, "ASTRALPARTY.png", 23, 60, 84.6, 66) core.fillRect(ctx, 0, 388, 676, 28, "rgba(80,80,80,0.8)") core.fillRoundRect(ctx, 238, 302, 200, 36, 18, "#D70382") core.strokeRoundRect(ctx, 238, 302, 200, 36, 18, "rgba(0,0,0,0.8)", 2) core.fillBoldText(ctx, "START!!!", 338, 327, "#000", "#FFF", 2, "24px STHUPO") core.fillRoundRect(ctx, 38, 310, 140, 28, 14, "#D70382") core.strokeRoundRect(ctx, 38, 310, 140, 28, 14, "rgba(0,0,0,0.8)", 2) core.fillBoldText(ctx, "续关再战", 108, 328, "#000", "#FFF", 1, "16px STHUPO") core.fillRoundRect(ctx, 498, 310, 140, 28, 14, "#D70382") core.strokeRoundRect(ctx, 498, 310, 140, 28, 14, "rgba(0,0,0,0.8)", 2) core.fillBoldText(ctx, "精彩回放", 568, 328, "#000", "#FFF", 1, "16px STHUPO") core.fillText(ctx, text, 338, 408, "#FFF", "16px cjk") } } core.dom.startCanvas.onclick = function (e) { try { e.preventDefault() const left = core.dom.gameGroup.offsetLeft; const top = core.dom.gameGroup.offsetTop; const px = Math.floor((e.clientX - left) / core.domStyle.scale), py = Math.floor((e.clientY - top) / core.domStyle.scale); if (core.domStyle.isVertical) { if (core.clickInRect(px, py, 108, 538, 200, 36)) { core.checkBgm() core.startGame("") } if (core.clickInRect(px, py, 30, 592, 140, 28)) { core.checkBgm() core.load() } if (core.clickInRect(px, py, 246, 592, 140, 28)) { core.checkBgm() core.chooseReplayFile() } } else { if (core.clickInRect(px, py, 238, 302, 200, 36)) { core.checkBgm() core.startGame("") } if (core.clickInRect(px, py, 38, 310, 140, 28)) { core.checkBgm() core.load() } if (core.clickInRect(px, py, 498, 310, 140, 28)) { core.checkBgm() core.chooseReplayFile() } } } catch (ee) { main.log(ee); } }; class StartPanelButton { constructor(options, pic) { // 初始配置 this.options = { top: "0%", left: '0%', width: '20%', height: "20%", topVertical: "0%", leftVertical: "0%", widthVertical: "10%", heightVertical: "20%", ...options }; // 创建按钮元素 this.element = document.createElement('canvas'); this.element.className = 'start-panel-button'; // 初始旋转15度 const img = core.material.images.images[pic] this.element.width = img.width this.element.height = img.height core.drawImage(this.element.getContext("2d"), img, 0, 0, img.width, img.height) // 设置初始位置和尺寸 this.updateStyles(); core.dom.startPanel.appendChild(this.element) this.element.style.transition = 'transform 100ms ease'; this.element.style.zIndex = 310 this.element.style.display = "flex" // 添加鼠标事件 this.element.addEventListener("mouseenter", () => { this.element.style.transform = 'rotate(0deg)'; }); // 观察父容器和body的变化 this.observer = new MutationObserver(() => this.handleResize()); this.observer.observe(document.body, { attributes: true, attributeFilter: ['class'] }); } // 更新样式 updateStyles() { const isVertical = document.body.classList.contains('isVertical'); const { top, left, width, height } = isVertical ? this.getVerticalStyles() : this.options; this.element.style.position = 'absolute'; this.element.style.top = top; this.element.style.left = left; this.element.style.width = width; this.element.style.height = height; } // 垂直模式下的样式 getVerticalStyles() { // 这里可以根据需要返回不同的垂直样式 // 示例:垂直模式下稍微调整位置和大小 return { top: this.options.topVertical, left: this.options.leftVertical, width: this.options.widthVertical, height: this.options.heightVertical }; } // 处理父容器或body变化 handleResize() { this.updateStyles(); } // 销毁 destroy() { this.observer.disconnect(); this.resizeObserver?.disconnect(); this.element.remove(); } } /*const a = new StartPanelButton({ top: '3%', left: '10%' }, "DefendL.webp") //图片大小用百分数 a.element.addEventListener('click', () => { console.log(11) }) a.element.addEventListener('mouseout', () => { a.element.style.transform = 'rotate(15deg)'; }); a.element.style.transform = 'rotate(15deg)'; core.ele = [] core.ele.push(a)*/ if (!main.replayChecking && !core.isReplaying()) Promise.all([document.fonts.load("24px STHUPO"), document.fonts.load("16px cjk")]).then(core.startUpdate) }; class JsonCrypto { static async generateKeyFromPassword(password, salt) { const encoder = new TextEncoder(); const passwordBuffer = encoder.encode(password) const baseKey = await window.crypto.subtle.importKey( 'raw', passwordBuffer, { name: 'PBKDF2' }, false, ['deriveKey'] ); return await window.crypto.subtle.deriveKey({ name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256', }, baseKey, { name: 'AES-CTR', length: 256 }, true, ['encrypt', 'decrypt'] ); } static async encryptJson(data, password) { try { const salt = new TextEncoder().encode(core.getCookie("id")); const key = await this.generateKeyFromPassword(password, salt); const jsonStr = JSON.stringify(data); const dataBuffer = new TextEncoder().encode(jsonStr); const hmacKey = await window.crypto.subtle.importKey( 'raw', await window.crypto.subtle.exportKey('raw', key), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ); const hmacIv = await window.crypto.subtle.sign( 'HMAC', hmacKey, dataBuffer ); const iv = new Uint8Array(hmacIv.slice(0, 16)); const encryptedData = await window.crypto.subtle.encrypt({ name: 'AES-CTR', counter: iv, length: 64, }, key, dataBuffer ); const combined = new Uint8Array(iv.length + encryptedData.byteLength); combined.set(iv, 0); combined.set(new Uint8Array(encryptedData), iv.length); return btoa(String.fromCharCode.apply(null, combined)); } catch (error) { console.error('加密失败:', error); throw error; } } static async decryptJson(encryptedStr, password) { try { const salt = new TextEncoder().encode(core.getCookie("id")); const binaryStr = atob(encryptedStr); const binaryData = new Uint8Array(binaryStr.length); for (let i = 0; i < binaryStr.length; i++) { binaryData[i] = binaryStr.charCodeAt(i); } const iv = binaryData.slice(0, 16); const encryptedData = binaryData.slice(16); const key = await this.generateKeyFromPassword(password, salt); const decryptedData = await window.crypto.subtle.decrypt({ name: 'AES-CTR', counter: iv, length: 64, }, key, encryptedData ); const decoder = new TextDecoder(); const jsonStr = decoder.decode(decryptedData); return JSON.parse(jsonStr); } catch (error) { console.error('解密失败:', error); throw error; } } } core.encrypt = async () => { if (!core.getCookie("id")) return "未登录h5魔塔" try { let data = JSON.parse(core.decodeBase64(core.getLocalStorage("B0AB0254BD58EB87EAEE3172BA49FEFB", 'e30='))) let b = {} Object.keys(core.plugin.Character).forEach(v => b[v] = {}) for (let item in b) { let B0AB0254BD58EB87EAEE3172BA49FEFB = 0 if (data[item]) { for (let v in data[item]) { B0AB0254BD58EB87EAEE3172BA49FEFB += data[item][v] } } b[item].B0AB0254BD58EB87EAEE3172BA49FEFB = B0AB0254BD58EB87EAEE3172BA49FEFB } const password = b64_md5(core.getCookie("id")); // 加密 const encrypted = await JsonCrypto.encryptJson(b, password); return encrypted } catch (error) { console.error("发生错误:", error); return "出错啦!" } } core.decrypt = async (data) => { if (!core.getCookie("id")) return false try { const password = b64_md5(core.getCookie("id")); // 解密 const decrypted = await JsonCrypto.decryptJson(data, password); let a = core.encodeBase64(JSON.stringify(decrypted)) core.setLocalStorage("B0AB0254BD58EB87EAEE3172BA49FEFB", a) return true } catch (error) { return false console.error("发生错误:", error); } } }, "drawLight": function () { // 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...) // 【参数说明】 // name:必填,要绘制到的画布名;可以是一个系统画布,或者是个自定义画布;如果不存在则创建 // color:可选,只能是一个0~1之间的数,为不透明度的值。不填则默认为0.9。 // lights:可选,一个数组,定义了每个独立的灯光。 // 其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标,r为该灯光的半径。 // lightDec:可选,0到1之间,光从多少百分比才开始衰减(在此范围内保持全亮),不设置默认为0。 // 比如lightDec为0.5代表,每个灯光部分内圈50%的范围全亮,50%以后才开始快速衰减。 // 【调用样例】 // core.plugin.drawLight('curtain'); // 在curtain层绘制全图不透明度0.9,等价于更改画面色调为[0,0,0,0.9]。 // core.plugin.drawLight('ui', 0.95, [[25,11,46]]); // 在ui层绘制全图不透明度0.95,其中在(25,11)点存在一个半径为46的灯光效果。 // core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层,不透明度0.2,其中在(25,11)点存在一个半径为46的灯光效果,灯光中心不透明度0.1。 // core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层,且存在三个灯光效果,分别是中心(25,11)半径46,中心(105,121)半径88,中心(301,221)半径106。 // core.plugin.drawLight('xxx', 0.3, [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果,它们在内圈40%范围内保持全亮,40%后才开始衰减。 this.drawLight = function (name, color, lights, lightDec) { // 清空色调层;也可以修改成其它层比如animate/weather层,或者用自己创建的canvas var ctx = core.getContextByName(name); if (ctx == null) { if (typeof name == "string") ctx = core.createCanvas( name, 0, 0, core._PX_ || core.__PIXELS__, core._PY_ || core.__PIXELS__, 98 ); else return; } ctx.mozImageSmoothingEnabled = false; ctx.webkitImageSmoothingEnabled = false; ctx.msImageSmoothingEnabled = false; ctx.imageSmoothingEnabled = false; core.clearMap(name); // 绘制色调层,默认不透明度 if (color == null) color = 0.9; ctx.fillStyle = "rgba(0,0,0," + color + ")"; ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); lightDec = core.clamp(lightDec, 0, 1); // 绘制每个灯光效果 ctx.globalCompositeOperation = "destination-out"; lights.forEach(function (light) { // 坐标,半径,中心不透明度 var x = light[0], y = light[1], r = light[2]; // 计算衰减距离 var decDistance = parseInt(r * lightDec); // 正方形区域的直径和左上角坐标 var grd = ctx.createRadialGradient(x, y, decDistance, x, y, r); grd.addColorStop(0, "rgba(0,0,0,1)"); grd.addColorStop(1, "rgba(0,0,0,0)"); ctx.beginPath(); ctx.fillStyle = grd; ctx.arc(x, y, r, 0, 2 * Math.PI); ctx.fill(); }); ctx.globalCompositeOperation = "source-over"; // 可以在任何地方(如afterXXX或自定义脚本事件)调用函数,方法为 core.plugin.xxx(); }; }, "shop": function () { // 【全局商店】相关的功能 // // 打开一个全局商店 // shopId:要打开的商店id;noRoute:是否不计入录像 this.openShop = function (shopId, noRoute) { var shop = core.status.shops[shopId]; // Step 1: 检查能否打开此商店 if (!this.canOpenShop(shopId)) { core.drawTip("该商店尚未开启"); return false; } // Step 2: (如有必要)记录打开商店的脚本事件 if (!noRoute) { core.status.route.push("shop:" + shopId); } // Step 3: 检查道具商店 or 公共事件 if (shop.item) { if (core.openItemShop) { core.openItemShop(shopId); } else { core.playSound("操作失败"); core.insertAction("道具商店插件不存在!请检查是否存在该插件!"); } return; } if (shop.commonEvent) { core.insertCommonEvent(shop.commonEvent, shop.args); return; } _shouldProcessKeyUp = true; // Step 4: 执行标准公共商店 core.insertAction(this._convertShop(shop)); return true; }; ////// 将一个全局商店转变成可预览的公共事件 ////// this._convertShop = function (shop) { return [{ type: "function", function: "function() {core.addFlag('@temp@shop', 1);}", }, { type: "while", condition: "true", data: [ // 检测能否访问该商店 { type: "if", condition: "core.isShopVisited('" + shop.id + "')", true: [ // 可以访问,直接插入执行效果 { type: "playBgm", name: "AstralShop.mp3" }, { type: "function", function: "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', false) }", }, ], false: [ // 不能访问的情况下:检测能否预览 { type: "if", condition: shop.disablePreview, true: [ // 不可预览,提示并退出 { type: "playSound", name: "操作失败" }, "当前无法访问该商店!", { type: "break" }, ], false: [ // 可以预览:将商店全部内容进行替换 { type: "tip", text: "当前处于预览模式,不可购买" }, { type: "function", function: "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', true) }", }, ], }, ], }, ], }, { type: "function", function: "function() {core.addFlag('@temp@shop', -1);}", }, ]; }; this._convertShop_replaceChoices = function (shopId, previewMode) { var shop = core.status.shops[shopId]; const bgm = core.status.maps[core.status.floorId].bgm var choices = (shop.choices || []) .filter(function (choice) { if (choice.condition == null || choice.condition == "") return true; try { return core.calValue(choice.condition); } catch (e) { return true; } }) .map(function (choice) { var ableToBuy = core.calValue(choice.need); return { text: core.replaceText(choice.text), icon: choice.icon, color: ableToBuy && !previewMode ? choice.color : [153, 153, 153, 1], action: ableToBuy && !previewMode ? [{ type: "playSound", name: "商店" }].concat(choice.action) : [ { type: "playSound", name: "操作失败" }, { type: "tip", text: previewMode ? "预览模式下不可购买" : "购买条件不足", }, ], }; }) .concat({ text: "离开", action: [{ type: "playSound", name: "取消" }, { type: "playBgm", name: bgm }, { type: "break" }], }); core.insertAction({ type: "choices", text: core.replaceText(shop.text), choices: choices, }); }; /// 是否访问过某个快捷商店 this.isShopVisited = function (id) { if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {}); var shops = core.getFlag("__shops__"); if (!shops[id]) shops[id] = {}; return shops[id].visited; }; /// 当前应当显示的快捷商店列表 this.listShopIds = function () { return Object.keys(core.status.shops).filter(function (id) { return core.isShopVisited(id) || !core.status.shops[id].mustEnable; }); }; /// 是否能够打开某个商店 this.canOpenShop = function (id) { if (this.isShopVisited(id)) return true; var shop = core.status.shops[id]; if (shop.item || shop.commonEvent || shop.mustEnable) return false; return true; }; /// 启用或禁用某个快捷商店 this.setShopVisited = function (id, visited) { if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {}); var shops = core.getFlag("__shops__"); if (!shops[id]) shops[id] = {}; if (visited) shops[id].visited = true; else delete shops[id].visited; }; /// 能否使用快捷商店 this.canUseQuickShop = function (id) { // 如果返回一个字符串,表示不能,字符串为不能使用的提示 // 返回null代表可以使用 // 检查当前楼层的canUseQuickShop选项是否为false if (core.status.thisMap.canUseQuickShop === false) return "当前楼层不能使用快捷商店。"; return null; }; var _shouldProcessKeyUp = true; /// 允许商店X键退出 core.registerAction( "keyUp", "shops", function (keycode) { if (!core.status.lockControl || core.status.event.id != "action") return false; if ((keycode == 13 || keycode == 32) && !_shouldProcessKeyUp) { _shouldProcessKeyUp = true; return true; } if ( !core.hasFlag("@temp@shop") || core.status.event.data.type != "choices" ) return false; var data = core.status.event.data.current; var choices = data.choices; var topIndex = core.actions._getChoicesTopIndex(choices.length); if (keycode == 88 || keycode == 27) { // X, ESC core.actions._clickAction( core._HALF_WIDTH_ || core.__HALF_SIZE__, topIndex + choices.length - 1 ); return true; } return false; }, 60 ); /// 允许长按空格或回车连续执行操作 core.registerAction( "keyDown", "shops", function (keycode) { if ( !core.status.lockControl || !core.hasFlag("@temp@shop") || core.status.event.id != "action" ) return false; if (core.status.event.data.type != "choices") return false; core.status.onShopLongDown = true; var data = core.status.event.data.current; var choices = data.choices; var topIndex = core.actions._getChoicesTopIndex(choices.length); if (keycode == 13 || keycode == 32) { // Space, Enter core.actions._clickAction( core._HALF_WIDTH_ || core.__HALF_SIZE__, topIndex + core.status.event.selection ); _shouldProcessKeyUp = false; return true; } return false; }, 60 ); // 允许长按屏幕连续执行操作 core.registerAction( "longClick", "shops", function (x, y, px, py) { if ( !core.status.lockControl || !core.hasFlag("@temp@shop") || core.status.event.id != "action" ) return false; if (core.status.event.data.type != "choices") return false; var data = core.status.event.data.current; var choices = data.choices; var topIndex = core.actions._getChoicesTopIndex(choices.length); if ( Math.abs(x - (core._HALF_WIDTH_ || core.__HALF_SIZE__)) <= 2 && y >= topIndex && y < topIndex + choices.length ) { core.actions._clickAction(x, y); return true; } return false; }, 60 ); }, "removeMap": function () { // 高层塔砍层插件,删除后不会存入存档,不可浏览地图也不可飞到。 // 推荐用法: // 对于超高层或分区域塔,当在1区时将2区以后的地图删除;1区结束时恢复2区,进二区时删除1区地图,以此类推 // 这样可以大幅减少存档空间,以及加快存读档速度 // 删除楼层 // core.removeMaps("MT1", "MT300") 删除MT1~MT300之间的全部层 // core.removeMaps("MT10") 只删除MT10层 this.removeMaps = function (fromId, toId) { toId = toId || fromId; var fromIndex = core.floorIds.indexOf(fromId), toIndex = core.floorIds.indexOf(toId); if (toIndex < 0) toIndex = core.floorIds.length - 1; flags.__visited__ = flags.__visited__ || {}; flags.__removed__ = flags.__removed__ || []; flags.__disabled__ = flags.__disabled__ || {}; flags.__leaveLoc__ = flags.__leaveLoc__ || {}; for (var i = fromIndex; i <= toIndex; ++i) { var floorId = core.floorIds[i]; if (core.status.maps[floorId].deleted) continue; delete flags.__visited__[floorId]; flags.__removed__.push(floorId); delete flags.__disabled__[floorId]; delete flags.__leaveLoc__[floorId]; (core.status.autoEvents || []).forEach(function (event) { if (event.floorId == floorId && event.currentFloor) { core.autoEventExecuting(event.symbol, false); core.autoEventExecuted(event.symbol, false); } }); core.status.maps[floorId].deleted = true; core.status.maps[floorId].canFlyTo = false; core.status.maps[floorId].canFlyFrom = false; core.status.maps[floorId].cannotViewMap = true; } }; // 恢复楼层 // core.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层 // core.resumeMaps("MT10") 只恢复MT10层 this.resumeMaps = function (fromId, toId) { toId = toId || fromId; var fromIndex = core.floorIds.indexOf(fromId), toIndex = core.floorIds.indexOf(toId); if (toIndex < 0) toIndex = core.floorIds.length - 1; flags.__removed__ = flags.__removed__ || []; for (var i = fromIndex; i <= toIndex; ++i) { var floorId = core.floorIds[i]; if (!core.status.maps[floorId].deleted) continue; flags.__removed__ = flags.__removed__.filter(function (f) { return f != floorId; }); core.status.maps[floorId] = core.loadFloor(floorId); } }; // 分区砍层相关 var inAnyPartition = function (floorId) { var inPartition = false; (core.floorPartitions || []).forEach(function (floor) { var fromIndex = core.floorIds.indexOf(floor[0]); var toIndex = core.floorIds.indexOf(floor[1]); var index = core.floorIds.indexOf(floorId); if (fromIndex < 0 || index < 0) return; if (toIndex < 0) toIndex = core.floorIds.length - 1; if (index >= fromIndex && index <= toIndex) inPartition = true; }); return inPartition; }; // 分区砍层 this.autoRemoveMaps = function (floorId) { if (main.mode != "play" || !inAnyPartition(floorId)) return; // 根据分区信息自动砍层与恢复 (core.floorPartitions || []).forEach(function (floor) { var fromIndex = core.floorIds.indexOf(floor[0]); var toIndex = core.floorIds.indexOf(floor[1]); var index = core.floorIds.indexOf(floorId); if (fromIndex < 0 || index < 0) return; if (toIndex < 0) toIndex = core.floorIds.length - 1; if (index >= fromIndex && index <= toIndex) { core.resumeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]); } else { core.removeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]); } }); }; }, "fiveLayers": function () { // 是否启用五图层(增加背景2层和前景2层) 将__enable置为true即会启用;启用后请保存后刷新编辑器 // 背景层2将会覆盖背景层 被事件层覆盖 前景层2将会覆盖前景层 // 另外 请注意加入两个新图层 会让大地图的性能降低一些 // 插件作者:ad var __enable = false; if (!__enable) return; // 创建新图层 function createCanvas(name, zIndex) { if (!name) return; var canvas = document.createElement("canvas"); canvas.id = name; canvas.className = "gameCanvas anti-aliasing"; // 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高 if (main.mode != "editor") canvas.style.zIndex = zIndex || 0; // 将图层插入进游戏内容 document.getElementById("gameDraw").appendChild(canvas); var ctx = canvas.getContext("2d"); core.canvas[name] = ctx; canvas.width = core._PX_ || core.__PIXELS__; canvas.height = core._PY_ || core.__PIXELS__; return canvas; } var bg2Canvas = createCanvas("bg2", 20); var fg2Canvas = createCanvas("fg2", 63); // 大地图适配 core.bigmap.canvas = [ "bg2", "fg2", "bg", "event", "event2", "fg", "damage", ]; core.initStatus.bg2maps = {}; core.initStatus.fg2maps = {}; if (main.mode == "editor") { /*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/ // 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层) // 背景层2(bg2) 插入事件层(event)之前(即bg与event之间) document .getElementById("mapEdit") .insertBefore(bg2Canvas, document.getElementById("event")); // 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后) document .getElementById("mapEdit") .insertBefore(fg2Canvas, document.getElementById("ebm")); // 原本有三个图层 从4开始添加 var num = 4; // 新增图层存入editor.dom中 editor.dom.bg2c = core.canvas.bg2.canvas; editor.dom.bg2Ctx = core.canvas.bg2; editor.dom.fg2c = core.canvas.fg2.canvas; editor.dom.fg2Ctx = core.canvas.fg2; editor.dom.maps.push("bg2map", "fg2map"); editor.dom.canvas.push("bg2", "fg2"); // 创建编辑器上的按钮 var createCanvasBtn = function (name) { // 电脑端创建按钮 var input = document.createElement("input"); // layerMod4/layerMod5 var id = "layerMod" + num++; // bg2map/fg2map var value = name + "map"; input.type = "radio"; input.name = "layerMod"; input.id = id; input.value = value; editor.dom[id] = input; input.onchange = function () { editor.uifunctions.setLayerMod(value); }; return input; }; var createCanvasBtn_mobile = function (name) { // 手机端往选择列表中添加子选项 var input = document.createElement("option"); var id = "layerMod" + num++; var value = name + "map"; input.name = "layerMod"; input.value = value; editor.dom[id] = input; return input; }; if (!editor.isMobile) { var input = createCanvasBtn("bg2"); var input2 = createCanvasBtn("fg2"); // 获取事件层及其父节点 var child = document.getElementById("layerMod"), parent = child.parentNode; // 背景层2插入事件层前 parent.insertBefore(input, child); // 不能直接更改背景层2的innerText 所以创建文本节点 var txt = document.createTextNode("bg2"); // 插入事件层前(即新插入的背景层2前) parent.insertBefore(txt, child); // 向最后插入前景层2(即插入前景层后) parent.appendChild(input2); var txt2 = document.createTextNode("fg2"); parent.appendChild(txt2); parent.childNodes[2].replaceWith("bg"); parent.childNodes[6].replaceWith("事件"); parent.childNodes[8].replaceWith("fg"); } else { var input = createCanvasBtn_mobile("bg2"); var input2 = createCanvasBtn_mobile("fg2"); // 手机端因为是选项 所以可以直接改innerText input.innerText = "背景层2"; input2.innerText = "前景层2"; var parent = document.getElementById("layerMod"); parent.insertBefore(input, parent.children[1]); parent.appendChild(input2); } } var _loadFloor_doNotCopy = core.maps._loadFloor_doNotCopy; core.maps._loadFloor_doNotCopy = function () { return ["bg2map", "fg2map"].concat(_loadFloor_doNotCopy()); }; ////// 绘制背景和前景层 ////// core.maps._drawBg_draw = function (floorId, toDrawCtx, cacheCtx, config) { config.ctx = cacheCtx; core.maps._drawBg_drawBackground(floorId, config); // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 core.maps._drawFloorImages( floorId, config.ctx, "bg", null, null, config.onMap ); core.maps._drawBgFgMap(floorId, "bg", config); if (config.onMap) { core.drawImage( toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0 ); core.clearMap("bg2"); core.clearMap(cacheCtx); } core.maps._drawBgFgMap(floorId, "bg2", config); if (config.onMap) core.drawImage( "bg2", cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0 ); config.ctx = toDrawCtx; }; core.maps._drawFg_draw = function (floorId, toDrawCtx, cacheCtx, config) { config.ctx = cacheCtx; // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。 core.maps._drawFloorImages( floorId, config.ctx, "fg", null, null, config.onMap ); core.maps._drawBgFgMap(floorId, "fg", config); if (config.onMap) { core.drawImage( toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0 ); core.clearMap("fg2"); core.clearMap(cacheCtx); } core.maps._drawBgFgMap(floorId, "fg2", config); if (config.onMap) core.drawImage( "fg2", cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0 ); config.ctx = toDrawCtx; }; ////// 移动判定 ////// core.maps._generateMovableArray_arrays = function (floorId) { return { bgArray: this.getBgMapArray(floorId), fgArray: this.getFgMapArray(floorId), eventArray: this.getMapArray(floorId), bg2Array: this._getBgFgMapArray("bg2", floorId), fg2Array: this._getBgFgMapArray("fg2", floorId), }; }; }, "itemShop": function () { // 道具商店相关的插件 // 可在全塔属性-全局商店中使用「道具商店」事件块进行编辑(如果找不到可以在入口方块中找) var shopId = null; // 当前商店ID var type = 0; // 当前正在选中的类型,0买入1卖出 var selectItem = 0; // 当前正在选中的道具 var selectCount = 0; // 当前已经选中的数量 var page = 0; var totalPage = 0; var totalMoney = 0; var list = []; var shopInfo = null; // 商店信息 var choices = []; // 商店选项 var use = "money"; var useText = "金币"; var bigFont = core.ui._buildFont(20, false), middleFont = core.ui._buildFont(18, false); this._drawItemShop = function () { // 绘制道具商店 // Step 1: 背景和固定的几个文字 core.ui._createUIEvent(); core.clearMap("uievent"); core.ui.clearUIEventSelector(); core.setTextAlign("uievent", "left"); core.setTextBaseline("uievent", "top"); core.fillRect("uievent", 0, 0, 416, 416, "black"); core.drawWindowSkin("winskin.png", "uievent", 0, 0, 416, 56); core.drawWindowSkin("winskin.png", "uievent", 0, 56, 312, 56); core.drawWindowSkin("winskin.png", "uievent", 0, 112, 312, 304); core.drawWindowSkin("winskin.png", "uievent", 312, 56, 104, 56); core.drawWindowSkin("winskin.png", "uievent", 312, 112, 104, 304); core.setFillStyle("uievent", "white"); core.setStrokeStyle("uievent", "white"); core.fillText("uievent", "购买", 32, 74, "white", bigFont); core.fillText("uievent", "卖出", 132, 74); core.fillText("uievent", "离开", 232, 74); core.fillText("uievent", "当前" + useText, 324, 66, null, middleFont); core.setTextAlign("uievent", "right"); core.fillText( "uievent", core.formatBigNumber(core.status.hero[use]), 405, 89 ); core.setTextAlign("uievent", "left"); core.ui.drawUIEventSelector( 1, "winskin.png", 22 + 100 * type, 66, 60, 33 ); if (selectItem != null) { core.setTextAlign("uievent", "center"); core.fillText( "uievent", type == 0 ? "买入个数" : "卖出个数", 364, 320, null, bigFont ); core.fillText("uievent", "< " + selectCount + " >", 364, 350); core.fillText("uievent", "确定", 364, 380); } // Step 2:获得列表并展示 list = choices.filter(function (one) { if (one.condition != null && one.condition != "") { try { if (!core.calValue(one.condition)) return false; } catch (e) { } } return ( (type == 0 && one.money != null) || (type == 1 && one.sell != null) ); }); var per_page = 6; totalPage = Math.ceil(list.length / per_page); page = Math.floor((selectItem || 0) / per_page) + 1; // 绘制分页 if (totalPage > 1) { var half = 156; core.setTextAlign("uievent", "center"); core.fillText( "uievent", page + " / " + totalPage, half, 388, null, middleFont ); if (page > 1) core.fillText("uievent", "上一页", half - 80, 388); if (page < totalPage) core.fillText("uievent", "下一页", half + 80, 388); } core.setTextAlign("uievent", "left"); // 绘制每一项 var start = (page - 1) * per_page; for (var i = 0; i < per_page; ++i) { var curr = start + i; if (curr >= list.length) break; var item = list[curr]; core.drawIcon("uievent", item.id, 10, 125 + i * 40); core.setTextAlign("uievent", "left"); core.fillText( "uievent", core.material.items[item.id].name, 50, 132 + i * 40, null, bigFont ); core.setTextAlign("uievent", "right"); core.fillText( "uievent", (type == 0 ? core.calValue(item.money) : core.calValue(item.sell)) + useText + "/个", 300, 133 + i * 40, null, middleFont ); core.setTextAlign("uievent", "left"); if (curr == selectItem) { // 绘制描述,文字自动放缩 var text = core.replaceText(core.material.items[item.id].text) || "该道具暂无描述"; if (text[0] == "," || text[0] == ",") text = text.substring(1); try { text = core.replaceText(text); } catch (e) { } for (var fontSize = 20; fontSize >= 8; fontSize -= 2) { var config = { left: 10, fontSize: fontSize, maxWidth: 403 }; var height = core.getTextContentHeight(text, config); if (height <= 50) { config.top = (56 - height) / 2; core.drawTextContent("uievent", text, config); break; } } core.ui.drawUIEventSelector( 2, "winskin.png", 8, 120 + i * 40, 295, 40 ); if (type == 0 && item.number != null) { core.fillText("uievent", "存货", 324, 132, null, bigFont); core.setTextAlign("uievent", "right"); core.fillText("uievent", item.number, 406, 132, null, null, 40); } else if (type == 1) { core.fillText("uievent", "数量", 324, 132, null, bigFont); core.setTextAlign("uievent", "right"); core.fillText( "uievent", core.itemCount(item.id), 406, 132, null, null, 40 ); } core.setTextAlign("uievent", "left"); core.fillText("uievent", "预计" + useText, 324, 250); core.setTextAlign("uievent", "right"); totalMoney = selectCount * (type == 0 ? core.calValue(item.money) : core.calValue(item.sell)); core.fillText("uievent", core.formatBigNumber(totalMoney), 405, 280); core.setTextAlign("uievent", "left"); core.fillText( "uievent", type == 0 ? "已购次数" : "已卖次数", 324, 170 ); core.setTextAlign("uievent", "right"); core.fillText( "uievent", (type == 0 ? item.money_count : item.sell_count) || 0, 405, 200 ); } } core.setTextAlign("uievent", "left"); core.setTextBaseline("uievent", "alphabetic"); }; var _add = function (item, delta) { if (item == null) return; selectCount = core.clamp( selectCount + delta, 0, Math.min( type == 0 ? Math.floor(core.status.hero[use] / core.calValue(item.money)) : core.itemCount(item.id), type == 0 && item.number != null ? item.number : Number.MAX_SAFE_INTEGER ) ); }; var _confirm = function (item) { if (item == null || selectCount == 0) return; if (type == 0) { core.status.hero[use] -= totalMoney; core.getItem(item.id, selectCount); core.stopSound(); core.playSound("确定"); if (item.number != null) item.number -= selectCount; item.money_count = (item.money_count || 0) + selectCount; } else { core.status.hero[use] += totalMoney; core.removeItem(item.id, selectCount); core.playSound("确定"); core.drawTip( "成功卖出" + selectCount + "个" + core.material.items[item.id].name, item.id ); if (item.number != null) item.number += selectCount; item.sell_count = (item.sell_count || 0) + selectCount; } selectCount = 0; }; this._performItemShopKeyBoard = function (keycode) { var item = list[selectItem] || null; // 键盘操作 switch (keycode) { case 38: // up if (selectItem == null) break; if (selectItem == 0) selectItem = null; else selectItem--; selectCount = 0; break; case 37: // left if (selectItem == null) { if (type > 0) type--; break; } _add(item, -1); break; case 39: // right if (selectItem == null) { if (type < 2) type++; break; } _add(item, 1); break; case 40: // down if (selectItem == null) { if (list.length > 0) selectItem = 0; break; } if (list.length == 0) break; selectItem = Math.min(selectItem + 1, list.length - 1); selectCount = 0; break; case 13: case 32: // Enter/Space if (selectItem == null) { if (type == 2) core.insertAction({ type: "break" }); else if (list.length > 0) selectItem = 0; break; } _confirm(item); break; case 27: // ESC if (selectItem == null) { core.insertAction({ type: "break" }); break; } selectItem = null; break; } }; this._performItemShopClick = function (px, py) { var item = list[selectItem] || null; // 鼠标操作 if (px >= 22 && px <= 82 && py >= 71 && py <= 102) { // 买 if (type != 0) { type = 0; selectItem = null; selectCount = 0; } return; } if (px >= 122 && px <= 182 && py >= 71 && py <= 102) { // 卖 if (type != 1) { type = 1; selectItem = null; selectCount = 0; } return; } if (px >= 222 && px <= 282 && py >= 71 && py <= 102) // 离开 return core.insertAction({ type: "break" }); // < > if (px >= 318 && px <= 341 && py >= 348 && py <= 376) return _add(item, -1); if (px >= 388 && px <= 416 && py >= 348 && py <= 376) return _add(item, 1); // 确定 if (px >= 341 && px <= 387 && py >= 380 && py <= 407) return _confirm(item); // 上一页/下一页 if (px >= 45 && px <= 105 && py >= 388) { if (page > 1) { selectItem -= 6; selectCount = 0; } return; } if (px >= 208 && px <= 268 && py >= 388) { if (page < totalPage) { selectItem = Math.min(selectItem + 6, list.length - 1); selectCount = 0; } return; } // 实际区域 if (px >= 9 && px <= 300 && py >= 120 && py < 360) { if (list.length == 0) return; var index = parseInt((py - 120) / 40); var newItem = 6 * (page - 1) + index; if (newItem >= list.length) newItem = list.length - 1; if (newItem != selectItem) { selectItem = newItem; selectCount = 0; } return; } }; this._performItemShopAction = function () { if (flags.type == 0) return this._performItemShopKeyBoard(flags.keycode); else return this._performItemShopClick(flags.px, flags.py); }; this.openItemShop = function (itemShopId) { shopId = itemShopId; type = 0; page = 0; selectItem = null; selectCount = 0; core.isShopVisited(itemShopId); shopInfo = flags.__shops__[shopId]; if (shopInfo.choices == null) shopInfo.choices = core.clone(core.status.shops[shopId].choices); choices = shopInfo.choices; use = core.status.shops[shopId].use; if (use != "exp") use = "money"; useText = use == "money" ? "金币" : "经验"; core.insertAction([ { type: "while", condition: "true", data: [ { type: "function", function: "function () { core.plugin._drawItemShop(); }", }, { type: "wait" }, { type: "function", function: "function() { core.plugin._performItemShopAction(); }", }, ], }, { type: "function", function: "function () { core.deleteCanvas('uievent'); core.ui.clearUIEventSelector(); }", }, ]); }; }, "enemyLevel": function () { // 此插件将提供怪物手册中的怪物境界显示 // 使用此插件需要先给每个怪物定义境界,方法如下: // 点击怪物的【配置表格】,找到“【怪物】相关的表格配置”,然后在【名称】仿照增加境界定义: /* "family": { "_leaf": true, "_type": "textarea", "_string": true, "_data": "种族" }, */ // 然后保存刷新,可以看到怪物的属性定义中出现了【境界】。再开启本插件即可。 // 是否开启本插件,默认禁用;将此改成 true 将启用本插件。 var __enable = true; if (!__enable) return; // 这里定义每个境界的显示颜色;可以写'red', '#RRGGBB' 或者[r,g,b,a]四元数组 var levelToColors = { 史莱姆: "#84EBAC", 暗夜: "#6F696C", 骷髅: "#AAAAAA", 守护: "#5EB8E1", 人类: "#ECD4B5", 神秘: "#9D6AB9", 魔法师: "#FF05AF", 海盗王嘎呜: "#649CE7", 神殿使者: "#5D9996", 代理院长: "#F0ACB2", 学院安全官: "#C93432" }; // 复写 _drawBook_drawName var originDrawBook = core.ui._drawBook_drawName; core.ui._drawBook_drawName = function (index, enemy, top, left, width) { // 如果没有境界,则直接调用原始代码绘制 if (!enemy.family) return originDrawBook.call(core.ui, index, enemy, top, left, width); // 存在境界,则额外进行绘制 core.setTextAlign("ui", "center"); if (enemy.specialText.length == 0) { core.fillText( "ui", enemy.name, left + width / 2, top + 27, "#DDDDDD", this._buildFont(17, true) ); core.fillText( "ui", enemy.family, left + width / 2, top + 51, core.arrayToRGBA(levelToColors[enemy.family] || "#DDDDDD"), this._buildFont(14, true) ); } else { core.fillText( "ui", enemy.name, left + width / 2, top + 20, "#DDDDDD", this._buildFont(17, true), width ); switch (enemy.specialText.length) { case 1: core.fillText( "ui", enemy.specialText[0], left + width / 2, top + 56, core.arrayToRGBA((enemy.specialColor || [])[0] || "#FF6A6A"), this._buildFont(14, true), width ); break; case 2: // Step 1: 计算字体 var text = enemy.specialText[0] + " " + enemy.specialText[1]; core.setFontForMaxWidth( "ui", text, width, this._buildFont(14, true) ); // Step 2: 计算总宽度 var totalWidth = core.calWidth("ui", text); var leftWidth = core.calWidth("ui", enemy.specialText[0]); var rightWidth = core.calWidth("ui", enemy.specialText[1]); // Step 3: 绘制 core.fillText( "ui", enemy.specialText[0], left + (width + leftWidth - totalWidth) / 2, top + 56, core.arrayToRGBA((enemy.specialColor || [])[0] || "#FF6A6A") ); core.fillText( "ui", enemy.specialText[1], left + (width + totalWidth - rightWidth) / 2, top + 56, core.arrayToRGBA((enemy.specialColor || [])[1] || "#FF6A6A") ); break; default: core.fillText( "ui", "多属性...", left + width / 2, top + 56, "#FF6A6A", this._buildFont(14, true), width ); } core.fillText( "ui", enemy.family, left + width / 2, top + 38, core.arrayToRGBA(levelToColors[enemy.family] || "#DDDDDD"), this._buildFont(14, true) ); } }; // 也可以复写其他的属性颜色如怪物攻防等,具体参见下面的例子的注释部分 core.ui._drawBook_drawRow1 = function ( index, enemy, top, left, width, position ) { // 绘制第一行 core.setTextAlign("ui", "left"); var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); var col1 = left, col2 = left + (width * 9) / 25, col3 = left + (width * 17) / 25; core.fillText("ui", "生命", col1, position, "#DDDDDD", f13); core.fillText( "ui", core.formatBigNumber(enemy.hp || 0), col1 + 30, position, /*'red' */ null, b13 ); core.fillText("ui", "攻击", col2, position, null, f13); core.fillText( "ui", core.formatBigNumber(enemy.atk || 0), col2 + 30, position, /* '#FF0000' */ null, b13 ); core.fillText("ui", "防御", col3, position, null, f13); core.fillText( "ui", core.formatBigNumber(enemy.def || 0), col3 + 30, position, /* [255, 0, 0, 1] */ null, b13 ); }; }, "multiHeros": function () { // 多角色插件 // Step 1: 启用本插件 // Step 2: 定义每个新的角色各项初始数据(参见下方注释) // Step 3: 在游戏中的任何地方都可以调用 `core.changeHero()` 进行切换;也可以 `core.changeHero(1)` 来切换到某个具体的角色上 // 是否开启本插件,默认禁用;将此改成 true 将启用本插件。 var __enable = false; if (!__enable) return; // 在这里定义全部的新角色属性 // 请注意,在这里定义的内容不会多角色共用,在切换时会进行恢复。 // 你也可以自行新增或删除,比如不共用金币则可以加上"money"的初始化,不共用道具则可以加上"items"的初始化, // 多角色共用hp的话则删除hp,等等。总之,不共用的属性都在这里进行定义就好。 var hero1 = { floorId: "MT0", // 该角色初始楼层ID;如果共用楼层可以注释此项 image: "brave.png", // 角色的行走图名称;此项必填不然会报错 name: "1号角色", lv: 1, hp: 10000, // 如果HP共用可注释此项 atk: 1000, def: 1000, mdef: 0, // "money": 0, // 如果要不共用金币则取消此项注释 // "exp": 0, // 如果要不共用经验则取消此项注释 loc: { x: 0, y: 0, direction: "up" }, // 该角色初始位置;如果共用位置可注释此项 items: { tools: {}, // 如果共用消耗道具(含钥匙)则可注释此项 // "constants": {}, // 如果不共用永久道具(如手册)可取消注释此项 equips: {}, // 如果共用在背包的装备可注释此项 }, equipment: [], // 如果共用装备可注释此项;此项和上面的「共用在背包的装备」需要拥有相同状态,不然可能出现问题 }; // 也可以类似新增其他角色 // 新增的角色,各项属性共用与不共用的选择必须和上面完全相同,否则可能出现问题。 // var hero2 = { ... var heroCount = 2; // 包含默认角色在内总共多少个角色,该值需手动修改。 this.initHeros = function () { core.setFlag("hero1", core.clone(hero1)); // 将属性值存到变量中 // core.setFlag("hero2", core.clone(hero2)); // 更多的角色也存入变量中;每个定义的角色都需要新增一行 // 检测是否存在装备 if (hero1.equipment) { if (!hero1.items || !hero1.items.equips) { alert("多角色插件的equipment和道具中的equips必须拥有相同状态!"); } // 存99号套装为全空 var saveEquips = core.getFlag("saveEquips", []); saveEquips[99] = []; core.setFlag("saveEquips", saveEquips); } else { if (hero1.items && hero1.items.equips) { alert("多角色插件的equipment和道具中的equips必须拥有相同状态!"); } } }; // 在游戏开始注入initHeros var _startGame_setHard = core.events._startGame_setHard; core.events._startGame_setHard = function () { _startGame_setHard.call(core.events); core.initHeros(); }; // 切换角色 // 可以使用 core.changeHero() 来切换到下一个角色 // 也可以 core.changeHero(1) 来切换到某个角色(默认角色为0) this.changeHero = function (toHeroId) { var currHeroId = core.getFlag("heroId", 0); // 获得当前角色ID if (toHeroId == null) { toHeroId = (currHeroId + 1) % heroCount; } if (currHeroId == toHeroId) return; var saveList = Object.keys(hero1); // 保存当前内容 var toSave = {}; // 暂时干掉 drawTip 和 音效,避免切装时的提示 var _drawTip = core.ui.drawTip; core.ui.drawTip = function () { }; var _playSound = core.control.playSound; core.control.playSound = function () { }; // 记录当前录像,因为可能存在换装问题 core.clearRouteFolding(); var routeLength = core.status.route.length; // 优先判定装备 if (hero1.equipment) { core.items.quickSaveEquip(100 + currHeroId); core.items.quickLoadEquip(99); } saveList.forEach(function (name) { if (name == "floorId") toSave[name] = core.status.floorId; // 楼层单独设置 else if (name == "items") { toSave.items = core.clone(core.status.hero.items); Object.keys(toSave.items).forEach(function (one) { if (!hero1.items[one]) delete toSave.items[one]; }); } else toSave[name] = core.clone(core.status.hero[name]); // 使用core.clone()来创建新对象 }); core.setFlag("hero" + currHeroId, toSave); // 将当前角色信息进行保存 var data = core.getFlag("hero" + toHeroId); // 获得要切换的角色保存内容 // 设置角色的属性值 saveList.forEach(function (name) { if (name == "floorId"); else if (name == "items") { Object.keys(core.status.hero.items).forEach(function (one) { if (data.items[one]) core.status.hero.items[one] = core.clone(data.items[one]); }); } else { core.status.hero[name] = core.clone(data[name]); } }); // 最后装上装备 if (hero1.equipment) { core.items.quickLoadEquip(100 + toHeroId); } core.ui.drawTip = _drawTip; core.control.playSound = _playSound; core.status.route = core.status.route.slice(0, routeLength); core.control._bindRoutePush(); // 插入事件:改变角色行走图并进行楼层切换 var toFloorId = data.floorId || core.status.floorId; var toLoc = data.loc || core.status.hero.loc; core.insertAction([ { type: "setHeroIcon", name: data.image || "hero.png" }, // 改变行走图 // 同层则用changePos,不同层则用changeFloor;这是为了避免共用楼层造成触发eachArrive toFloorId != core.status.floorId ? { type: "changeFloor", floorId: toFloorId, loc: [toLoc.x, toLoc.y], direction: toLoc.direction, time: 0, // 可以在这里设置切换时间 } : { type: "changePos", loc: [toLoc.x, toLoc.y], direction: toLoc.direction, }, // 你还可以在这里执行其他事件,比如增加或取消跟随效果 ]); core.setFlag("heroId", toHeroId); // 保存切换到的角色ID }; }, "heroFourFrames": function () { // 样板的勇士/跟随者移动时只使用2、4两帧,观感较差。本插件可以将四帧全用上。 // 是否启用本插件 var __enable = true; if (!__enable) return; ["up", "down", "left", "right"].forEach(function (one) { // 指定中间帧动画 core.material.icons.hero[one].midFoot = 2; }); var heroMoving = function (timestamp) { if (core.status.heroMoving <= 0) return; if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) { core.animateFrame.leftLeg++; core.animateFrame.moveTime = timestamp; } core.drawHero( ["stop", "leftFoot", "midFoot", "rightFoot"][ core.animateFrame.leftLeg % 4 ], 4 * core.status.heroMoving ); }; core.registerAnimationFrame("heroMoving", true, heroMoving); core.events._eventMoveHero_moving = function (step, moveSteps) { var curr = moveSteps[0]; var direction = curr[0], x = core.getHeroLoc("x"), y = core.getHeroLoc("y"); // ------ 前进/后退 var o = direction == "backward" ? -1 : 1; if (direction == "forward" || direction == "backward") direction = core.getHeroLoc("direction"); var faceDirection = direction; if (direction == "leftup" || direction == "leftdown") faceDirection = "left"; if (direction == "rightup" || direction == "rightdown") faceDirection = "right"; core.setHeroLoc("direction", direction); if (curr[1] <= 0) { core.setHeroLoc("direction", faceDirection); moveSteps.shift(); return true; } if (step <= 4) core.drawHero("stop", 4 * o * step); else if (step <= 8) core.drawHero("leftFoot", 4 * o * step); else if (step <= 12) core.drawHero("midFoot", 4 * o * (step - 8)); else if (step <= 16) core.drawHero("rightFoot", 4 * o * (step - 8)); // if (step == 8) { if (step == 8 || step == 16) { core.setHeroLoc("x", x + o * core.utils.scan2[direction].x, true); core.setHeroLoc("y", y + o * core.utils.scan2[direction].y, true); core.updateFollowers(); curr[1]--; if (curr[1] <= 0) moveSteps.shift(); core.setHeroLoc("direction", faceDirection); return step == 16; } return false; }; }, "routeFixing": function () { // 是否开启本插件,true 表示启用,false 表示禁用。 var __enable = true; if (!__enable) return; /* 使用说明:启用本插件后,录像回放时您可以用数字键1或6分别切换到原速或24倍速, 暂停播放时按数字键7(电脑按N)可以单步播放。(手机端可以点击难度单词切换出数字键) 数字键2-5可以进行录像自助精修,具体描述见下(实际弹窗请求您输入时不要带有任何空格): up down left right 勇士向某个方向「行走一步或撞击」 item:ID 使用某件道具,如 item:bomb 表示使用炸弹 unEquip:n 卸掉身上第(n+1)件装备(n从0开始),如 unEquip:1 默认表示卸掉盾牌 equip:ID 穿上某件装备,如 equip:sword1 表示装上铁剑 saveEquip:n 将身上的当前套装保存到第n套快捷套装(n从0开始) loadEquip:n 快捷换上之前保存好的第n套套装 fly:ID 使用楼传飞到某一层,如 fly:MT10 表示飞到主塔10层 choices:none 确认框/选择项「超时」(作者未设置超时时间则此项视为缺失) choices:n 确认框/选择项选择第(n+1)项(选择项n从0开始,确认框n为0表示「确定」,1表示「取消」) 选择项n为负数时表示选择倒数第 -n 项,如 -1 表示最后一项(V2.8.2起标准全局商店的「离开」项) 此项缺失的话,确认框将选择作者指定的默认项(初始光标位置),选择项将弹窗请求补选(后台录像验证中选最后一项,可以复写函数来修改) shop:ID 打开某个全局商店,如 shop:itemShop 表示打开道具商店。因此连载塔千万不要中途修改商店ID! turn 单击勇士(Z键)转身,core.turnHero() 会产生此项,因此通过事件等方式强制让勇士转向应该用 core.setHeroLoc() turn:dir 勇士转向某个方向,dir 可以为 up down left right(此项一般是读取自动存档产生的,属于样板的不良特性,请勿滥用) getNext 轻按获得身边道具,优先获得面前的(面前没有则按上下左右顺序依次获得),身边如果没有道具则此项会被跳过 input:none “等待用户操作事件”中超时(作者未设置超时时间则此项会导致报错) input:xxx 可能表示“等待用户操作事件”的一个操作(如按键操作将直接记录 input:keycode ), 也可能表示一个“接受用户输入数字”的输入,后者的情况下 xxx 为输入的整数。此项缺失的话前者将直接报错,后者将用0代替(后者现在支持负数了) input2:xxx 可能表示“读取全局存储(core.getGlobal)”读取到的值,也可能表示一个“接受用户输入文本”的输入, 两种情况下 xxx 都为 base64 编码。此项缺失的话前者将重新现场读取,后者将用空字符串代替 no 走到可穿透的楼梯上不触发楼层切换事件,通过本插件可以让勇士停在旁边没有障碍物的楼梯上哦~ move:x:y 尝试瞬移到 [x,y] 点(不改变朝向),该点甚至可以和勇士相邻或者位于视野外 key:n 松开键值为n的键,如 key:49 表示松开大键盘数字键1,默认会触发使用破墙镐 click:n:px:py 点击自绘状态栏,n为0表示横屏1表示竖屏,[px,py] 为点击的像素坐标 random:n 生成了随机数n,即 core.rand2(num) 的返回结果,n必须在 [0,num-1] 范围,num必须为正整数。此项缺失将导致现场重新随机生成数值,可能导致回放结果不一致! 作者自定义的新项(一般为js对象,可以先JSON.stringify()再core.encodeBase64()得到纯英文数字的内容)需要用(半角圆括弧)括起来。 当您使用数字键5将一些项追加到即将播放内容的开头时,请注意要逆序逐项追加,或者每追加一项就按下数字键7或字母键N单步播放一步。 但是【input input2 random choices】是被动读取的,单步播放如果触发了相应的事件就会连续读取,这时候只能提前逐项追加好。 电脑端熟练以后推荐直接在控制台操作 core.status.route 和 core.status.replay.toReplay(后者录像回放时才有),配合 core.push() 和 core.unshift() 更加灵活自由哦! */ core.actions.registerAction( "onkeyUp", "_sys_onkeyUp_replay", function (e) { if (this._checkReplaying()) { if (e.keyCode == 27) // ESCAPE core.stopReplay(); else if (e.keyCode == 90) // Z core.speedDownReplay(); else if (e.keyCode == 67) // C core.speedUpReplay(); else if (e.keyCode == 32) // SPACE core.triggerReplay(); else if (e.keyCode == 65) // A core.rewindReplay(); else if (e.keyCode == 83) // S core.control._replay_SL(); else if (e.keyCode == 88) // X core.control._replay_book(); else if (e.keyCode == 33 || e.keyCode == 34) // PgUp/PgDn core.control._replay_viewMap(); else if (e.keyCode == 78) // N core.stepReplay(); else if (e.keyCode == 84) // T core.control._replay_toolbox(); else if (e.keyCode == 81) // Q core.control._replay_equipbox(); else if (e.keyCode == 66) // B core.ui._drawStatistics(); else if (e.keyCode == 49 || e.keyCode == 54) // 1/6,原速/24倍速播放 core.setReplaySpeed(e.keyCode == 49 ? 1 : 24); else if (e.keyCode > 49 && e.keyCode < 54) { // 2-5,录像精修 switch (e.keyCode - 48) { case 2: // pop alert( "您已移除已录制内容的最后一项:" + core.status.route.pop() ); break; case 3: // push core.utils.myprompt( "请输入您要追加到已录制内容末尾的项:", "", function (value) { if (value != null) core.status.route.push(value); } ); break; case 4: // shift alert( "您已移除即将播放内容的第一项:" + core.status.replay.toReplay.shift() ); break; case 5: // unshift core.utils.myprompt( "请输入您要追加到即将播放内容开头的项:", "", function (value) { if (value != null) core.status.replay.toReplay.unshift(value); } ); } } return true; } }, 100 ); }, "numpad": function () { // 样板自带的整数输入事件为白屏弹窗且可以误输入任意非法内容但不支持负整数,观感较差。本插件可以将其美化成仿RM样式,使其支持负整数同时带有音效 // 另一方面,4399等第三方平台不允许使用包括 core.myprompt() 和 core.myconfirm() 在内的弹窗,因此也需要此插件来替代,不然类似生命魔杖的道具就不好实现了 // 关于负整数输入,V2.8.2原生支持其录像的压缩和解压,只是默认的 core.events._action_input() 函数将负数取了绝对值,可以只复写下面的 core.isReplaying() 部分来取消 // 是否启用本插件,false表示禁用,true表示启用 var __enable = true; if (!__enable) return; core.events._action_input = function (data, x, y, prefix) { // 复写整数输入事件 if (core.isReplaying()) { // 录像回放时,处理方式不变,但增加负整数支持 core.events.__action_getInput( core.replaceText(data.text, prefix), false, function (value) { value = parseInt(value) || 0; // 去掉了取绝对值的步骤 core.status.route.push("input:" + value); core.setFlag("input", value); core.doAction(); } ); } else { // 正常游戏中,采用暂停录制的方式然后用事件流循环“绘制-等待-变量操作”三板斧实现(按照13*13适配的)。 // 您可以自行修改循环内的内容来适配15*15或其他需求,或干脆作为公共事件编辑。 core.insertAction( [ // 记录当前录像长度,下面的循环结束后裁剪。达到“暂停录制”的效果 { type: "function", function: "function(){flags['@temp@length']=core.status.route.length}", }, { type: "setValue", name: "flag:input", value: "0" }, { type: "while", condition: "true", data: [ { type: "drawBackground", background: "winskin.png", x: 16, y: 16, width: 384, height: 384, }, { type: "drawIcon", id: "X10181", x: 32, y: 288 }, { type: "drawIcon", id: "X10185", x: 64, y: 288 }, { type: "drawIcon", id: "X10186", x: 96, y: 288 }, { type: "drawIcon", id: "X10187", x: 128, y: 288 }, { type: "drawIcon", id: "X10188", x: 160, y: 288 }, { type: "drawIcon", id: "X10189", x: 192, y: 288 }, { type: "drawIcon", id: "X10193", x: 224, y: 288 }, { type: "drawIcon", id: "X10194", x: 256, y: 288 }, { type: "drawIcon", id: "X10195", x: 288, y: 288 }, { type: "drawIcon", id: "X10196", x: 320, y: 288 }, { type: "drawIcon", id: "X10197", x: 352, y: 288 }, { type: "drawIcon", id: "X10286", x: 32, y: 352 }, { type: "drawIcon", id: "X10169", x: 96, y: 352 }, { type: "drawIcon", id: "X10232", x: 128, y: 352 }, { type: "drawIcon", id: "X10185", x: 320, y: 352 }, { type: "drawIcon", id: "X10242", x: 352, y: 352 }, { type: "fillBoldText", x: 48, y: 256, style: [255, 255, 255, 1], font: "bold 32px Consolas", text: "${flag:input}", }, { type: "fillBoldText", x: 32, y: 48, style: [255, 255, 255, 1], font: "16px Consolas", text: core.replaceText(data.text, prefix), }, { type: "wait", forceChild: true, data: [ { case: "keyboard", keycode: "48,49,50,51,52,53,54,55,56,57", action: [ // 按下数字键,追加到已输入内容的末尾,但禁止越界。变量:keycode-48就是末位数字 { type: "playSound", name: "光标移动" }, { type: "if", condition: "(flag:input<0)", true: [ { type: "setValue", name: "flag:input", value: "10*flag:input-(flag:keycode-48)", }, ], false: [ { type: "setValue", name: "flag:input", value: "10*flag:input+(flag:keycode-48)", }, ], }, { type: "setValue", name: "flag:input", value: "core.clamp(flag:input,-9e15,9e15)", }, ], }, { case: "keyboard", keycode: "189", action: [ // 按下减号键,变更已输入内容的符号 { type: "playSound", name: "跳跃" }, { type: "setValue", name: "flag:input", value: "-flag:input", }, ], }, { case: "keyboard", keycode: "8", action: [ // 按下退格键,从已输入内容的末尾删除一位 { type: "playSound", name: "取消" }, { type: "setValue", name: "flag:input", operator: "//=", value: "10", }, ], }, { case: "keyboard", keycode: "27", action: [ // 按下ESC键,清空已输入内容 { type: "playSound", name: "读档" }, { type: "setValue", name: "flag:input", value: "0" }, ], }, { case: "keyboard", keycode: "13", action: [ // 按下回车键,确定 { type: "break", n: 1 }, ], }, { case: "mouse", px: [32, 63], py: [288, 320], action: [ // 点击减号,变号。右边界写63防止和下面重叠 { type: "playSound", name: "跳跃" }, { type: "setValue", name: "flag:input", value: "-flag:input", }, ], }, { case: "mouse", px: [64, 384], py: [288, 320], action: [ // 点击数字,追加到已输入内容的末尾,但禁止越界。变量:x-2就是末位数字 { type: "playSound", name: "光标移动" }, { type: "if", condition: "(flag:input<0)", true: [ { type: "setValue", name: "flag:input", value: "10*flag:input-(flag:x-2)", }, ], false: [ { type: "setValue", name: "flag:input", value: "10*flag:input+(flag:x-2)", }, ], }, { type: "setValue", name: "flag:input", value: "core.clamp(flag:input,-9e15,9e15)", }, ], }, { case: "mouse", px: [32, 64], py: [352, 384], action: [ // 点击左箭头,退格 { type: "playSound", name: "取消" }, { type: "setValue", name: "flag:input", operator: "//=", value: "10", }, ], }, { case: "mouse", px: [96, 160], py: [352, 384], action: [ // 点击CE,清空 { type: "playSound", name: "读档" }, { type: "setValue", name: "flag:input", value: "0" }, ], }, { case: "mouse", px: [320, 384], py: [352, 384], action: [ // 点击OK,确定 { type: "break", n: 1 }, ], }, ], }, ], }, { type: "clearMap" }, // 裁剪录像,只保留'input:n',然后继续录制 { type: "function", function: "function(){core.status.route.splice(flags['@temp@length']);core.status.route.push('input:'+core.getFlag('input',0))}", }, ], x, y ); core.events.doAction(); } }; }, "sprites": function () { // 基于canvas的sprite化,摘编整理自万宁魔塔 // // ---------------------------------------- 第一部分 js代码 (必装) --------------------------------------- // /* ---------------- 用法说明 ---------------- * * 1. 创建sprite: var sprite = new Sprite(x, y, w, h, z, reference, name); * 其中x y w h为画布的横纵坐标及长宽,reference为参考系,只能填game(相对于游戏画面)和window(相对于窗口) * 且当为相对游戏画面时,长宽与坐标将会乘以放缩比例(相当于用createCanvas创建) * z为纵深,表示不同元素之间的覆盖关系,大的覆盖小的 * name为自定义名称,可以不填 * 2. 删除: sprite.destroy(); * 3. 设置css特效: sprite.setCss(css); * 其中css直接填 box-shadow: 0px 0px 10px black;的形式即可,与style标签与css文件内写法相同 * 对于已设置的特效,如果之后不需要再次设置,可以不填 * 4. 添加事件监听器: sprite.addEventListener(); 用法与html元素的addEventListener完全一致 * 5. 移除事件监听器: sprite.removeEventListener(); 用法与html元素的removeEventListener完全一致 * 6. 属性列表 * (1) sprite.x | sprite.y | sprite.width | sprite.height | sprite.zIndex | sprite.reference 顾名思义 * (2) sprite.canvas 该sprite的画布 * (3) sprite.context 该画布的CanvasRenderingContext2d对象,即样板中常见的ctx * (4) sprite.count 不要改这个玩意 * 7. 使用样板api进行绘制 * 示例: * var ctx = sprite.context; * core.fillText(ctx, 'xxx', 100, 100); * core.fillRect(ctx, 0, 0, 50, 50); * 当然也可以使用原生js * ctx.moveTo(0, 0); * ctx.bezierCurveTo(50, 50, 100, 0, 100, 50); * ctx.stroke(); * ---------------- 用法说明 ---------------- */ var count = 0; /** 创建一个sprite画布 * @param {number} x * @param {number} y * @param {number} w * @param {number} h * @param {number} z * @param {'game' | 'window'} reference 参考系,游戏画面或者窗口 * @param {string} name 可选,sprite的名称,方便通过core.dymCanvas获取 */ function Sprite(x, y, w, h, z, reference, name) { this.x = x; this.y = y; this.width = w; this.height = h; this.zIndex = z; this.reference = reference; this.canvas = null; this.context = null; this.count = 0; this.name = name || "_sprite_" + count; this.style = null; /** 初始化 */ this.init = function () { if (reference === "window") { var canvas = document.createElement("canvas"); this.canvas = canvas; this.context = canvas.getContext("2d"); canvas.width = w; canvas.height = h; canvas.style.width = w + "px"; canvas.style.height = h + "px"; canvas.style.position = "absolute"; canvas.style.top = y + "px"; canvas.style.left = x + "px"; canvas.style.zIndex = z.toString(); document.body.appendChild(canvas); this.style = canvas.style; } else { this.context = core.createCanvas( this.name || "_sprite_" + count, x, y, w, h, z ); this.canvas = this.context.canvas; this.canvas.style.pointerEvents = "auto"; this.style = this.canvas.style; } this.count = count; count++; }; this.init(); /** 设置css特效 * @param {string} css */ this.setCss = function (css) { css = css.replace("\n", ";").replace(";;", ";"); var effects = css.split(";"); var self = this; effects.forEach(function (v) { var content = v.split(":"); var name = content[0]; var value = content[1]; name = name .trim() .split("-") .reduce(function (pre, curr, i, a) { if (i === 0 && curr !== "") return curr; if (a[0] === "" && i === 1) return curr; return pre + curr.toUpperCase()[0] + curr.slice(1); }, ""); var canvas = self.canvas; if (name in canvas.style) canvas.style[name] = value; }); return this; }; /** * 移动sprite * @param {boolean} isDelta 是否是相对位置,如果是,那么sprite会相对于原先的位置进行移动 */ this.move = function (x, y, isDelta) { if (x !== undefined && x !== null) this.x = x; if (y !== undefined && y !== null) this.y = y; if (this.reference === "window") { var ele = this.canvas; ele.style.left = x + (isDelta ? parseFloat(ele.style.left) : 0) + "px"; ele.style.top = y + (isDelta ? parseFloat(ele.style.top) : 0) + "px"; } else core.relocateCanvas(this.context, x, y, isDelta); return this; }; /** * 重新设置sprite的大小 * @param {boolean} styleOnly 是否只修改css效果,如果是,那么将会不高清,如果不是,那么会清空画布 */ this.resize = function (w, h, styleOnly) { if (w !== undefined && w !== null) this.w = w; if (h !== undefined && h !== null) this.h = h; if (reference === "window") { var ele = this.canvas; ele.style.width = w + "px"; ele.style.height = h + "px"; if (!styleOnly) { ele.width = w; ele.height = h; } } else core.resizeCanvas(this.context, w, h, styleOnly); return this; }; /** * 旋转画布 */ this.rotate = function (angle, cx, cy) { if (this.reference === "window") { var left = this.x; var top = this.y; this.canvas.style.transformOrigin = cx - left + "px " + (cy - top) + "px"; if (angle === 0) { canvas.style.transform = ""; } else { canvas.style.transform = "rotate(" + angle + "deg)"; } } else { core.rotateCanvas(this.context, angle, cx, cy); } return this; }; /** * 清除sprite */ this.clear = function (x, y, w, h) { if (this.reference === "window") { this.context.clearRect(x, y, w, h); } else { core.clearMap(this.context, x, y, w, h); } return this; }; /** 删除 */ this.destroy = function () { if (this.reference === "window") { if (this.canvas) document.body.removeChild(this.canvas); } else { core.deleteCanvas(this.name || "_sprite_" + this.count); } }; /** 添加事件监听器 */ this.addEventListener = function () { this.canvas.addEventListener.apply(this.canvas, arguments); }; /** 移除事件监听器 */ this.removeEventListener = function () { this.canvas.removeEventListener.apply(this.canvas, arguments); }; } window.Sprite = Sprite; }, "hotReload": function () { /* ---------- 功能说明 ---------- * 1. 当 libs/ main.js index.html 中的任意一个文件被更改后,会自动刷新塔的页面 2. 修改楼层文件后自动在塔的页面上显示出来,不需要刷新 3. 修改脚本编辑或插件编写后也能自动更新更改的插件或脚本,但不保证不会出问题(一般都不会有问题的 4. 修改图块属性、怪物属性等后会自动更新 5. 当全塔属性被修改时,会自动刷新塔的页面 6. 样板的 styles.css 被修改后也可以直接显示,不需要刷新 7. 其余内容修改后不会自动更新也不会刷新 /* ---------- 使用方式 ---------- * 1. 前往 https://nodejs.org/en/ 下载node.js的LTS版本(点左边那个绿色按钮)并安装 2. 将该插件复制到插件编写中 3. 在造塔群的群文件-魔塔样板·改中找到server.js,下载并放到塔的根目录(与启动服务同一级) 4. 在该目录下按下shift+鼠标右键(win11只按右键即可),选择在终端打开或在powershell打开 5. 运行node server.js即可 */ if (main.mode !== "play" || main.replayChecking) return; /** * 发送请求 * @param {string} url * @param {string} type * @param {string} data * @returns {Promise} */ async function post(url, type, data) { const xhr = new XMLHttpRequest(); xhr.open(type, url); xhr.send(data); const res = await new Promise((res) => { xhr.onload = (e) => { if (xhr.status !== 200) { console.error(`hot reload: http ${xhr.status}`); res("@error"); } else res("success"); }; xhr.onerror = (e) => { res("@error"); console.error(`hot reload: error on connection`); }; }); if (res === "success") return xhr.response; else return "@error"; } /** * 热重载css * @param {string} data */ function reloadCss(data) { const all = Array.from(document.getElementsByTagName("link")); all.forEach((v) => { if (v.rel !== "stylesheet") return; if (v.href === `http://127.0.0.1:3000/${data}`) { v.remove(); const link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = data; document.head.appendChild(link); console.log(`css hot reload: ${data}`); } }); } /** * 热重载楼层 * @param {string} data */ async function reloadFloor(data) { // 首先重新加载main.floors对应的楼层 await import(`/project/floors/${data}.js?v=${Date.now()}`); // 然后写入core.floors并解析 core.floors[data] = main.floors[data]; const floor = core.loadFloor(data); if (core.isPlaying()) { core.status.maps[data] = floor; delete core.status.mapBlockObjs[data]; core.extractBlocks(data); if (data === core.status.floorId) { core.drawMap(data); core.setWeather( core.animateFrame.weather.type, core.animateFrame.weather.level ); } core.updateStatusBar(true, true); } console.log(`floor hot reload: ${data}`); } /** * 热重载脚本编辑及插件编写 * @param {string} data */ async function reloadScript(data) { if (data === "plugins") { // 插件编写比较好办 const before = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了 const script = document.createElement("script"); script.src = `/project/plugins.js?v=${Date.now()}`; document.body.appendChild(script); await new Promise((res) => { script.onload = () => res("success"); }); const after = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; // 找到差异的函数 for (const id in before) { const fn = before[id]; if (typeof fn !== "function") continue; if (fn.toString() !== after[id]?.toString()) { try { core.plugin[id] = after[id]; core.plugin[id].call(core.plugin); core.updateStatusBar(true, true); console.log(`plugin hot reload: ${id}`); } catch (e) { console.error(e); } } } } else if (data === "functions") { // 脚本编辑略微麻烦点 const before = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了 const script = document.createElement("script"); script.src = `/project/functions.js?v=${Date.now()}`; document.body.appendChild(script); await new Promise((res) => { script.onload = () => res("success"); }); const after = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; // 找到差异的函数 for (const mod in before) { const fns = before[mod]; for (const id in fns) { const fn = fns[id]; if (typeof fn !== "function" || id === "hasSpecial") continue; const now = after[mod][id]; if (fn.toString() !== now.toString()) { try { if (mod === "events") { core.events.eventdata[id] = now; } else if (mod === "enemys") { core.enemys.enemydata[id] = now; } else if (mod === "actions") { core.actions.actionsdata[id] = now; } else if (mod === "control") { core.control.controldata[id] = now; } else if (mod === "ui") { core.ui.uidata[id] = now; } core.updateStatusBar(true, true); console.log(`function hot reload: ${mod}.${id}`); } catch (e) { console.error(e); } } } } } } /** * 属性热重载,包括全塔属性等 * @param {string} data */ async function reloadData(data) { const script = document.createElement("script"); script.src = `/project/${data}.js?v=${Date.now()}`; document.body.appendChild(script); await new Promise((res) => { script.onload = () => res("success"); }); let after; if (data === "data") after = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; if (data === "enemys") after = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; if (data === "icons") after = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; if (data === "items") after = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a; if (data === "maps") after = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; if (data === "events") after = events_c12a15a8_c380_4b28_8144_256cba95f760; if (data === "enemys") { core.enemys.enemys = after; for (var enemyId in after) { core.enemys.enemys[enemyId].id = enemyId; } core.material.enemys = core.getEnemys(); } else if (data === "icons") { core.icons.icons = after; core.material.icons = core.getIcons(); } else if (data === "items") { core.items.items = after; for (var itemId in after) { core.items.items[itemId].id = itemId; } core.material.items = core.getItems(); } else if (data === "maps") { core.maps.blocksInfo = after; core.status.mapBlockObjs = {}; core.status.number2block = {}; Object.values(core.status.maps).forEach((v) => delete v.blocks); core.extractBlocks(); core.setWeather( core.animateFrame.weather.type, core.animateFrame.weather.level ); core.drawMap(); } else if (data === "events") { core.events.commonEvent = after.commonEvent; } else if (data === "data") { location.reload(); } core.updateStatusBar(true, true); console.log(`data hot reload: ${data}`); } // 初始化 (async function () { const data = await post("/reload", "POST", "test"); if (data === "@error") { console.log(`未检测到node服务,热重载插件将无法使用`); } else { console.log(`热重载插件加载成功`); // reload setInterval(async () => { const res = await post("/reload", "POST"); if (res === "@error") return; if (res === "true") location.reload(); else return; }, 1000); // hot reload setInterval(async () => { const res = await post("/hotReload", "POST"); const data = res.split("@@"); data.forEach((v) => { if (v === "") return; const [type, file] = v.split(":"); if (type === "css") reloadCss(file); if (type === "data") reloadData(file); if (type === "floor") reloadFloor(file); if (type === "script") reloadScript(file); }); }, 1000); } })(); }, "statusBar": function () { main.dom.floorMsgGroup.style.display = "none"; main.dom.statusBar.style.display = "none"; main.dom.toolBar.style.display = "none"; const GAMEVIEW_WIDTH = 676; //横屏画面宽度 const GAMEVIEW_HEIGHT = 416; //横屏画面高度 const GAMEVIEW_WIDTH_VERTICAL = 416; //竖屏画面宽度 const GAMEVIEW_HEIGHT_VERTICAL = 676; //竖屏画面高度 const BAR_WIDTH = 130; //横屏左侧额外距离(即边栏宽度) const BAR_HEIGHT_VERTICAL = 130; //竖屏上侧额外距离(即边栏高度) const BORDER_WIDTH = 0; //游戏画面左侧偏移距离 const BORDER_HEIGHT = 0; //游戏画面上侧偏移距离 const FORCE_COUNTABLE_ITEMS = ["centerFly"]; //常态显示数量的非永久道具,如果道具不在此数组中,则只有道具多余1时显示数量 const outerBackground = document.createElement("canvas"); //背景画布设置 outerBackground.style.position = "absolute"; outerBackground.style.zIndex = 5; outerBackground.id = "outerBackground"; main.dom.outerBackground = outerBackground; main.dom.startPanel.insertAdjacentElement("afterend", outerBackground); const outerUI = document.createElement("canvas"); //额外ui画布设置(状态栏所有绘制、点击都在额外ui上) outerUI.style.position = "absolute"; outerUI.style.zIndex = 165; outerUI.id = "outerUI"; main.dom.outerUI = outerUI; outerBackground.insertAdjacentElement("afterend", outerUI); setTimeout(function () { // Should be executed immediately after init() main.canvas.outerUI = outerUI.getContext("2d"); }); outerUI.onclick = function (e) { try { //e.preventDefault(); if (!core.isPlaying()) return false; const left = core.dom.gameGroup.offsetLeft; const top = core.dom.gameGroup.offsetTop; const px = Math.floor((e.clientX - left) / core.domStyle.scale), py = Math.floor((e.clientY - top) / core.domStyle.scale); core.ui.statusBar.onclick(px, py); } catch (ee) { main.log(ee); } }; outerUI.onmousemove = function (e) { try { //e.preventDefault(); if (!core.isPlaying()) return false; const left = core.dom.gameGroup.offsetLeft; const top = core.dom.gameGroup.offsetTop; const px = Math.floor((e.clientX - left) / core.domStyle.scale), py = Math.floor((e.clientY - top) / core.domStyle.scale); core.ui.statusBar.onmove(px, py); } catch (ee) { main.log(ee); } }; // 排行榜 function checkLeaderboardOrientation() { let leaderboardBtn = document.getElementById('leaderboardBtn') leaderboardBtn.style.fontSize = `${parseFloat(core.dom.gameGroup.style.width)*0.03}px` if (typeof core !== 'undefined' && core.domStyle && core.domStyle.isVertical !== undefined) { if (core.domStyle.isVertical) { document.body.classList.add('isVertical'); leaderboardBtn.style.top = `3%`; leaderboardBtn.style.left = `72%`; leaderboardBtn.style.width = `20%`; leaderboardBtn.style.height = `6%`; leaderboardBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.03}px` leaderboardBtn.style.border = `${4*core.domStyle.scale}px solid black` } else { document.body.classList.remove('isVertical'); leaderboardBtn.style.top = `3%`; leaderboardBtn.style.left = `83%`; leaderboardBtn.style.width = `15%`; leaderboardBtn.style.height = `10%`; leaderboardBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.05}px` leaderboardBtn.style.border = `${4*core.domStyle.scale}px solid black` } } } // 检查实际的 core.domStyle.isVertical 状态(如果存在) function checkOrientation() { let updateLogBtn = document.getElementById('updateLogBtn') updateLogBtn.style.fontSize = `${parseFloat(core.dom.gameGroup.style.width)*0.04}px` if (typeof core !== 'undefined' && core.domStyle && core.domStyle.isVertical !== undefined) { if (core.domStyle.isVertical) { document.body.classList.add('isVertical'); updateLogBtn.style.top = `3%`; updateLogBtn.style.left = `2%`; updateLogBtn.style.width = `20%`; updateLogBtn.style.height = `6%`; updateLogBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.03}px` updateLogBtn.style.border = `${4*core.domStyle.scale}px solid black` } else { document.body.classList.remove('isVertical'); updateLogBtn.style.top = `3%`; updateLogBtn.style.left = `2%`; updateLogBtn.style.width = `15%`; updateLogBtn.style.height = `10%`; updateLogBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.05}px` updateLogBtn.style.border = `${4*core.domStyle.scale}px solid black` } } } function checkLogBtn() { let LogBtn = document.getElementById('LogBtn') LogBtn.style.fontSize = `${parseFloat(core.dom.gameGroup.style.width)*0.03}px` if (typeof core !== 'undefined' && core.domStyle && core.domStyle.isVertical !== undefined) { if (core.domStyle.isVertical) { document.body.classList.add('isVertical'); LogBtn.style.top = `10%`; LogBtn.style.left = `72%`; LogBtn.style.width = `20%`; LogBtn.style.height = `6%`; LogBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.03}px` LogBtn.style.border = `${4*core.domStyle.scale}px solid black` } else { document.body.classList.remove('isVertical'); LogBtn.style.top = `14%`; LogBtn.style.left = `83%`; LogBtn.style.width = `15%`; LogBtn.style.height = `10%`; LogBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.05}px` LogBtn.style.border = `${4*core.domStyle.scale}px solid black` } } } const _resize_gameGroup = function (obj) { //游戏画面自适应调节 const gameGroup = core.dom.gameGroup; gameGroup.style.width = obj.totalWidth + "px"; gameGroup.style.height = obj.totalHeight + "px"; gameGroup.style.left = (obj.clientWidth - obj.totalWidth) / 2 + "px"; gameGroup.style.top = (obj.clientHeight - obj.totalHeight) / 2 + "px"; const startCanvas = core.dom.startCanvas startCanvas.style.width = obj.totalWidth + "px"; startCanvas.style.height = obj.totalHeight + "px"; //floorMsgGroup为切换楼层中生效,显示时间可通过‘全塔属性’——‘切换楼层时间’或游戏内设置调整 //显示内容为游戏名/版本号/楼层名 // floorMsgGroup var floorMsgGroup = core.dom.floorMsgGroup; var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; floorMsgGroup.style = globalAttribute.floorChangingStyle; floorMsgGroup.style.height = floorMsgGroup.style.width = GAMEVIEW_HEIGHT * core.domStyle.scale + "px"; floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px"; if (core.domStyle.isVertical) { floorMsgGroup.style.left = "0px"; floorMsgGroup.style.top = ((GAMEVIEW_HEIGHT_VERTICAL - GAMEVIEW_WIDTH_VERTICAL) * core.domStyle.scale) / 2 + "px"; } else { floorMsgGroup.style.left = ((GAMEVIEW_WIDTH - GAMEVIEW_HEIGHT) * core.domStyle.scale) / 2 + "px"; floorMsgGroup.style.top = "0px"; } core.dom.musicBtn.style.right = (obj.clientWidth - obj.totalWidth) / 2 + "px"; core.dom.musicBtn.style.bottom = (obj.clientHeight - obj.totalHeight) / 2 - 9 + "px"; let startBackground = core.domStyle.isVertical ? main.styles.startVerticalBackground || main.styles.startBackground : main.styles.startBackground; if (main.dom.startBackground.getAttribute("__src__") != startBackground) { main.dom.startBackground.setAttribute("__src__", startBackground); main.dom.startBackground.src = startBackground; } }; const _resize_canvas = function (obj) { //自适应画布 main.dom.outerBackground.style.width = obj.totalWidth + "px"; main.dom.outerBackground.style.height = obj.totalHeight + "px"; main.dom.outerUI.style.width = obj.totalWidth + "px"; main.dom.outerUI.style.height = obj.totalHeight + "px"; main.dom.select.style.width = obj.totalWidth + "px"; main.dom.select.style.height = obj.totalHeight + "px"; main.dom.boss1.style.width = obj.totalWidth + "px"; main.dom.boss1.style.height = obj.totalHeight + "px"; main.dom.boss2.style.width = obj.totalWidth + "px"; main.dom.boss2.style.height = obj.totalHeight + "px"; main.dom.boss3.style.width = obj.totalWidth + "px"; main.dom.boss3.style.height = obj.totalHeight + "px"; main.dom.boss4.style.width = obj.totalWidth + "px"; main.dom.boss4.style.height = obj.totalHeight + "px"; main.dom.boss5.style.width = obj.totalWidth + "px"; main.dom.boss5.style.height = obj.totalHeight + "px"; main.dom.choose.style.width = obj.totalWidth + "px"; main.dom.choose.style.height = obj.totalHeight + "px"; // perf 8: HD compatibility. const innerSize = obj.canvasWidth * core.domStyle.scale + "px"; for (let i = 0; i < core.dom.gameCanvas.length; ++i) { core.dom.gameCanvas[i].style.width = innerSize; core.dom.gameCanvas[i].style.height = innerSize; } core.dom.gif.style.width = core.dom.gif.style.height = innerSize; core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize; core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = innerSize; core.dom.gameDraw.style.top = obj.gameDrawBox.top * core.domStyle.scale + "px"; core.dom.gameDraw.style.left = obj.gameDrawBox.left * core.domStyle.scale + "px"; // resize bigmap core.bigmap.canvas.forEach(function (cn) { const ratio = core.canvas[cn].canvas.hasAttribute("isHD") ? core.domStyle.ratio * devicePixelRatio : 1; core.canvas[cn].canvas.style.width = (innerSize / ratio) * core.domStyle.scale + "px"; core.canvas[cn].canvas.style.height = (innerSize / ratio) * core.domStyle.scale + "px"; }); // resize dynamic canvas for (const name in core.dymCanvas) { const ctx = core.dymCanvas[name], canvas = ctx.canvas; const ratio = canvas.hasAttribute("isHD") ? core.domStyle.ratio * devicePixelRatio : 1; canvas.style.width = (canvas.width / ratio) * core.domStyle.scale + 'px'; canvas.style.height = (canvas.height / ratio) * core.domStyle.scale + 'px'; canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; } // resize next main.dom.next.style.width = main.dom.next.style.height = 5 * core.domStyle.scale + "px"; main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; }; core.control.resize = function () { //自适应,可实现横竖屏切换 if (main.mode == "editor") return; const clientWidth = main.dom.body.clientWidth, clientHeight = main.dom.body.clientHeight; const canvasWidth = core.__PIXELS__; const isVertical = clientHeight > clientWidth; core.domStyle.isVertical = isVertical; const totalWidth = isVertical ? GAMEVIEW_WIDTH_VERTICAL : GAMEVIEW_WIDTH, totalHeight = isVertical ? GAMEVIEW_HEIGHT_VERTICAL : GAMEVIEW_HEIGHT; const maxRatio = Math.min( clientWidth / totalWidth, clientHeight / totalHeight ); core.domStyle.availableScale = []; [1, 1.25, 1.5, 1.75, 2].forEach(function (v) { if (maxRatio >= v) { core.domStyle.availableScale.push(v); } }); if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) { core.domStyle.scale = Math.min(1, maxRatio); } else if ( core.getLocalStorage("scale") == null && core.domStyle.availableScale.length >= 2 ) { core.domStyle.scale = core.domStyle.availableScale[core.domStyle.availableScale.length - 2]; core.setLocalStorage("scale", core.domStyle.scale); } const totalWidthScaled = totalWidth * core.domStyle.scale, totalHeightScaled = totalHeight * core.domStyle.scale; const gameDrawBox = isVertical ? { left: BORDER_WIDTH, top: BAR_HEIGHT_VERTICAL + BORDER_HEIGHT, } : { left: BAR_WIDTH + BORDER_WIDTH, top: BORDER_HEIGHT }; const obj = { clientWidth: clientWidth, clientHeight: clientHeight, canvasWidth: canvasWidth, totalWidth: totalWidthScaled, totalHeight: totalHeightScaled, gameDrawBox: gameDrawBox, globalAttribute: core.status.globalAttribute || core.initStatus.globalAttribute, }; _resize_gameGroup(obj); _resize_canvas(obj); checkLeaderboardOrientation() checkOrientation() checkLogBtn() if (core.startUpdate) core.startUpdate() if (core.ele) core.ele.forEach(v => v.updateStyles()) if (core.dom.select.style.display === "block") core.ui.select.update() if (core.status.automaticRoute == null) core.status.automaticRoute = {}; core.updateStatusBar(); if (core.dom.boss1.style.display === "block") core.boss.update() if (core.dom.choose.style.display === "block") core.ui.choose.update() if (core.status.event && core.status.event.id === "token") core.tokendraw() if (core.status.event && core.status.event.id === "card") core.plugin.card.drawcard() if (core.status.event && core.status.event.id === "gallery") core.plugin.gallery.update() }; const bgctx = main.dom.outerBackground.getContext("2d"); const uictx = main.dom.outerUI.getContext("2d"); bgctx.imageSmoothingEnabled = false uictx.imageSmoothingEnabled = false class StatusBar { constructor() { //道具栏列表 this.itemMx = [ //空位用‘none’填充,当前ui至多3列3行 ["book", "fly", "bomb"], ["downFly", "centerFly", "upFly"], ["earthquake", "superPotion", "pickaxe"], ]; this.itemMx3 = [ ["book", "fly", "bomb"], ["jumpShoes", "bigKey", "superPotion"], ["none", "townMap", "none"], ] this.once = ['book', 'fly', 'wand', 'I385'] //单次点击即可使用的道具列表 } //初始化内容(工具栏/录像操作执行函数) init() { this.toolbarAction = [ [ main.core.openKeyBoard, main.core.openQuickShop, core.plugin.gallery.update, core.doSL, ], [main.core.save, main.core.load, main.core.openSettings, core.doSL], ]; this.replayAction = [ [core.triggerReplay, core.stopReplay, core.rewindReplay], [core.speedDownReplay, core.speedUpReplay, function () { core.control._replay_SL() }], ]; } //更新 update() { this._update_background(); //更新背景 this._update_status() //更新状态栏 this._update_character() //更新角色立绘称号 this._update_items(); //更新道具 this._update_keys(); //更新钥匙 this._update_skillAndDifficult() //更新技能状态 this._update_infoWindow(); //更新道具说明 this._update_toolBox(); //更新工具栏 this._update_cost(); } //更新背景 _update_background() { if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(bgctx, 416, 676) core.maps._setHDCanvasSize(uictx, 416, 676) const bg3 = core.material.images.images["statusBackground2.png"]; //竖屏按钮 bgctx.drawImage( bg3, 0, 0, GAMEVIEW_WIDTH_VERTICAL, GAMEVIEW_HEIGHT_VERTICAL ); core.setTextAlign("outerUI", "center"); } else { core.maps._setHDCanvasSize(bgctx, 676, 416) core.maps._setHDCanvasSize(uictx, 676, 416) const bg3 = core.material.images.images["statusBackground.png"]; //横屏按钮 bgctx.drawImage(bg3, 0, 0, GAMEVIEW_WIDTH, GAMEVIEW_HEIGHT); core.setTextAlign("outerUI", "center"); } } //更新cost _update_cost() { if (!core.domStyle.isVertical) { core.clearMap("outerUI", 0, 330, 130, 86) core.drawImage("outerUI", "cost.png", 18, 335, 28, 28) core.fillText("outerUI", hero.cost, 60, 360, "#66FFCC", "30px STHUPO") core.fillText("outerUI", "/", 84, 359, "#66FFCC", "30px STHUPO") core.fillText("outerUI", hero.costmax, 102, 360, "#66FFCC", "30px STHUPO") if (hero.cost < hero.costmax) core.fillText("outerUI", hero.costCD, 75, 345, "#FFF", "14px cjk") core.drawImage("outerUI", "GALLERYBOOK.png", 40, 366, 53, 33) } else { core.clearMap("outerUI", 275, 546, 60, 130) core.drawImage("outerUI", "cost.png", 288, 556, 28, 28) core.fillText("outerUI", hero.cost, 303, 610, "#66FFCC", "30px STHUPO") core.fillText("outerUI", "—", 303, 630, "#66FFCC", "30px STHUPO") core.fillText("outerUI", hero.costmax, 303, 655, "#66FFCC", "30px STHUPO") if (hero.cost < hero.costmax) core.fillText("outerUI", hero.costCD, 314, 595, "#FFF", "14px cjk") } } //更新状态栏 _update_status(updatedFloorTitle) { if (!updatedFloorTitle && core.status.floorId) { updatedFloorTitle = core.status.maps[core.status.floorId].name; } let ATK = core.getRealStatus("atk") let ATKbet = 0 ATK += flags.cardATK if (hero.hp <= 125 * flags.ratio) { if (core.hasItem("AdrenalineR")) ATK += 30 * flags.ratio if (core.hasItem("AdrenalineH")) ATK += 80 * flags.ratio } if (core.hasItem("StarCoinHammer2") && flags.star.level > 34) ATK += Math.min(Math.floor(hero.money * flags.ratio / (flags.MapStatus?.Map === "魔法学院" ? 1000 : 100)), 1000 * flags.ratio) if (core.hasItem("UtilityKnifeB2") && hero.hp >= 1000 * flags.ratio) ATK += flags.health.count if (core.hasItem("UtilityKnifeS2") && hero.hp >= 800 * flags.ratio) ATK += flags.health.count if (core.hasItem("FlashlightS2")) ATK += Math.floor(flags.star.level * Math.sqrt(flags.ratio)) if (core.status.hero.name === "帕帕拉") ATK += Math.floor(core.getRealStatus("hp") / 300) if (core.status.hero.name === "娜蒂斯") ATK += core.status.hero.myCard.length * flags.ratio if (flags.KingsPower) ATK += 15 * flags.ratio ATKbet += flags.cardFATK + flags.Trident * 3 if (core.hasItem("RevengeHalberd")) ATKbet += Math.floor(hero.statistics.battleDamage / 1000) if (core.hasItem("PremiumSwordShield2")) ATKbet += 2 * flags.hasChip.length if (flags.FinalAttack) ATKbet += 50 ATK = Math.floor(ATK * (100 + ATKbet) / 100) const ATKbuff = ATK - core.getRealStatus("atk") let DEF = core.getRealStatus("def") let DEFbet = 0 if (flags.jasmine) DEF = 0 DEF += flags.cardDEF if (flags.Immovable) DEF += 15 * flags.ratio DEFbet += flags.cardFDEF + flags.Trident * 5 if (core.hasItem("PremiumSwordShield2")) DEFbet += 5 * flags.hasChip.length DEF = Math.floor(DEF * (100 + DEFbet) / 100) const DEFbuff = DEF - core.getRealStatus("def") if (!core.domStyle.isVertical) { core.clearMap("outerUI", 0, 0, 130, 220) core.setTextAlign("outerUI", "center") core.fillText("outerUI", flags.MapStatus?.Map ?? "选择地图和角色", 65, 27, "#FFF", "Bold 12px cjk") core.fillText("outerUI", updatedFloorTitle, 65, 53, "#FFF", "Bold 12px cjk") const statusList = ["hp", "atk", "def", "money", "BuffHealth", "BuffStarlight"] let dy = 65 statusList.forEach(v => { core.drawIcon("outerUI", v, 20, dy, 19, 19) dy += 27 }) if (hero.name === "恋") core.fillText("outerUI", core.getRealStatus("hp") + "(" + (flags.mouseShield ?? 0) + "." + (flags.mouseCD ?? 0) * 2 + ")", 83, 80, "#FFF", "Bold 12px cjk") else core.fillText("outerUI", core.getRealStatus("hp"), 83, 80, "#FFF", "Bold 12px cjk") if (flags.HOT) core.fillText("outerUI", "红温警告!", 83, 132, "#FF0000", "Bold 12px cjk") else if (DEFbuff > 0) core.fillText("outerUI", DEF + "(+" + DEFbuff + ")", 83, 132, "#11FF11", "Bold 12px cjk") else if (DEFbuff < 0) core.fillText("outerUI", DEF + "(" + DEFbuff + ")", 83, 132, "red", "Bold 12px cjk") else core.fillText("outerUI", DEF, 83, 132, "#FFF", "Bold 12px cjk") core.fillText("outerUI", core.getRealStatus("money"), 83, 158, "#FFF", "Bold 12px cjk") core.fillText("outerUI", flags.health.count + "+" + flags.health.upbuff, 83, 184, "#FFF", "Bold 12px cjk") core.fillText("outerUI", flags.star.level + "×" + flags.ratio + "(" + flags.star.count + ")", 83, 211, "#FFF", "Bold 12px cjk") if (ATKbuff > 0) core.fillText("outerUI", ATK + "(+" + ATKbuff + ")", 83, 106, "#11FF11", "Bold 12px cjk") else if (ATKbuff < 0) core.fillText("outerUI", ATK + "(" + ATKbuff + ")", 83, 106, "red", "Bold 12px cjk") else core.fillText("outerUI", ATK, 83, 106, "#FFF", "Bold 12px cjk") } else { core.clearMap("outerUI", 0, 0, 210, 130) core.setTextAlign("outerUI", "center") core.fillText("outerUI", flags.MapStatus?.Map ?? "选择地图和角色", 58, 28, "#FFF", "Bold 10.5px cjk") core.fillText("outerUI", updatedFloorTitle, 58, 55, "#FFF", "Bold 10.5px cjk") core.fillText("outerUI", flags.health.count + "+" + flags.health.upbuff, 70, 81, "#FFF", "Bold 10.5px cjk") core.fillText("outerUI", flags.star.level + "×" + flags.ratio + "(" + flags.star.count + ")", 70, 107, "#FFF", "Bold 10.5px cjk") core.drawIcon("outerUI", "BuffHealth", 11, 68, 20, 20) core.drawIcon("outerUI", "BuffStarlight", 11, 94, 20, 20) const statusList = ["hp", "atk", "def", "money"] let dy = 16 statusList.forEach(v => { core.drawIcon("outerUI", v, 110, dy, 20, 20) dy += 26 }) if (hero.name === "恋") core.fillText("outerUI", core.getRealStatus("hp") + "(" + (flags.mouseShield ?? 0) + "." + (flags.mouseCD ?? 0) * 2 + ")", 170, 28, "#FFF", "Bold 10.5px cjk") else core.fillText("outerUI", core.getRealStatus("hp"), 170, 28, "#FFF", "Bold 10.5px cjk") if (ATKbuff > 0) core.fillText("outerUI", ATK + "(+" + ATKbuff + ")", 170, 54, "#11FF11", "Bold 10.5px cjk") else if (ATKbuff < 0) core.fillText("outerUI", ATK + "(" + ATKbuff + ")", 170, 54, "red", "Bold 10.5px cjk") else core.fillText("outerUI", ATK, 170, 54, "#FFF", "Bold 10.5px cjk") if (flags.HOT) core.fillText("outerUI", "红温警告!", 170, 80, "#FF0000", "Bold 10.5px cjk") else if (DEFbuff > 0) core.fillText("outerUI", DEF + "(+" + DEFbuff + ")", 170, 80, "#11FF11", "Bold 10.5px cjk") else if (DEFbuff < 0) core.fillText("outerUI", DEF + "(" + DEFbuff + ")", 170, 80, "red", "Bold 10.5px cjk") else core.fillText("outerUI", DEF, 170, 80, "#FFF", "Bold 10.5px cjk") core.fillText("outerUI", core.getRealStatus("money"), 170, 106, "#FFF", "Bold 10.5px cjk") } } // 绘制角色立绘和名称 _update_character() { const character = core.plugin.Character[hero.name] if (!core.domStyle.isVertical) { core.clearMap("outerUI", 546, 0, 130, 120) core.plugin.utils.fillTextVertical("outerUI", character.title, 568, 72, "#FFF", 12, "cjk") core.drawImage("outerUI", character.Main, 591, 13, 78, 105) } else { core.clearMap("outerUI", 280, 0, 136, 130) core.plugin.utils.fillTextVertical("outerUI", character.title, 300, 72, "#FFF", 10.5, "cjk") core.drawImage("outerUI", character.Main, 323, 15, 78, 105) } } _update_items() { //更新道具栏 const drawItemMx = (drawFn) => { let itemList if (flags.MapStatus?.Map === "水乡古镇" || flags.MapStatus?.Map === "龙宫游乐园") itemList = this.itemMx3 else if (flags.MapStatus?.Map === "星趴·梦想号" || flags.MapStatus?.Map === "御魂庆典" || flags.MapStatus?.Map === "魔法学院" || flags.MapStatus?.Map === "新手教程" || flags.MapStatus?.Map === "进阶教程") itemList = this.itemMx else return for (let i = 0; i < itemList.length; i++) { for (let j = 0; j < itemList[i].length; j++) { var item = itemList[i][j]; drawFn(i, j, item); } } }; if (core.domStyle.isVertical) { const drawItem = (item, posx, posy) => { const icon = core.material.icons.items[item], image = core.material.images.items; core.drawImage( "outerUI", image, 0, 32 * icon, 32, 32, posx, posy, 36, 36 ); const cnt = core.itemCount(item); if ( (core.items.items[item].cls === "tools" && cnt > 1) || FORCE_COUNTABLE_ITEMS.includes(item) ) { core.fillText( "outerUI", cnt, posx + 26, posy + 30, "#FFFFFF", "bold 12px cjk" ); } }; core.clearMap("outerUI", 0, 546, 140, 130) drawItemMx((i, j, item) => { if (core.hasItem(item)) { const posx = 20 + j * 36, posy = 554 + i * 36; drawItem(item, posx, posy); } }); } else { const drawItem = (item, posx, posy) => { const icon = core.material.icons.items[item], image = core.material.images.items; core.drawImage( "outerUI", image, 0, 32 * icon, 32, 32, posx, posy, 32, 32 ); const cnt = core.itemCount(item); if ( (core.items.items[item].cls === "tools" && cnt > 1) || FORCE_COUNTABLE_ITEMS.includes(item) ) { core.fillText( "outerUI", cnt, posx + 25, posy + 28, "#FFFFFF", "bold 12px cjk" ); } }; core.clearMap("outerUI", 0, 220, 130, 110); drawItemMx((i, j, item) => { if (core.hasItem(item)) { const posx = 17 + j * 32, posy = 225 + i * 32; drawItem(item, posx, posy); } }); } } drawKey(key, x, y) { let sx = 0, sy = 0; if (key == "yellowKey") sx += 13; else if (key == "blueKey") sx += 26; else if (key == "greenKey") sx += 39; core.drawImage("outerUI", "maba.png", sx, sy, 13, 26, x, y, 13, 26); } //更新钥匙 _update_keys() { const drawKeyList = (baseX, baseY) => { const todraw = [], keyList = ['yellowKey', 'blueKey', 'redKey']; let total = 0; keyList.forEach(function (key, i) { todraw[i] = core.itemCount(key); total += todraw[i]; }); let dn = 2; for (let i = 0; i <= dn; i++) { let delta = i * 35; if (core.domStyle.isVertical) { this.drawKey(keyList[i], baseX + delta + 12, baseY); } else { this.drawKey(keyList[i], baseX + delta - 8, baseY); } core.setFont("outerUI", 'bold 14px cjk'); core.setTextAlign("outerUI", "center"); if (core.domStyle.isVertical) { core.fillText("outerUI", todraw[i], baseX + delta + 18, baseY + 33, "white"); } else { core.fillText("outerUI", todraw[i], baseX + delta - 3, baseY + 33, "white"); } } }; if (core.domStyle.isVertical) { core.clearMap( "outerUI", 150, 555, 120, 50 ); drawKeyList(153, 560); } else { core.clearMap("outerUI", 546, 120, 130, 60); drawKeyList(579, 132); } } // 绘制难易度和更新技能按钮 _update_skillAndDifficult(mouse) { if (hero.ActiveCD < 0) hero.ActiveCD = 0 if (!flags.MapStatus || flags.MapStatus?.Map === "新手教程") return else if (flags.MapStatus?.Map === "进阶教程" && !flags.Skills) return if (!core.domStyle.isVertical) { core.clearMap("outerUI", 546, 180, 130, 75) core.fillBoldText("outerUI", flags.MapStatus?.Hard, 596, 210, "#FFF", '#000', 3, "10.5px STHUPO") if (hero.name !== "照") { if (!mouse && hero.ActiveCD === 0) core.drawImage("outerUI", "skill_on.png", 552, 220, 60, 25) if (mouse && hero.ActiveCD === 0) core.drawImage("outerUI", "skill_set.png", 552, 220, 60, 25) if (hero.ActiveCD > 0) { core.drawImage("outerUI", "skill_off.png", 552, 220, 60, 25) core.fillRoundRect("outerUI", 554, 222, 56, 22, 11, "#000000B2") core.setTextAlign("center") if (hero.ActiveCD < Infinity) core.fillText("outerUI", hero.ActiveCD, 582, 240, "#FFF", "Bold 22px cjk") } } else { if (!mouse && flags.Teru.Active > 0) core.drawImage("outerUI", "skill_on.png", 552, 220, 60, 25) if (mouse && flags.Teru.Active > 0) core.drawImage("outerUI", "skill_set.png", 552, 220, 60, 25) if (flags.Teru.Active === 0) { core.drawImage("outerUI", "skill_off.png", 552, 220, 60, 25) core.fillRoundRect("outerUI", 554, 222, 56, 22, 11, "#000000B2") core.setTextAlign("center") core.fillText("outerUI", hero.ActiveCD, 582, 240, "#FFF", "Bold 22px cjk") } else { core.fillText("outerUI", flags.Teru.Active, 560, 254, "#6FC", "16px cjk") if (flags.Teru.Active < 3) core.fillText("outerUI", hero.ActiveCD, 600, 254, "#FFF", "16px cjk") } } } else { core.clearMap("outerUI", 150, 605, 125, 70) core.fillBoldText("outerUI", flags.MapStatus?.Hard, 188, 649, "#FFF", '#000', 3, "10.5px STHUPO") if (hero.name !== "照") { if (!mouse && hero.ActiveCD === 0) core.drawImage("outerUI", "skill_on.png", 203, 626, 60, 25) if (mouse && hero.ActiveCD === 0) core.drawImage("outerUI", "skill_set.png", 203, 626, 60, 25) if (hero.ActiveCD > 0) { core.drawImage("outerUI", "skill_off.png", 203, 626, 60, 25) core.fillRoundRect("outerUI", 205, 628, 56, 22, 11, "#000000B2") core.setTextAlign("center") if (hero.ActiveCD < Infinity) core.fillText("outerUI", hero.ActiveCD, 233, 646, "#FFF", "Bold 22px cjk") } } else { if (!mouse && flags.Teru.Active > 0) core.drawImage("outerUI", "skill_on.png", 203, 626, 60, 25) if (mouse && flags.Teru.Active > 0) core.drawImage("outerUI", "skill_set.png", 203, 626, 60, 25) if (flags.Teru.Active === 0) { core.drawImage("outerUI", "skill_off.png", 203, 626, 60, 25) core.fillRoundRect("outerUI", 205, 628, 56, 22, 11, "#000000B2") core.setTextAlign("center") core.fillText("outerUI", hero.ActiveCD, 233, 646, "#FFF", "Bold 22px cjk") } else { core.fillText("outerUI", flags.Teru.Active, 213, 662, "#6FC", "16px cjk") if (flags.Teru.Active < 3) core.fillText("outerUI", hero.ActiveCD, 251, 662, "#FFF", "16px cjk") } } } } _update_infoWindow() { const itemId = this.selectedItem; let text = ""; if (this.selectedItem) { text = core.replaceText(core.material.items[itemId]?.text); if (text[0] == "," || text[0] == ",") text = text.substring(1); } if (!core.domStyle.isVertical) { core.clearMap("outerUI", 546, 255, 130, 88); if (this.selectedItem) { core.setTextAlign("outerUI", "center"); core.drawIcon("outerUI", itemId, 555, 258, 24, 24); core.fillText("outerUI", core.material.items[itemId].name, 630, 274, "#FFF", "10px cjk"); core.ui.drawTextContent("outerUI", text, { left: 556, top: 294, maxWidth: 112, align: "left", color: "#FFF", fontSize: 8, font: "cjk" }); } } } showItemInfo(itemId) { //展示道具说明 this.selectedItem = itemId; this._update_infoWindow(); } clearItemInfo() { //清除道具说明 this.selectedItem = null; this._update_infoWindow(); } _update_toolBox() { const tools = core.isReplaying() ? [ [core.status.replay.pausing ? "play" : "pause", "stop", "rewind"], ["speedDown", "speedUp", "save"], ] : [ ["keyboard", "shop", "itembag", "T371"], ["save", "load", "settings", "T392"], ]; if (core.domStyle.isVertical) { core.clearMap("outerUI", 335, 546, 81, 130) for (let i = 0; i < tools.length; i++) { for (let j = 0; j < tools[i].length; j++) { core.drawIcon( "outerUI", tools[i][j], 344 + i * 27, 558 + j * 27, 26, 26 ); } } } else { core.clearMap("outerUI", 546, 343, 130, 73); for (let i = 0; i < tools.length; i++) { for (let j = 0; j < tools[i].length; j++) { core.drawIcon( "outerUI", tools[i][j], 557 + j * 27, 350 + i * 27, 26, 26, ); } } } } onclick(x, y) { const makeBox = ([x, y], [w, h]) => { return [ [x, y], [x + w, y + h], ]; }; const gridify = ([x, y], [gw, gh]) => { return [Math.floor(x / gw), Math.floor(y / gh)]; }; const useItem = (itemId) => { if (!core.hasItem(itemId)) return; if (itemId != this.selectedItem) { this.showItemInfo(itemId); if (this.once.indexOf(itemId) >= 0) { switch (itemId) { case "centerFly": core.ui._drawCenterFly(); break; case "book": core.openBook(true); break; case "fly": core.useFly(true) break; case "wand": core.plugin.wand() break; case "I385": core.plugin.Gallery() break; default: core.useItem(itemId); } } } else { switch (itemId) { case "centerFly": core.ui._drawCenterFly(); break; case "book": core.openBook(true); break; case "fly": core.useFly(true) break; case "wand": core.plugin.wand() break; case "I385": core.plugin.Gallery() break; default: core.useItem(itemId); } } }; const inRect = ([x, y], [ [sx, sy], [dx, dy] ]) => { return sx <= x && x <= dx && sy <= y && y <= dy; }; const relativeTo = ([x, y], [ax, ay]) => { return [x - ax, y - ay]; }; const pos = [x, y]; if (core.domStyle.isVertical) { const itemBox = makeBox( [20, 554], [36 * 3, 36 * 3] ); if (inRect(pos, itemBox)) { let itemList if (flags.MapStatus?.Map === "星趴·梦想号" || flags.MapStatus?.Map === "御魂庆典" || flags.MapStatus?.Map === "魔法学院" || flags.MapStatus?.Map === "新手教程" || flags.MapStatus?.Map === "进阶教程") itemList = this.itemMx else if (flags.MapStatus?.Map === "水乡古镇" || flags.MapStatus?.Map === "龙宫游乐园") itemList = this.itemMx3 else return const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [36, 36]); const itemId = itemList[gy][gx]; if ( (core.status.event.id == "viewMaps" || core.status.event.id == "fly") && itemId === "book" ) core.openBook(true); if ( core.isReplaying() || core.status.lockControl || core.isMoving() ) return; useItem(itemId); return; } const toolBox = makeBox( [344, 558], [27 * 2, 27 * 4] ); if (inRect(pos, toolBox)) { const [col, row] = gridify(relativeTo(pos, toolBox[0]), [27, 27]); if (core.isReplaying()) { if (flags.replayToken) return core.drawTip("录像选取筹码中,请勿操作") this.replayAction[col][row].call(core); } else if (core.isPlaying() && !(core.isReplaying() || core.status.lockControl || core.isMoving())) { if (col === 0 && row === 3) { core.doSL("autoSave", "load"); } else if (col === 1 && row === 3) { core.doSL("autoSave", "reload"); } else if (col === 0 && row === 2) { core.plugin.gallery.update() } else { this.toolbarAction[col][row].call(core, true); } } return; } const skillBox = makeBox([203, 626], [60, 25]) if (inRect(pos, skillBox) && (core.isPlaying() && !(core.isReplaying() || core.status.lockControl || core.isMoving()))) { if (!flags.MapStatus || flags.MapStatus?.Map === "新手教程") return else if (flags.MapStatus?.Map === "进阶教程" && !flags.Skills) return if (hero.ActiveCD === 0 || (hero.name === "照" && flags.Teru.Active > 0)) { core.plugin.Character[hero.name].Active() core.status.route.push("skill") core.updateStatusBar() } } const cardui = makeBox([221, 32], [48, 70]) if (inRect(pos, cardui) && (core.isPlaying() && !(core.isReplaying() || core.isMoving())) && !flags.cardlock) { if (core.status.event.id === "card") core.plugin.card.close() else if (core.status.event.id === null) core.plugin.card.drawcard() } } else { const itemBox = makeBox( [17, 225], [32 * 3, 32 * 3] ); if (inRect(pos, itemBox)) { let itemList if (flags.MapStatus?.Map === "星趴·梦想号" || flags.MapStatus?.Map === "御魂庆典" || flags.MapStatus?.Map === "魔法学院" || flags.MapStatus?.Map === "新手教程" || flags.MapStatus?.Map === "进阶教程") itemList = this.itemMx else if (flags.MapStatus?.Map === "水乡古镇" || flags.MapStatus?.Map === "龙宫游乐园") itemList = this.itemMx3 else return const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [32, 32]); const itemId = itemList[gy][gx]; if ( (core.status.event.id == "viewMaps" || core.status.event.id == "fly") && itemId === "book" ) core.openBook(true); if ( core.isReplaying() || core.status.lockControl || core.isMoving() ) return; useItem(itemId); return; } const toolBox = makeBox( [557, 350], [27 * 4, 27 * 2] ); if (inRect(pos, toolBox)) { const [row, col] = gridify(relativeTo(pos, toolBox[0]), [27, 27]); if (core.isReplaying()) { if (flags.replayToken) return core.drawTip("录像选取筹码中,请勿操作") this.replayAction[col][row].call(core); } else if (core.isPlaying() && !(core.isReplaying() || core.status.lockControl || core.isMoving())) { if (col === 0 && row === 3) { core.doSL("autoSave", "load"); } else if (col === 1 && row === 3) { core.doSL("autoSave", "reload"); } else if (col === 0 && row === 2) { core.plugin.gallery.update() } else { this.toolbarAction[col][row].call(core, true); } } return; } const skillBox = makeBox([552, 220], [60, 25]) if (inRect(pos, skillBox) && (core.isPlaying() && !(core.isReplaying() || core.status.lockControl || core.isMoving()))) { if (!flags.MapStatus || flags.MapStatus?.Map === "新手教程") return else if (flags.MapStatus?.Map === "进阶教程" && !flags.Skills) return if (hero.ActiveCD === 0 || (hero.name === "照" && flags.Teru.Active > 0)) { core.plugin.Character[hero.name].Active() core.status.route.push("skill") core.updateStatusBar() } } const cardui = makeBox([615, 181], [48, 70]) if (inRect(pos, cardui) && (core.isPlaying() && !(core.isReplaying() || core.isMoving())) && !flags.cardlock) { if (core.status.event.id === "card") core.plugin.card.close() else if (core.status.event.id === null) core.plugin.card.drawcard() } if (inRect(pos, makeBox([18, 366], [97, 33])) && (core.isPlaying() && !(core.isReplaying() || core.status.lockControl || core.isMoving()))) core.plugin.gallery.update() } } onmove(x, y) { if (!flags.MapStatus || flags.MapStatus?.Map === "新手教程") return else if (flags.MapStatus?.Map === "进阶教程" && !flags.Skills) return const makeBox = ([x, y], [w, h]) => { return [ [x, y], [x + w, y + h], ]; }; const inRect = ([x, y], [ [sx, sy], [dx, dy] ]) => { return sx <= x && x <= dx && sy <= y && y <= dy; }; const pos = [x, y]; if (core.domStyle.isVertical) { const skillBox = makeBox([203, 626], [60, 25]) if (inRect(pos, skillBox) && (core.isPlaying() && !(core.isReplaying() || core.status.lockControl || core.isMoving()))) { if (hero.ActiveCD === 0 || (hero.name === "照" && flags.Teru.Active > 0)) { this._update_skillAndDifficult(true) } } else this._update_skillAndDifficult(false) } else { const skillBox = makeBox([552, 220], [60, 25]) if (inRect(pos, skillBox) && (core.isPlaying() && !(core.isReplaying() || core.status.lockControl || core.isMoving()))) { if (hero.ActiveCD === 0 || (hero.name === "照" && flags.Teru.Active > 0)) { this._update_skillAndDifficult(true) } } else this._update_skillAndDifficult(false) } } } core.ui.statusBar = new StatusBar(); core.control.clearStatusBar = function () { core.clearMap("outerUI"); }; // init() called in `afterLoadResources`. }, "override": function () { core.statusBar.icons = { floor: 0, name: null, lv: 1, hpmax: 2, hp: 3, atk: 4, def: 5, mdef: 6, money: 7, exp: 8, up: 9, book: 10, fly: 11, toolbox: 12, keyboard: 13, shop: 14, save: 15, load: 16, settings: 17, play: 18, pause: 19, stop: 20, speedDown: 21, speedUp: 22, rewind: 23, equipbox: 24, mana: 25, skill: 26, exit: 27, btn1: 28, btn2: 29, btn3: 30, btn4: 31, btn5: 32, btn6: 33, btn7: 34, alt: 35, keys: 36, help: 37, battle: 38 }; core.actions._getClickLoc = function (x, y) { var size = 32 * core.domStyle.scale; var left = main.dom.gameDraw.offsetLeft + main.dom.gameGroup.offsetLeft; var top = main.dom.gameDraw.offsetTop + main.dom.gameGroup.offsetTop; var loc = { x: Math.max(x - left, 0), y: Math.max(y - top, 0), size: size, }; return loc; }; /*core.ui._drawWindowSelector = function (background, x, y, w, h) { w = Math.round(w) + 48; h = Math.round(h); var ctx = core.ui.createCanvas("_selector", x - 24, y, w, h, 165); ctx.canvas.id = ""; this._drawSelector(ctx, background, w, h); }; core.ui._drawSelector = function (ctx, background, w, h, left, top) { left = left || 0; top = top || 0; ctx = this.getContextByName(ctx); if (!ctx) return; if (typeof background == "string") background = core.material.images.images[background]; if (!(background instanceof Image)) return; // badge ctx.drawImage(background, 132, 68, 24, 24, left + 4, top + 4, 24, 24); ctx.drawImage( background, 132, 68, 24, 24, w - left - 28, top + 4, 24, 24 ); };*/ enemys.prototype._nextCriticals_useBinarySearch = function ( enemy, info, number, x, y, floorId ) { var mon_hp = info.mon_hp, hero_atk = Math.floor(core.getRealStatus("atk")), mon_def = info.mon_def, pre = info.damage; var list = []; var start_atk = hero_atk; if (info.__over__) { start_atk += info.__overAtk__; list.push([info.__overAtk__, -info.damage]); } var calNext = function (currAtk, maxAtk) { var start = Math.floor(currAtk), end = Math.floor(maxAtk); if (start > end) return null; while (start < end) { var mid = Math.floor((start + end) / 2); if (mid - start > end - mid) mid--; var nextInfo = core.enemys.getDamageInfo( enemy, { atk: mid }, x, y, floorId ); if (nextInfo == null || typeof nextInfo == "number") return null; if (pre > nextInfo.damage) end = mid; else start = mid + 1; } var nextInfo = core.enemys.getDamageInfo( enemy, { atk: start }, x, y, floorId ); return nextInfo == null || typeof nextInfo == "number" || nextInfo.damage >= pre ? null : [start, nextInfo.damage]; }; var currAtk = start_atk; while (true) { var next = calNext(currAtk + 1, (mon_hp + mon_def) * 2, pre); if (next == null) break; currAtk = next[0]; pre = next[1]; list.push([currAtk - hero_atk, info.damage - pre]); if (pre <= 0 && !core.flags.enableNegativeDamage) break; if (list.length >= number) break; } if (list.length == 0) list.push([0, 0]); return list; }; core.ui.clearMap = function (name, x, y, width, height) { if (name == "all") { for (var m in core.canvas) { core.canvas[m].clearRect( -32, -32, core.canvas[m].canvas.width + 32, core.canvas[m].canvas.height + 32 ); } core.clearMap("outerUI"); core.dom.gif.innerHTML = ""; core.removeGlobalAnimate(); core.deleteCanvas(function (one) { return one.startsWith("_bigImage_"); }); core.setWeather(null); } else { var ctx = this.getContextByName(name); if (ctx) ctx.clearRect( x || 0, y || 0, width || ctx.canvas.width, height || ctx.canvas.height ); } }; events.prototype.openBook = function (fromUserAction) { if (core.isReplaying()) return; // 如果能恢复事件(从callBook事件触发) if ( core.status.event.id == "book" && core.events.recoverEvents(core.status.event.interval) ) return; // 当前是book,且从“浏览地图”打开 if (core.status.event.id == "book" && core.status.event.ui) { core.status.boxAnimateObjs = []; core.ui._drawViewMaps(core.status.event.ui); return; } // 从“浏览地图”页面打开 if (core.status.event.id == "viewMaps" || core.status.event.id == "fly") { fromUserAction = false; core.status.event.ui = core.status.event.data; } if (!this._checkStatus("book", fromUserAction, true)) return; core.playSound("打开界面"); core.useItem("book", true); }; ////// 怪物手册界面时,放开某个键的操作 ////// core.actions._keyUpBook = function (keycode) { if (keycode == 27 || keycode == 88) { core.playSound("取消"); if (core.events.recoverEvents(core.status.event.interval)) { return; } else if (core.status.event.ui != null) { core.status.boxAnimateObjs = []; if (typeof core.status.event.ui === "number") { core.status.event.id = "fly"; core.ui.drawFly(core.status.event.ui); } else { core.ui._drawViewMaps(core.status.event.ui); } } else core.ui.closePanel(); return; } if (keycode == 13 || keycode == 32 || keycode == 67) { var data = core.status.event.data; if (data != null) { core.ui._drawBookDetail(data); } return; } }; ////// 怪物手册界面的点击操作 ////// actions.prototype._clickBook = function (x, y) { var pageinfo = core.ui._drawBook_pageinfo(); // 上一页 if ( (x == this._HX_ - 2 || x == this._HX_ - 3) && y === core._HEIGHT_ - 1 ) { core.playSound("光标移动"); core.ui.drawBook(core.status.event.data - pageinfo.per_page); return; } // 下一页 if ( (x == this._HX_ + 2 || x == this._HX_ + 3) && y === core._HEIGHT_ - 1 ) { core.playSound("光标移动"); core.ui.drawBook(core.status.event.data + pageinfo.per_page); return; } // 返回 if (x >= this.LAST - 2 && y === core._HEIGHT_ - 1) { core.playSound("取消"); if (core.events.recoverEvents(core.status.event.interval)) { return; } else if (core.status.event.ui != null) { core.status.boxAnimateObjs = []; if (typeof core.status.event.ui === "number") { core.status.event.id = "fly"; core.ui.drawFly(core.status.event.ui); } else { core.ui._drawViewMaps(core.status.event.ui); } } else core.ui.closePanel(); return; } // 怪物信息 var data = core.status.event.data; if (data != null && y < core._HEIGHT_ - 1) { var per_page = pageinfo.per_page, page = parseInt(data / per_page); var u = (core._HEIGHT_ - 1) / per_page; for (var i = 0; i < per_page; ++i) { if (y >= u * i && y < u * (i + 1)) { var index = per_page * page + i; core.ui.drawBook(index); core.ui._drawBookDetail(index); break; } } return; } return; }; ui.prototype.fillBoldText = function (name, text, x, y, style, strokeStyle, lineWidth, font, maxWidth) { var ctx = this.getContextByName(name); if (!ctx) return; if (font) ctx.font = font; if (!style) style = ctx.fillStyle; style = core.arrayToRGBA(style); if (!strokeStyle) strokeStyle = '#000000'; strokeStyle = core.arrayToRGBA(strokeStyle); if (maxWidth != null) { this.setFontForMaxWidth(ctx, text, maxWidth); } ctx.strokeStyle = strokeStyle; ctx.lineWidth = lineWidth ?? 2; ctx.strokeText(text, x, y); ctx.fillStyle = style; ctx.fillText(text, x, y); }; ////// 战斗 ////// events.prototype._sys_battle = function (data, callback) { if (core.getStatus('isStealth')) return // 检查战前事件 var beforeBattle = [] core.push(beforeBattle, core.floors[core.status.floorId].beforeBattle[data.x + ',' + data.y]) core.push(beforeBattle, (core.material.enemys[data.event.id] || {}).beforeBattle) if (beforeBattle.length > 0) { core.push(beforeBattle, [{ type: 'battle', x: data.x, y: data.y }]) core.clearContinueAutomaticRoute() // 自动存档 var inAction = core.status.event.id == 'action' if (inAction) { core.insertAction(beforeBattle, data.x, data.y) core.doAction() } else { core.autosave(true) core.insertAction(beforeBattle, data.x, data.y, callback) } } else { this.battle(data.event.id, data.x, data.y, false, callback) } } events.prototype.battle = function (id, x, y, force, callback) { core.saveAndStopAutomaticRoute(); id = id || core.getBlockId(x, y); const cls = core.getClsFromId(id); if (!id || !cls || !(cls === 'enemys' || cls === 'enemy48')) return core.clearContinueAutomaticRoute(callback); if (!id) return core.clearContinueAutomaticRoute(callback); // 非强制战斗 if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id && !["SeaGawu", "LionGawu", "ADMasao", "SOMasao", "Masao", "Souri", "Mamushi"].includes(id)) { core.stopSound(); core.playSound('操作失败'); core.drawTip("你打不过此怪物!", id); return core.clearContinueAutomaticRoute(callback); } // 自动存档 if (!core.status.event.id && !flags.jianqi) core.autosave(true); // 战前事件 if (!this.beforeBattle(id, x, y)) return core.clearContinueAutomaticRoute(callback); // 战后事件 this.afterBattle(id, x, y, callback) } //点击复写 actions.prototype._sys_ondown_lockControl = function (x, y, px, py) { if (core.status.played && !core.status.lockControl) return false; switch (core.status.event.id) { case "token": core.plugin.clickToken(x, y, px, py) break; case 'centerFly': this._clickCenterFly(x, y, px, py); break; case 'book': this._clickBook(x, y, px, py); break; case 'book-detail': this._clickBookDetail(x, y, px, py); break; case 'fly': this._clickFly(x, y, px, py); break; case 'viewMaps': this._clickViewMaps(x, y, px, py); break; case 'switchs': this._clickSwitchs(x, y, px, py); break; case 'switchs-sounds': this._clickSwitchs_sounds(x, y, px, py); break; case 'switchs-display': this._clickSwitchs_display(x, y, px, py); break; case 'switchs-action': this._clickSwitchs_action(x, y, px, py); break; case 'switchs-preference': this._clickSwitchs_preference(x, y, px, py); break; case 'settings': this._clickSettings(x, y, px, py); break; case 'selectShop': this._clickQuickShop(x, y, px, py); break; case 'equipbox': this._clickEquipbox(x, y, px, py); break; case 'toolbox': this._clickToolbox(x, y, px, py); break; case 'save': case 'load': case 'replayLoad': case 'replayRemain': case 'replaySince': this._clickSL(x, y, px, py); break; case 'confirmBox': this._clickConfirmBox(x, y, px, py); break; case 'keyBoard': this._clickKeyBoard(x, y, px, py); break; case 'action': this._clickAction(x, y, px, py); break; case 'text': core.drawText(); break; case 'notes': this._clickNotes(x, y, px, py); break; case 'syncSave': this._clickSyncSave(x, y, px, py); break; case 'syncSelect': this._clickSyncSelect(x, y, px, py); break; case 'localSaveSelect': this._clickLocalSaveSelect(x, y, px, py); break; case 'storageRemove': this._clickStorageRemove(x, y, px, py); break; case 'cursor': this._clickCursor(x, y, px, py); break; case 'replay': this._clickReplay(x, y, px, py); break; case 'gameInfo': this._clickGameInfo(x, y, px, py); break; case 'about': case 'help': core.ui.closePanel(); break; } // --- 长按判定 if (core.timeout.onDownTimeout == null) { core.timeout.onDownTimeout = setTimeout(function () { if (core.interval.onDownInterval == null) { core.interval.onDownInterval = setInterval(function () { if (!core.actions.longClick(x, y, px, py)) { clearInterval(core.interval.onDownInterval); core.interval.onDownInterval = null; } }, 40) } }, 500); } return true; } core.registerAction('ondown', '_sys_ondown_lockControl', core.actions._sys_ondown_lockControl, 30); ui.prototype._drawTip_drawOne = function (tip, k) { core.setAlpha('data', tip.opacity); core.fillRect('data', 5, 5 + 42 * k, tip.width, 42, '#000000'); if (tip.image) core.drawImage('data', tip.image, (tip.posX + tip.frame) * 32, tip.posY * tip.height, 32, 32, 10, 10 + 42 * k, 32, 32); core.fillText('data', tip.text, tip.textX, 33 + 42 * k, '#FFFFFF'); core.setAlpha('data', 1); } let time = 0 control.prototype._animateFrame_tip = function (timestamp) { let delta if (core.animateFrame.tip.length === 0) return; if (timestamp - time <= 30) return; delta = timestamp - time; time = timestamp; core.setFont("data", "16px Arial"); core.setTextAlign("data", "left"); core.clearMap("data", 0, 0, core._PX_, core._PX_); core.animateFrame.tip.forEach((v, k) => { core.ui._drawTip_drawOne(v, k); if (v.stage == 1) { v.opacity += 0.05; if (v.opacity >= 0.6) { v.stage = 2; v.displayTime = 0; } } else if (v.stage == 2) { v.displayTime += delta; if (v.displayTime >= 1000) v.stage = 3; } else v.opacity -= 0.05; }) core.animateFrame.tip = core.animateFrame.tip.filter(v => v.opacity > 0) }; core.registerAnimationFrame("tip", true, core.control._animateFrame_tip) ui.prototype.drawTip = function (text, id, frame) { core.animateFrame.tip = core.animateFrame.tip ?? [] text = core.replaceText(text) || ""; var realText = this._getRealContent(text); var one = { text: text, textX: 21, width: 26 + core.calWidth('data', realText, "16px Arial"), opacity: 0.1, stage: 1, frame: frame || 0, time: 0 }; if (id != null) { var info = core.getBlockInfo(id); if (info == null || !info.image || info.bigImage) { // 检查状态栏图标 if (core.statusBar.icons[id] instanceof Image) { info = { image: core.statusBar.icons[id], posX: 0, posY: 0, height: 32 }; } else info = null; } if (info != null) { one.image = info.image; one.posX = info.posX; one.posY = info.posY; one.height = info.height; one.textX += 24; one.width += 24; } } core.animateFrame.tip.push(one) if (core.animateFrame.tip.length > 4) core.animateFrame.tip.shift() } control.prototype.getStatusOrDefault = function (status, name) { if (status && name in status) return Math.floor(status[name]); if (name === "atk") return Math.floor(this.getStatus(name) + flags.CharacterATK) if (name === "def") return Math.floor(this.getStatus(name) + flags.CharacterDEF) return Math.floor(this.getStatus(name)); } ////// 1防减伤计算 ////// enemys.prototype.getDefDamage = function (enemy, k, x, y, floorId) { if (typeof enemy == "string") enemy = core.material.enemys[enemy]; k = k || 1; var nowDamage = this._getDamage(enemy, null, x, y, floorId); var nextDamage = this._getDamage( enemy, { def: core.getRealStatus("def") + k }, x, y, floorId ); if (nowDamage == null || nextDamage == null) return "???"; return nowDamage - nextDamage; }; //防御倍数 enemys.prototype._getCurrentEnemys_addEnemy_defDamage = function ( enemy, x, y, floorId ) { var ratio = core.status.maps[floorId || core.status.floorId].ratio || 1; //第一行为按照ratio值计算减防,第二行为1防减伤 //return this.getDefDamage(enemy, ratio, x, y, floorId); return this.getDefDamage(enemy, null, x, y, floorId); }; }, "额外信息": function () { /* 宝石血瓶左下角显示数值 * 注意!!!不要在道具属性中直接操作flags,使用core.status.hero.flags或core.setFlag系列函数代替! * 需要将 变量:itemDetail改为true才可正常运行 * 请尽量减少勇士的属性数量,否则可能会出现严重卡顿(划掉,现在你放一万个属性也不会卡) * 注意:这里的属性必须是core.status.hero里面的,flag无法显示 * 如果不想显示,可以core.setFlag("itemDetail", false); * 然后再core.getItemDetail(); * 如有bug在大群或造塔群@古祠 */ // 忽略的道具 const ignore = ["superPotion"]; // 取消注释下面这句可以减少超大地图的判定。 // 如果地图宝石过多,可能会略有卡顿,可以尝试取消注释下面这句话来解决。 // core.bigmap.threshold = 256; const origin = core.control.updateStatusBar; core.updateStatusBar = core.control.updateStatusBar = function () { if (core.getFlag("__statistics__")) return; else return origin.apply(core.control, arguments); }; core.control.updateDamage = function (floorId, ctx) { floorId = floorId || core.status.floorId; if (!floorId || core.status.gameOver || main.mode != "play") return; const onMap = ctx == null; // 没有怪物手册 if (!core.hasItem("book")) return; core.status.damage.posX = core.bigmap.posX; core.status.damage.posY = core.bigmap.posY; if (!onMap) { const width = core.floors[floorId].width, height = core.floors[floorId].height; // 地图过大的缩略图不绘制显伤 if (width * height > core.bigmap.threshold) return; } this._updateDamage_damage(floorId, onMap); this._updateDamage_extraDamage(floorId, onMap); core.getItemDetail(floorId); // 宝石血瓶详细信息 this.drawDamage(ctx); }; // 获取宝石信息 并绘制 this.getItemDetail = function (floorId) { if (!core.flags.itemDetail) return; if (!core.status.thisMap) return; floorId = floorId ?? core.status.thisMap.floorId; const beforeRatio = core.status.thisMap.ratio; core.status.thisMap.ratio = core.status.maps[floorId].ratio; let diff = {}; const before = core.status.hero; const hero = core.clone(core.status.hero); const handler = { set(target, key, v) { diff[key] = v - (target[key] || 0); if (!diff[key]) diff[key] = void 0; return true; }, }; core.status.hero = new Proxy(hero, handler); core.status.maps[floorId].blocks.forEach(function (block) { if ( block.event.cls !== "items" || ignore.includes(block.event.id) || block.disable ) return; const x = block.x, y = block.y; // v2优化,只绘制范围内的部分 if (core.bigmap.v2) { if ( x < core.bigmap.posX - core.bigmap.extend || x > core.bigmap.posX + core._WIDTH_ + core.bigmap.extend || y < core.bigmap.posY - core.bigmap.extend || y > core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend ) { return; } } diff = {}; const id = block.event.id; const item = core.material.items[id]; if (item.cls === "equips") { // 装备也显示 const diff = item.equip.value ?? {}; const per = item.equip.percentage ?? {}; for (const name in per) { diff[name + "per"] = per[name].toString() + "%"; } drawItemDetail(diff, x, y); return; } if (item.id === "redGem") { if (flags.MapStatus?.Map === "魔法学院" && floorId.startsWith("MAR")) drawItemDetail({ atk: "2" }, x, y) else drawItemDetail({ atk: "1" }, x, y) return; } if (item.id === "redGem2") { if (flags.MapStatus?.Map === "魔法学院" && floorId.startsWith("MAR")) drawItemDetail({ atk: "2~6" }, x, y) else drawItemDetail({ atk: "1~3" }, x, y) return; } if (item.id === "redGem3") { if (flags.MapStatus?.Map === "魔法学院" && floorId.startsWith("MAR")) drawItemDetail({ atk: "2~12" }, x, y) else drawItemDetail({ atk: "1~6" }, x, y) return; } if (item.id === "redGem4") { if (flags.MapStatus?.Map === "魔法学院" && floorId.startsWith("MAR")) drawItemDetail({ atk: "2~20" }, x, y) else drawItemDetail({ atk: "1~10" }, x, y) return; } if (item.id === "blueGem") { if (flags.MapStatus?.Map === "魔法学院" && floorId.startsWith("MAR")) drawItemDetail({ def: "2" }, x, y) else drawItemDetail({ def: "1" }, x, y) return; } if (item.id === "blueGem2") { if (flags.MapStatus?.Map === "魔法学院" && floorId.startsWith("MAR")) drawItemDetail({ def: "2~6" }, x, y) else drawItemDetail({ def: "1~3" }, x, y) return; } if (item.id === "blueGem3") { if (flags.MapStatus?.Map === "魔法学院" && floorId.startsWith("MAR")) drawItemDetail({ def: "2~12" }, x, y) else drawItemDetail({ def: "1~6" }, x, y) return; } if (item.id === "blueGem4") { if (flags.MapStatus?.Map === "魔法学院" && floorId.startsWith("MAR")) drawItemDetail({ def: "2~20" }, x, y) else drawItemDetail({ def: "1~10" }, x, y) return; } // 跟数据统计原理一样 执行效果 前后比较 core.setFlag("__statistics__", true); try { eval(item.itemEffect); } catch (error) {} drawItemDetail(diff, x, y); }); core.status.thisMap.ratio = beforeRatio; core.status.hero = before; window.hero = before; window.flags = before.flags; }; // 绘制 function drawItemDetail(diff, x, y) { const px = 32 * x + 2, py = 32 * y + 30; let content = ""; // 获得数据和颜色 let i = 0; for (const name in diff) { if (!diff[name]) continue; let color = "#fff"; if (typeof diff[name] === "number") content = core.formatBigNumber(diff[name], true); else content = diff[name]; switch (name) { case "atk": case "atkper": color = "#FF7A7A"; break; case "def": case "defper": color = "#00E6F1"; break; case "mdef": case "mdefper": color = "#6EFF83"; break; case "hp": color = "#A4FF00"; break; case "hpmax": case "hpmaxper": color = "#F9FF00"; break; case "mana": color = "#c66"; break; } // 绘制 core.status.damage.data.push({ text: content, px: px, py: py - 10 * i, color: color, }); i++; } } }, "编辑器显伤": function () { // 在此增加新插件 /////// 用户设置 /////// // 将__enable置为false将关闭插件 var __enable = true; // 魔防攻速之类的属性可以在这里加 ['atk', 'def', 'mdef'] var heroStatus = ["atk", "def", "mdef", "hp"]; // saveHero为true 将会把每次造塔测试时的角色数据存下来 否则会读取初始属性 // 用不着可以关了 节约缓存空间 (虽然根本没多少 还没一个存档大 // 也可以手动清理 控制台输入core.removeLocalStorage('editorHero')即可 var saveHero = true; // 下为具体实现 懒得写注释了 大概就是写HTML然后注册交互 if (!__enable || main.mode != "editor") return; core.plugin.initEditorDamage = false; if (heroStatus.length >= 4 && !editor.isMobile) editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + "px"; editor.statusRatio = core.getLocalStorage("statusRatio", 1); editor.saveHero = saveHero; editor._heroStatus = heroStatus; editor.dom.mapEdit.appendChild(core.canvas.damage.canvas); var HTML = ""; //if (heroStatus.length >= 4 && !editor.isMobile) editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + 'px'; heroStatus.forEach(function (status) { var id = status + "set", id2 = status + "add", id3 = status + "rec", id4 = status + "help"; HTML += "
"; }); document.getElementById("viewportButtons").innerHTML = HTML; ["set", "add", "rec", "help"].forEach(function (e) { heroStatus.forEach(function (status) { editor.dom[status + e] = document.getElementById(status + e); }); }); var _hasItem = core.items.hasItem; core.items.hasItem = function (itemId) { if (itemId == "book" && main.mode == "editor") return true; return _hasItem.call(core.items, itemId); }; if (main.mode == "editor") { var applyList = [ "getDamageString", "nextCriticals", "getEnemyInfo", "getEnemyValue", ]; applyList.forEach(function (name) { var func = core.enemys[name]; core.enemys[name] = function () { var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments); if (typeof args[0] == "string") args[0] = core.enemys.enemys[args[0]]; return func.apply(core.enemys, args); }; }); } ////// 获得勇士属性 ////// core.control.getStatus = function (name) { if (!core.status.hero) return null; if (name == "x" || name == "y" || name == "direction") return this.getHeroLoc(name); /*if ( main.mode == 'editor' && !core.hasFlag('__statistics__')) { return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero[name]; }*/ return core.status.hero[name]; }; core.control.updateDamage = function (floorId, ctx) { floorId = floorId || core.status.floorId; if (!floorId || core.status.gameOver) return; var onMap = ctx == null; if (main.mode == "editor") { ctx = core.canvas.damage; core.updateCheckBlock(); core.clearMap(ctx); if (editor.uivalues.bigmap) return; } // 没有怪物手册 if (!core.hasItem("book")) return; core.status.damage.posX = core.bigmap.posX; core.status.damage.posY = core.bigmap.posY; if (!onMap) { var width = core.floors[floorId].width, height = core.floors[floorId].height; // 地图过大的缩略图不绘制显伤 if (width * height > core.bigmap.threshold) return; } this._updateDamage_damage(floorId, onMap); this._updateDamage_extraDamage(floorId, onMap); this.drawDamage(ctx); }; core.control.drawDamage = function (ctx) { if ( core.status.gameOver || !core.status.damage /* || main.mode != 'play'*/ ) return; var onMap = false; if (ctx == null) { ctx = core.canvas.damage; core.clearMap("damage"); onMap = true; } if (onMap && core.bigmap.v2) { // 检查是否需要重算... if ( Math.abs(core.bigmap.posX - core.status.damage.posX) >= core.bigmap.extend - 1 || Math.abs(core.bigmap.posY - core.status.damage.posY) >= core.bigmap.extend - 1 ) { return this.updateDamage(); } } return this._drawDamage_draw(ctx, onMap); }; ////// 以x,y的形式返回每个点的事件 ////// core.maps.getMapBlocksObj = function (floorId, noCache) { floorId = floorId || core.status.floorId; if ( core.status.mapBlockObjs[floorId] && !noCache && main.mode != "editor" ) return core.status.mapBlockObjs[floorId]; var obj = {}; core.extractBlocks(floorId); core.status.maps[floorId].blocks.forEach(function (block) { obj[block.x + "," + block.y] = block; }); core.status.mapBlockObjs[floorId] = obj; return obj; }; this.bignum = function (num, defaultValue) { if (num == null || num == "") return defaultValue; num = num + ""; var list = { w: 1e4, e: 1e8, z: 1e12, j: 1e16, g: 1e20, }; // 浮点数问题 function checkFloat(num) { if (!core.isset(num)) return 0; num = num + ""; var index = num.indexOf("."); if (index < 0) return 0; else return num.slice(index + 1).length; } var index = num.search(/w|e|z|j|g/); if (index <= 0) { num = parseInt(num); if (core.isset(num)) return num; else { alert("不正确的输入"); return defaultValue; } } for (; index > 0; index = num.search(/w|e|z|j|g/)) { var p = num[index], q = list[p], n = num.slice(0, index), m = Math.pow(10, checkFloat(n)); num = (n * m * q) / m + num.slice(index + 1); } return parseInt(num); }; this.updateEditorDamage = function (noSave) { core.updateDamage(); heroStatus.forEach(function (status) { editor.dom[status + "set"].value = core.status.hero[status]; }); if (!noSave && editor.saveHero) core.setLocalStorage("editorHero", core.status.hero); }; var _resizeMap = core.maps.resizeMap; core.maps.resizeMap = function (floorId) { _resizeMap.call(core.maps, floorId); if (!core.plugin.initEditorDamage && main.mode == "editor") { core.plugin.initEditorDamage = true; var editorHero = core.getLocalStorage("editorHero"); if (editorHero && saveHero) core.status.hero = editorHero; else core.removeLocalStorage("editorHero"); editor._heroStatus.forEach(function (e) { editor.dom[e + "set"].onchange = function () { var status = this.id.slice(0, -3); core.status.hero[status] = core.bignum( this.value, core.status.hero[status] ); core.updateEditorDamage(); }; editor.dom[e + "add"].onclick = function () { var status = this.id.slice(0, -3); core.status.hero[status] += editor.statusRatio; core.updateEditorDamage(); }; editor.dom[e + "rec"].onclick = function () { var status = this.id.slice(0, -3); core.status.hero[status] -= editor.statusRatio; core.updateEditorDamage(); }; editor.dom[e + "help"].onclick = function () { var status = this.id.slice(0, -4), name = core.getStatusLabel(status); var ratio = parseInt( prompt( "当前属性:" + name + "\n现在的点击按钮变化值:" + editor.statusRatio + ",请输入按下一次+/-按钮的属性变化量,可以写4w 10.2e这种字母缩写" ) ); if (!core.isset(ratio)) { printe("不合法的输入"); return; } editor.statusRatio = ratio; core.setLocalStorage("statusRatio", ratio); }; }); var _updateMap = editor.updateMap; editor.updateMap = function () { _updateMap.call(editor); core.updateEditorDamage(true); }; editor.mode.onmode = function (mode, callback) { if (editor_mode.mode != mode) { if (mode === "save") { editor_mode.doActionList( editor_mode.mode, editor_mode.actionList, function () { if (callback) callback(); core.updateEditorDamage(); } ); } if (editor_mode.mode === "nextChange" && mode) editor_mode.showMode(mode); if (mode !== "save") editor_mode.mode = mode; editor_mode.actionList = []; } }; } }; }, "手册区分特殊属性": function () { // 在此增加新插件 this.arrsame = function (Arraya, Arrayb) { let a = Arraya || []; let b = Arrayb || []; if (typeof a === "number") a = [a]; if (typeof b === "number") b = [b]; let c = [...a, ...b]; for (const i of c) if (!a.includes(i) || !b.includes(i)) { return false; } return true; }; enemys.prototype.getCurrentEnemys = function (floorId) { floorId = floorId || core.status.floorId; var enemys = [], used = {}; core.extractBlocks(floorId); core.status.maps[floorId].blocks.forEach(function (block) { if (!block.disable && block.event.cls.indexOf("enemy") == 0) { this._getCurrentEnemys_addEnemy( block.event.id, enemys, used, block.x, block.y, floorId ); } }, this); return this._getCurrentEnemys_sort(enemys); }; enemys.prototype._getCurrentEnemys_getEnemy = function (enemyId) { var enemy = core.material.enemys[enemyId]; if (!enemy) return null; // 检查朝向;displayIdInBook return ( core.material.enemys[enemy.displayIdInBook] || core.material.enemys[(enemy.faceIds || {}).down] || enemy ); }; enemys.prototype._getCurrentEnemys_addEnemy = function ( enemyId, enemys, used, x, y, floorId ) { var enemy = this._getCurrentEnemys_getEnemy(enemyId); if (enemy == null) return; var id = enemy.id; var enemyInfo = this.getEnemyInfo(enemy, null, null, null, floorId); var locEnemyInfo = this.getEnemyInfo(enemy, null, x, y, floorId); // 检查enemys里面是否使用了存在的内容 for (var i = 0; i < enemys.length; ++i) { var one = enemys[i]; if ( id == one.id && one.locs != null && locEnemyInfo.atk == one.atk && locEnemyInfo.def == one.def && locEnemyInfo.hp == one.hp && core.plugin.arrsame(locEnemyInfo.special, one.special) && locEnemyInfo.mark == one.mark ) { one.locs.push([x, y]); return; } } enemyInfo = locEnemyInfo; var id = enemy.id + ":" + x + ":" + y; if (used[id]) return; used[id] = true; var specialText = core.enemys.getSpecialText(core.getEnemyInfo(core.getBlockId(x, y, floorId), void 0, x, y, floorId), x, y, floorId); var specialColor = core.enemys.getSpecialColor(core.getEnemyInfo(core.getBlockId(x, y, floorId), void 0, x, y, floorId), x, y, floorId); var critical = this.nextCriticals(enemy, 1, x, y, floorId); if (critical.length > 0) critical = critical[0]; var e = core.clone(enemy); for (var v in enemyInfo) { e[v] = enemyInfo[v]; } if (x != null && y != null) { e.locs = [ [x, y] ]; } e.name = core.getEnemyValue(enemy, "name", x, y, floorId); e.specialText = specialText; e.specialColor = specialColor; e.damage = core.getDamage(enemy, x, y, floorId); e.critical = critical[0]; e.criticalDamage = critical[1]; e.defDamage = this._getCurrentEnemys_addEnemy_defDamage( enemy, x, y, floorId ); enemys.push(e); }; enemys.prototype._getCurrentEnemys_addEnemy_defDamage = function ( enemy, x, y, floorId ) { return this.getDefDamage(enemy, 1, x, y, floorId); }; enemys.prototype._getCurrentEnemys_sort = function (enemys) { return enemys.sort(function (a, b) { if (a.damage == b.damage) { return a.money - b.money; } if (a.damage == null) { return 1; } if (b.damage == null) { return -1; } return a.damage - b.damage; }); }; ////// 获得所有特殊属性的名称 ////// enemys.prototype.getSpecialText = function (enemy, x, y, floorId) { if (typeof enemy == "string") enemy = this.getEnemyInfo(enemy, null, x, y, floorId); if (!enemy) return []; var special = enemy.special; var text = []; var specials = this.getSpecials(); if (specials) { for (var i = 0; i < specials.length; i++) { if (this.hasSpecial(special, specials[i][0])) text.push(this._calSpecialContent(enemy, specials[i][1])); } } return text; }; ////// 获得所有特殊属性的颜色 ////// enemys.prototype.getSpecialColor = function (enemy, x, y, floorId) { if (typeof enemy == "string") enemy = this.getEnemyInfo(enemy, null, x, y, floorId); if (!enemy) return []; var special = enemy.special; var colors = []; var specials = this.getSpecials(); if (specials) { for (var i = 0; i < specials.length; i++) { if (this.hasSpecial(special, specials[i][0])) colors.push(specials[i][3] || null); } } return colors; }; ////// 获得所有特殊属性的额外标记 ////// enemys.prototype.getSpecialFlag = function (enemy, x, y, floorId) { if (typeof enemy == "string") enemy = getEnemyInfo(enemy, null, x, y, floorId); if (!enemy) return []; var special = enemy.special; var flag = 0; var specials = this.getSpecials(); if (specials) { for (var i = 0; i < specials.length; i++) { if (this.hasSpecial(special, specials[i][0])) flag |= specials[i][4] || 0; } } return flag; }; ////// 获得每个特殊属性的说明 ////// enemys.prototype.getSpecialHint = function (enemy, special) { var specials = this.getSpecials(); if (special == null) { if (specials == null) return []; var hints = []; for (var i = 0; i < specials.length; i++) { if (this.hasSpecial(enemy, specials[i][0])) hints.push( "\r[" + core.arrayToRGBA(specials[i][3] || "#FF6A6A") + "]\\d" + this._calSpecialContent(enemy, specials[i][1]) + ":\\d\r[]" + this._calSpecialContent(enemy, specials[i][2]) ); } return hints; } if (specials == null) return ""; for (var i = 0; i < specials.length; i++) { if (special == specials[i][0]) return ( "\r[#FF6A6A]\\d" + this._calSpecialContent(enemy, specials[i][1]) + ":\\d\r[]" + this._calSpecialContent(enemy, specials[i][2]) ); } return ""; }; ui.prototype._drawBook_drawName = function ( index, enemy, top, left, width ) { // 绘制第零列(名称和特殊属性) // 如果需要添加自己的比如怪物的称号等,也可以在这里绘制 core.setTextAlign("ui", "center"); if (core.enemys.getSpecialText(enemy).length == 0) { core.fillText( "ui", enemy.name, left + width / 2, top + 35, "#DDDDDD", this._buildFont(17, true), width ); } else { core.fillText( "ui", enemy.name, left + width / 2, top + 28, "#DDDDDD", this._buildFont(17, true), width ); switch (core.enemys.getSpecialText(enemy).length) { case 1: core.fillText( "ui", core.enemys.getSpecialText(enemy)[0], left + width / 2, top + 50, core.arrayToRGBA( (core.enemys.getSpecialColor(enemy) || [])[0] || "#FF6A6A" ), this._buildFont(15, true), width ); break; case 2: // Step 1: 计算字体 var text = core.enemys.getSpecialText(enemy)[0] + " " + core.enemys.getSpecialText(enemy)[1]; core.setFontForMaxWidth( "ui", text, width, this._buildFont(15, true) ); // Step 2: 计算总宽度 var totalWidth = core.calWidth("ui", text); var leftWidth = core.calWidth( "ui", core.enemys.getSpecialText(enemy)[0] ); var rightWidth = core.calWidth( "ui", core.enemys.getSpecialText(enemy)[1] ); // Step 3: 绘制 core.fillText( "ui", core.enemys.getSpecialText(enemy)[0], left + (width + leftWidth - totalWidth) / 2, top + 50, core.arrayToRGBA( (core.enemys.getSpecialColor(enemy) || [])[0] || "#FF6A6A" ) ); core.fillText( "ui", core.enemys.getSpecialText(enemy)[1], left + (width + totalWidth - rightWidth) / 2, top + 50, core.arrayToRGBA( (core.enemys.getSpecialColor(enemy) || [])[1] || "#FF6A6A" ) ); break; default: core.fillText( "ui", "多属性...", left + width / 2, top + 50, "#FF6A6A", this._buildFont(15, true), width ); } } }; ui.prototype._drawBookDetail_getInfo = function (index) { var floorId = core.floorIds[(core.status.event.ui || {}).index] || core.status.floorId; // 清除浏览地图时的光环缓存 if (floorId != core.status.floorId && core.status.checkBlock) { core.status.checkBlock.cache = {}; } var enemys = core.enemys.getCurrentEnemys(floorId); //console.log(123); if (enemys.length == 0) return []; index = core.clamp(index, 0, enemys.length - 1); var enemy = enemys[index] var texts = core.enemys.getSpecialHint(enemy); if (texts.length == 0) texts.push("该怪物无特殊属性。"); if (enemy.description) texts.push(enemy.description + "\r"); this._drawBookDetail_getTexts(enemy, floorId, texts); texts.push(""); return [enemy, texts]; }; }, "一防减伤": function () { // 在此增加新插件 ui.prototype._drawBook_drawRow3 = function ( index, enemy, top, left, width, position ) { if (["SeaGawu", "Masao", "LionGawu", "ADMasao", "SOMasao", "Souri", "Mamushi"].includes(enemy.id)) return if (!core.flags.enableBookRow3) { // 绘制第三行 core.setTextAlign('ui', 'left'); var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); var col1 = left, col2 = left + width * 9 / 25, col3 = left + width * 17 / 25; core.fillText('ui', '临界', col1, position, '#DDDDDD', f13); core.fillText('ui', core.formatBigNumber(enemy.critical || 0), col1 + 30, position, null, b13); core.fillText('ui', '减伤', col2, position, null, f13); core.fillText('ui', core.formatBigNumber(enemy.criticalDamage || 0), col2 + 30, position, null, b13); core.fillText('ui', '加防', col3, position, null, f13); core.fillText('ui', core.formatBigNumber(enemy.defDamage || 0), col3 + 30, position, null, b13); } else { // 绘制第三行 core.setTextAlign("ui", "left"); var b13 = this._buildFont(13, true), f13 = this._buildFont(13, false); var col1 = left, col2 = left + (width * 9) / 25, col3 = left + (width * 17) / 25; core.fillText("ui", "临界", col1, position, "#DDDDDD", f13); core.fillText( "ui", core.formatBigNumber(enemy.critical || 0), col1 + 30, position, null, b13 ); if (enemy.damage) { core.fillText("ui", "减伤", col2, position, null, f13); core.fillText( "ui", core.formatBigNumber(enemy.criticalDamage || 0), col2 + 30, position, null, b13 ); core.fillText("ui", "1防", col3, position, null, f13); core.fillText( "ui", core.formatBigNumber(enemy.defDamage || 0), col3 + 30, position, null, b13 ); } }; } }, "新道具栏/装备栏": function () { // 在此增加新插件 // 注:///// *** 裹起来的区域: 该区域内参数可以随意更改调整ui绘制 不会影响总体布局 // 请尽量修改该区域而不是其他区域 修改的时候最好可以对照现有ui修改 ///// *** 道具类型 // cls对应name var itemClsName = { constants: "永久道具", tools: "消耗道具", }; // 一页最大放的道具数量 将把整个道具左栏分成num份 每份是一个道具项 var itemNum = 12; ///// *** // 背景设置 this.drawBoxBackground = function (ctx) { core.setTextAlign(ctx, "left"); core.clearMap(ctx); core.deleteCanvas("_selector"); var info = core.status.thisUIEventInfo || {}; ///// *** 背景设置 var max = core.__PIXELS__; var x = 2, y = x, w = max - x * 2, h = w; var borderWidth = 2, borderRadius = 5, // radius:圆角矩形的圆角半径 borderStyle = "#fff"; var backgroundColor = "gray"; // 设置背景不透明度(0.85) var backgroundAlpha = 0.85; ///// *** var start_x = x + borderWidth / 2, start_y = y + borderWidth / 2, width = max - start_x * 2, height = max - start_y * 2; // 渐变色背景的一个例子(黑色渐变白色): // 有关渐变色的具体知识请网上搜索canvas createGradient了解 /* var grd = ctx.createLinearGradient(x, y, x + w, y); grd.addColorStop(0, "black"); grd.addColorStop(1, "white"); backgroundColor = grd; */ // 使用图片背景要注释掉下面的strokeRect和fillRoundRect // 图片背景的一个例子: /* core.drawImage(ctx, "xxx.png", x, y, w, h); core.strokeRect(ctx, x, y, w, h, borderStyle, borderWidth); */ core.setAlpha(ctx, backgroundAlpha); core.strokeRoundRect( ctx, x, y, w, h, borderRadius, borderStyle, borderWidth ); core.fillRoundRect( ctx, start_x, start_y, width, height, borderRadius, backgroundColor ); core.setAlpha(ctx, 1); ///// *** 左栏配置 var leftbar_height = height; // 左边栏宽度(width*0.6) 本身仅为坐标使用 需要与底下的rightbar_width(width*0.4)同时更改 var leftbar_width = width * 0.6; ///// *** // xxx_right参数 代表最右侧坐标 var leftbar_right = start_x + leftbar_width - borderWidth / 2; var leftbar_bottom = start_y + leftbar_height; var leftbar_x = start_x; var leftbar_y = start_y; ///// *** 道具栏配置 var boxName_color = "#fff"; var boxName_fontSize = 15; var boxName_font = core.ui._buildFont(boxName_fontSize, true); var arrow_x = 10 + start_x; var arrow_y = 10 + start_y; var arrow_width = 20; var arrow_style = "white"; // 暂时只能是1 否则不太行 等待新样板(2.7.3)之后对drawArrow做优化 var arrow_lineWidth = 1; // 右箭头 var rightArrow_right = leftbar_right - 10; // 道具内栏顶部坐标 本质是通过该项 控制(道具栏顶部文字和箭头)与道具内栏顶部的间隔 var itembar_top = arrow_y + 15; ///// *** var itembar_right = rightArrow_right; var boxName = core.status.event.id == "toolbox" ? "\r[yellow]道具栏\r | 装备栏" : "道具栏 | \r[yellow]装备栏\r"; core.drawArrow( ctx, arrow_x + arrow_width, arrow_y, arrow_x, arrow_y, arrow_style, arrow_lineWidth ); core.drawArrow( ctx, rightArrow_right - arrow_width, arrow_y, rightArrow_right, arrow_y, arrow_style, arrow_lineWidth ); core.setTextAlign(ctx, "center"); core.setTextBaseline(ctx, "middle"); var changeBox = function () { var id = core.status.event.id; core.closePanel(); if (id == "toolbox") core.openEquipbox(); else core.openToolbox(); }; core.fillText( ctx, boxName, (leftbar_right + leftbar_x) / 2, arrow_y + 2, boxName_color, boxName_font ); ///// *** 底栏按钮 var pageBtn_radius = 8; // xxx_left 最左侧坐标 var pageBtn_left = leftbar_x + 3; var pageBtn_right = leftbar_right - 3; // xxx_bottom 最底部坐标 var pageBtn_bottom = leftbar_bottom - 2; var pageBtn_borderStyle = "#fff"; var pageBtn_borderWidth = 2; var pageText_color = "#fff"; // 底部按钮与上面的道具内栏的间隔大小 var bottomSpace = 8; ///// *** core.drawItemListbox_setPageBtn( ctx, pageBtn_left, pageBtn_right, pageBtn_bottom, pageBtn_radius, pageBtn_borderStyle, pageBtn_borderWidth ); var page = info.page || 1; var pageFontSize = pageBtn_radius * 2 - 4; var pageFont = core.ui._buildFont(pageFontSize); core.setPageItems(page); var num = itemNum; if (core.status.event.id == "equipbox") num -= 5; var maxPage = info.maxPage; var pageText = page + " / " + maxPage; core.setTextAlign(ctx, "center"); core.setTextBaseline(ctx, "bottom"); core.fillText( ctx, pageText, (leftbar_x + leftbar_right) / 2, pageBtn_bottom, pageText_color, pageFont ); core.addUIEventListener( start_x, start_y, leftbar_right - start_x, arrow_y - start_y + 13, changeBox ); var itembar_height = Math.ceil( pageBtn_bottom - pageBtn_radius * 2 - pageBtn_borderWidth / 2 - bottomSpace - itembar_top ); var oneItemHeight = (itembar_height - 4) / itemNum; return { x: start_x, y: start_y, width: width, height: height, leftbar_right: leftbar_right, obj: { x: arrow_x, y: itembar_top, width: itembar_right - arrow_x, height: itembar_height, oneItemHeight: oneItemHeight, }, }; }; this.drawItemListbox = function (ctx, obj) { ctx = ctx || core.canvas.ui; var itembar_x = obj.x, itembar_y = obj.y, itembar_width = obj.width, itembar_height = obj.height, itemNum = obj.itemNum, oneItemHeight = obj.oneItemHeight; var itembar_right = itembar_x + itembar_width; var info = core.status.thisUIEventInfo || {}; var obj = {}; var page = info.page || 1, index = info.index, select = info.select || {}; ///// *** 道具栏内栏配置 var itembar_style = "black"; var itembar_alpha = 0.7; // 一个竖屏下减少道具显示的例子: // if (core.domStyle.isVertical) itemNum = 10; // 每个道具项的上下空隙占总高度的比例 var itembar_marginHeightRatio = 0.2; // 左右间隔空隙 var item_marginLeft = 2; var item_x = itembar_x + 2, item_y = itembar_y + 2, item_right = itembar_right - 2, itemName_color = "#fff"; // 修改此项以更换闪烁光标 var item_selector = "winskin.png"; ///// *** core.setAlpha(ctx, itembar_alpha); core.fillRect( ctx, itembar_x, itembar_y, itembar_width, itembar_height, itembar_style ); core.setAlpha(ctx, 1); var pageItems = core.setPageItems(page); var marginHeight = itembar_marginHeightRatio * oneItemHeight; core.setTextBaseline(ctx, "middle"); var originColor = itemName_color; for (var i = 0; i < pageItems.length; i++) { itemName_color = originColor; var item = pageItems[i]; // 设置某个的字体颜色的一个例子 // if (item.id == "xxx") itemName_color = "green"; core.drawItemListbox_drawItem( ctx, item_x, item_right, item_y, oneItemHeight, item_marginLeft, marginHeight, itemName_color, pageItems[i] ); if (index == i + 1) core.ui._drawWindowSelector( item_selector, item_x + 1, item_y - 1, item_right - item_x - 2, oneItemHeight - 2 ); item_y += oneItemHeight; } }; this.drawToolboxRightbar = function (ctx, obj) { ctx = ctx || core.canvas.ui; var info = core.status.thisUIEventInfo || {}; var page = info.page || 1, index = info.index || 1, select = info.select || {}; var start_x = obj.x, start_y = obj.y, width = obj.width, height = obj.height; var toolboxRight = start_x + width, toolboxBottom = start_y + height; ///// *** 侧边栏(rightbar)背景设置(物品介绍) var rightbar_width = width * 0.4; var rightbar_height = height; var rightbar_lineWidth = 2; var rightbar_lineStyle = "#fff"; ///// *** var rightbar_x = toolboxRight - rightbar_width - rightbar_lineWidth / 2; var rightbar_y = start_y; core.drawLine( ctx, rightbar_x, rightbar_y, rightbar_x, rightbar_y + rightbar_height, rightbar_lineStyle, rightbar_lineWidth ); // 获取道具id(有可能为null) var itemId = select.id; var item = core.material.items[itemId]; ///// *** 侧边栏物品Icon信息 var iconRect_y = rightbar_y + 10; // space:间距 // 这里布局设定iconRect与侧边栏左边框 itemName与工具栏右边框 itemRect与itemName的间距均为space var space = 15; var iconRect_x = rightbar_x + space; var iconRect_radius = 2, iconRect_width = 32, iconRect_height = 32, iconRect_style = "#fff", iconRect_lineWidth = 2; ///// *** var iconRect_bottom = iconRect_y + iconRect_height, iconRect_right = iconRect_x + iconRect_width; ///// *** 侧边栏各项信息 var itemTextFontSize = 15, itemText_x = iconRect_x - 4, itemText_y = Math.floor(start_y + rightbar_height * 0.25), // 坐标取整防止模糊 itemClsFontSize = 15, itemClsFont = core.ui._buildFont(itemClsFontSize), itemClsColor = "#fff", itemCls_x = itemText_x - itemClsFontSize / 2, itemCls_middle = (iconRect_bottom + itemText_y) / 2, //_middle代表文字的中心y坐标 itemNameFontSize = 18, itemNameColor = "#fff", itemNameFont = core.ui._buildFont(itemNameFontSize, true); var itemName_x = iconRect_right + space; var itemName_middle = iconRect_y + iconRect_height / 2 + iconRect_lineWidth; // 修改这里可以编辑未选中道具时的默认值 var defaultItem = { cls: "constants", name: "未知道具", text: "没有道具最永久", }; var defaultEquip = { cls: "equips", name: "未知装备", text: "一无所有,又何尝不是一种装备", equip: { type: "装备", }, }; ///// *** var originItem = item; if (core.status.event.id == "equipbox") item = item || defaultEquip; item = item || defaultItem; var itemCls = item.cls, itemName = item.name, itemText = item.text; itemText = core.replaceText(itemText); if (itemText[0] == "," || itemText[0] == ",") itemText = itemText.substring(1); /* 一个根据道具id修改道具名字(右栏)的例子 * if (item.id == "xxx") itemNameColor = "red"; */ var itemClsName = core.getItemClsName(item); var itemNameMaxWidth = rightbar_width - iconRect_width - iconRect_lineWidth * 2 - space * 2; core.strokeRoundRect( ctx, iconRect_x, iconRect_y, iconRect_width, iconRect_height, iconRect_radius, iconRect_style, iconRect_lineWidth ); if (item.id) core.drawIcon( ctx, item.id, iconRect_x + iconRect_lineWidth / 2, iconRect_y + iconRect_lineWidth / 2, iconRect_width - iconRect_lineWidth, iconRect_height - iconRect_lineWidth ); core.setTextAlign(ctx, "left"); core.setTextBaseline(ctx, "middle"); core.fillText( ctx, itemName, itemName_x, itemName_middle, itemNameColor, itemNameFont, itemNameMaxWidth ); core.fillText( ctx, "【" + itemClsName + "】", itemCls_x, itemCls_middle, itemClsColor, itemClsFont ); var statusText = ""; if (core.status.event.id == "equipbox") { var type = item.equip.type; if (typeof type == "string") type = core.getEquipTypeByName(type); var compare = core.compareEquipment(item.id, core.getEquip(type)); if (info.select.action == "unload") compare = core.compareEquipment(null, item.id); // --- 变化值... for (var name in core.status.hero) { if (typeof core.status.hero[name] != "number") continue; var nowValue = core.getRealStatus(name); // 查询新值 var newValue = Math.floor( ((core.getStatus(name) + (compare.value[name] || 0)) * (core.getBuff(name) * 100 + (compare.percentage[name] || 0))) / 100 ); if (nowValue == newValue) continue; var color = newValue > nowValue ? "#00FF00" : "#FF0000"; nowValue = core.formatBigNumber(nowValue); newValue = core.formatBigNumber(newValue); statusText += core.getStatusLabel(name) + " " + nowValue + "->\r[" + color + "]" + newValue + "\r\n"; } } itemText = statusText + itemText; core.drawTextContent(ctx, itemText, { left: itemText_x, top: itemText_y, bold: false, color: "white", align: "left", fontSize: itemTextFontSize, maxWidth: rightbar_width - (itemText_x - rightbar_x) * 2 + itemTextFontSize / 2, }); ///// *** 退出按钮设置 var btnRadius = 10; var btnBorderWidth = 2; var btnRight = toolboxRight - 2; var btnBottom = toolboxBottom - 2; var btnBorderStyle = "#fff"; ///// *** // 获取圆心位置 var btn_x = btnRight - btnRadius - btnBorderWidth / 2, btn_y = btnBottom - btnRadius - btnBorderWidth / 2; core.drawToolbox_setExitBtn( ctx, btn_x, btn_y, btnRadius, btnBorderStyle, btnBorderWidth ); ///// *** 使用按钮设置 var useBtnHeight = btnRadius * 2; // 这里不设置useBtnWidth而是根据各项数据自动得出width var useBtnRadius = useBtnHeight / 2; var useBtn_x = rightbar_x + 4, useBtn_y = btnBottom - useBtnHeight; var useBtnBorderStyle = "#fff"; var useBtnBorderWidth = btnBorderWidth; ///// *** core.drawToolbox_setUseBtn( ctx, useBtn_x, useBtn_y, useBtnRadius, useBtnHeight, useBtnBorderStyle, useBtnBorderWidth ); }; this.drawEquipbox_drawOthers = function (ctx, obj) { var info = core.status.thisUIEventInfo; ///// *** 装备格设置 var equipList_lineWidth = 2; var equipList_boxSize = 32; var equipList_borderWidth = 2; var equipList_borderStyle = "#fff"; var equipList_nameColor = "#fff"; ///// *** var equipList_x = obj.x + 4, equipList_bottom = obj.obj.y - equipList_lineWidth, equipList_y = equipList_bottom - obj.obj.oneItemHeight * reduceItem - 2, equipList_height = equipList_bottom - equipList_y; var equipList_right = obj.leftbar_right, equipList_width = equipList_right - equipList_x; core.drawLine( ctx, obj.x, equipList_bottom + equipList_lineWidth / 2, equipList_right, equipList_bottom + equipList_lineWidth / 2, equipList_borderStyle, equipList_lineWidth ); var toDrawList = core.status.globalAttribute.equipName, len = toDrawList.length; ///// *** 装备格设置 var maxItem = 4; var box_width = 32, box_height = 32, box_borderStyle = "#fff", box_selectBorderStyle = "gold", // 选中的装备格的颜色 box_borderWidth = 2; var boxName_fontSize = 14, boxName_space = 2, boxName_color = "#fff"; // 装备格名称与上面的装备格框的距离 var maxLine = Math.ceil(len / maxItem); ///// *** var l = Math.sqrt(len); if (Math.pow(l) == len && len != 4) { if (l <= maxItem) maxItem = l; } maxItem = Math.min(toDrawList.length, maxItem); info.equips = maxItem; var boxName_font = core.ui._buildFont(boxName_fontSize); // 总宽高减去所有装备格宽高得到空隙大小 var oneBoxWidth = box_width + box_borderWidth * 2; var oneBoxHeight = box_height + boxName_fontSize + boxName_space + 2 * box_borderWidth; var space_y = (equipList_height - maxLine * oneBoxHeight) / (1 + maxLine), space_x = (equipList_width - maxItem * oneBoxWidth) / (1 + maxItem); var box_x = equipList_x + space_x, box_y = equipList_y + space_y; for (var i = 0; i < len; i++) { var id = core.getEquip(i), name = toDrawList[i]; var selectBorder = false; if (core.status.thisUIEventInfo.select.type == i) selectBorder = true; var borderStyle = selectBorder ? box_selectBorderStyle : box_borderStyle; core.drawEquipbox_drawOne( ctx, name, id, box_x, box_y, box_width, box_height, boxName_space, boxName_font, boxName_color, borderStyle, box_borderWidth ); var todo = new Function( "core.clickOneEquipbox('" + id + "'," + i + ")" ); core.addUIEventListener( box_x - box_borderWidth / 2, box_y - box_borderWidth / 2, oneBoxWidth, oneBoxHeight, todo ); box_x += space_x + oneBoxWidth; if ((i + 1) % maxItem == 0) { box_x = equipList_x + space_x; box_y += space_y + oneBoxHeight; } } }; this.drawToolbox = function (ctx) { ctx = ctx || core.canvas.ui; core.status.thisEventClickArea = []; var info = core.drawBoxBackground(ctx); info.itemNum = itemNum; core.drawItemListbox(ctx, info.obj); core.drawToolboxRightbar(ctx, info); core.setTextBaseline(ctx, "alphabetic"); core.setTextAlign("left"); }; var reduceItem = 4; this.drawEquipbox = function (ctx) { ctx = ctx || core.canvas.ui; core.status.thisEventClickArea = []; var info = core.drawBoxBackground(ctx); info.itemNum = itemNum - reduceItem; info.obj.y += info.obj.oneItemHeight * reduceItem; info.obj.height -= info.obj.oneItemHeight * reduceItem; core.drawItemListbox(ctx, info.obj); core.drawEquipbox_drawOthers(ctx, info); core.drawToolboxRightbar(ctx, info); core.setTextBaseline(ctx, "alphabetic"); core.setTextAlign("left"); }; this.drawEquipbox_drawOne = function ( ctx, name, id, x, y, width, height, space, font, color, style, lineWidth ) { if (id) core.drawIcon( ctx, id, x + lineWidth / 2, y + lineWidth / 2, width, height ); core.strokeRect( ctx, x, y, width + lineWidth, height + lineWidth, style, lineWidth ); core.setTextAlign(ctx, "center"); core.setTextBaseline(ctx, "top"); var tx = (x + x + lineWidth / 2 + width) / 2, ty = y + height + (lineWidth / 2) * 3 + space; core.fillText(ctx, name, tx, ty, color, font); core.setTextBaseline(ctx, "alphabetic"); core.setTextAlign("left"); }; this.drawItemListbox_drawItem = function ( ctx, left, right, top, height, marginLeft, marginHeight, style, id ) { var info = core.status.thisUIEventInfo; var nowClick = info.index; var item = core.material.items[id] || {}; var name = item.name || "???"; var num = core.itemCount(id) || 0; var fontSize = Math.floor(height - marginHeight * 2); core.setTextAlign(ctx, "right"); var numText = "x" + num; core.fillText( ctx, numText, right - marginLeft, top + height / 2, style, core.ui._buildFont(fontSize) ); if (name != "???") core.drawIcon( ctx, id, left + marginLeft, top + marginHeight, fontSize, fontSize ); var text_x = left + marginLeft + fontSize + 2; var maxWidth = right - core.calWidth(ctx, numText) - text_x; core.setTextAlign(ctx, "left"); core.fillText( ctx, name, text_x, top + height / 2, style, core.ui._buildFont(fontSize), maxWidth ); var todo = new Function("core.clickItemFunc('" + id + "');"); core.addUIEventListener(left, top, right - left, height, todo); }; this.setPageItems = function (page) { var num = itemNum; if (core.status.event.id == "equipbox") num -= reduceItem; var info = core.status.thisUIEventInfo; if (!info) return; page = page || info.page; var items = core.getToolboxItems( core.status.event.id == "toolbox" ? "all" : "equips" ); info.allItems = items; var maxPage = Math.ceil(items.length / num); info.maxPage = maxPage; var pageItems = items.slice((page - 1) * num, page * num); info.pageItems = pageItems; info.maxItem = pageItems.length; if (items.length == 0 && pageItems.length == 0) info.index = null; if (pageItems.length == 0 && info.page > 1) { info.page = Math.max(1, info.page - 1); return core.setPageItems(info.page); } return pageItems; }; this.drawToolbox_setExitBtn = function (ctx, x, y, r, style, lineWidth) { core.strokeCircle(ctx, x, y, r, style, lineWidth); ctx.textAlign = "center"; ctx.textBaseline = "middle"; var textSize = Math.sqrt(2) * r; core.fillText( ctx, "x", x, y, style, core.ui._buildFont(textSize), textSize ); core.setTextAlign(ctx, "start"); core.setTextBaseline(ctx, "top"); var todo = function () { core.closePanel(); }; core.addUIEventListener(x - r, y - r, r * 2, r * 2, todo); }; this.drawToolbox_setUseBtn = function (ctx, x, y, r, h, style, lineWidth) { core.setTextAlign(ctx, "left"); core.setTextBaseline(ctx, "top"); var fontSize = h - 4; var font = core.ui._buildFont(fontSize); var text = core.status.event.id == "toolbox" ? "使用" : "装备"; if (core.status.thisUIEventInfo.select.action == "unload") text = "卸下"; var w = core.calWidth(ctx, text, font) + 2 * r + lineWidth / 2; core.strokeRoundRect(ctx, x, y, w, h, r, style, lineWidth); core.fillText(ctx, text, x + r, y + lineWidth / 2 + 2, style, font); var todo = function () { core.useSelectItemInBox(); }; core.addUIEventListener(x, y, w, h, todo); }; this.drawItemListbox_setPageBtn = function ( ctx, left, right, bottom, r, style, lineWidth ) { var offset = lineWidth / 2 + r; var x = left + offset; var y = bottom - offset; var pos = (Math.sqrt(2) / 2) * (r - lineWidth / 2); core.fillPolygon( ctx, [ [x - pos, y], [x + pos - 2, y - pos], [x + pos - 2, y + pos], ], style ); core.strokeCircle(ctx, x, y, r, style, lineWidth); var todo = function () { core.addItemListboxPage(-1); }; core.addUIEventListener(x - r - 2, y - r - 2, r * 2 + 4, r * 2 + 4, todo); x = right - offset; core.fillPolygon( ctx, [ [x + pos, y], [x - pos + 2, y - pos], [x - pos + 2, y + pos], ], style ); core.strokeCircle(ctx, x, y, r, style, lineWidth); var todo = function () { core.addItemListboxPage(1); }; core.addUIEventListener(x - r - 2, y - r - 2, r * 2 + 4, r * 2 + 4, todo); }; this.clickItemFunc = function (id) { var info = core.status.thisUIEventInfo; if (!info) return; if (info.select.id == id) return core.useSelectItemInBox(); info.select = {}; info.select.id = id; core.setIndexAndSelect("index"); core.refreshBox(); }; this.clickOneEquipbox = function (id, type) { var info = core.status.thisUIEventInfo; if (!info) return; if (info.select.id == id && info.select.type == type) core.useSelectItemInBox(); else core.status.thisUIEventInfo.select = { id: id, type: type, action: "unload", }; return core.refreshBox(); }; core.ui.getToolboxItems = function (cls) { var list = Object.keys(core.status.hero.items[cls] || {}); if (cls == "all") { for (var name in core.status.hero.items) { if (name == "equips") continue; list = list.concat(Object.keys(core.status.hero.items[name])); } return list .filter(function (id) { return !core.material.items[id].hideInToolbox; }) .sort(); } if (this.uidata.getToolboxItems) { return this.uidata.getToolboxItems(cls); } return list .filter(function (id) { return !core.material.items[id].hideInToolbox; }) .sort(); }; this.useSelectItemInBox = function () { var info = core.status.thisUIEventInfo; if (!info) return; if (!info.select.id) return; var id = info.select.id; if (core.status.event.id == "toolbox") { core.events.tryUseItem(id); // core.closePanel(); } else if (core.status.event.id == "equipbox") { var action = info.select.action || "load"; info.index = 1; if (action == "load") { var type = core.getEquipTypeById(id); core.loadEquip(id, function () { core.status.route.push("equip:" + id); info.select.type = type; core.setIndexAndSelect("select"); core.drawEquipbox(); }); } else { var type = info.select.type; core.unloadEquip(type, function () { core.status.route.push("unEquip:" + type); info.select.type = type; //info.select.action = 'load' core.setIndexAndSelect("select"); core.drawEquipbox(); }); } } }; this.setIndexAndSelect = function (toChange) { var info = core.status.thisUIEventInfo; if (!info) return; core.setPageItems(info.page); var index = info.index || 1; var items = info.pageItems; if (info.select.type != null) { var type = info.select.type; id = core.getEquip(type); info.index = null; info.select = { id: id, action: "unload", type: type, }; return; } else { info.select.action = null; info.select.type = null; if (toChange == "index") info.index = items.indexOf(info.select.id) + 1; info.select.id = items[info.index - 1]; } }; this.addItemListboxPage = function (num) { var info = core.status.thisUIEventInfo; if (!info) return; var maxPage = info.maxPage || 1; info.page = info.page || 1; info.page += num; if (info.page <= 0) info.page = maxPage; if (info.page > maxPage) info.page = 1; info.index = 1; core.setPageItems(info.page); core.setIndexAndSelect("select"); core.refreshBox(); }; this.addItemListboxIndex = function (num) { var info = core.status.thisUIEventInfo; if (!info) return; var maxItem = info.maxItem || 0; info.index = info.index || 0; info.index += num; if (info.index <= 0) info.index = 1; if (info.index > maxItem) info.index = maxItem; core.setIndexAndSelect("select"); core.refreshBox(); }; this.addEquipboxType = function (num) { var info = core.status.thisUIEventInfo; var type = info.select.type; if (type == null && num > 0) info.select.type = 0; else info.select.type = type + num; var max = core.status.globalAttribute.equipName.length; if (info.select.type >= max) { info.select = {}; core.setIndexAndSelect("select"); return core.addItemListboxPage(0); } else { var m = Math.abs(info.select.type); if (info.select.type < 0) info.select.type = max - m; core.setIndexAndSelect("select"); core.refreshBox(); return; } }; core.actions._keyDownToolbox = function (keycode) { if (!core.status.thisEventClickArea) return; if (keycode == 37) { // left core.addItemListboxPage(-1); return; } if (keycode == 38) { // up core.addItemListboxIndex(-1); return; } if (keycode == 39) { // right core.addItemListboxPage(1); return; } if (keycode == 40) { // down core.addItemListboxIndex(1); return; } }; ////// 工具栏界面时,放开某个键的操作 ////// core.actions._keyUpToolbox = function (keycode) { if (keycode == 81) { core.ui.closePanel(); if (core.isReplaying()) core.control._replay_equipbox(); else core.openEquipbox(); return; } if (keycode == 84 || keycode == 27 || keycode == 88) { core.closePanel(); return; } if (keycode == 13 || keycode == 32 || keycode == 67) { var info = core.status.thisUIEventInfo; if (info.select) { core.useSelectItemInBox(); } return; } }; core.actions._keyDownEquipbox = function (keycode) { if (!core.status.thisEventClickArea) return; if (keycode == 37) { // left var info = core.status.thisUIEventInfo; if (info.index != null) return core.addItemListboxPage(-1); return core.addEquipboxType(-1); } if (keycode == 38) { // up var info = core.status.thisUIEventInfo; if (info.index == 1) { info.select.type = core.status.globalAttribute.equipName.length - 1; core.setIndexAndSelect(); return core.refreshBox(); } if (info.index) return core.addItemListboxIndex(-1); return core.addEquipboxType(-1 * info.equips); } if (keycode == 39) { // right var info = core.status.thisUIEventInfo; if (info.index != null) return core.addItemListboxPage(1); return core.addEquipboxType(1); } if (keycode == 40) { // down var info = core.status.thisUIEventInfo; if (info.index) return core.addItemListboxIndex(1); return core.addEquipboxType(info.equips); } }; core.actions._keyUpEquipbox = function (keycode, altKey) { if (altKey && keycode >= 48 && keycode <= 57) { core.items.quickSaveEquip(keycode - 48); return; } if (keycode == 84) { core.ui.closePanel(); if (core.isReplaying()) core.control._replay_toolbox(); else core.openToolbox(); return; } if (keycode == 81 || keycode == 27 || keycode == 88) { core.closePanel(); return; } if (keycode == 13 || keycode == 32 || keycode == 67) { var info = core.status.thisUIEventInfo; if (info.select) core.useSelectItemInBox(); return; } }; core.registerAction( "ondown", "inEventClickAction", function (x, y, px, py) { if (!core.status.thisEventClickArea) return false; // console.log(px + "," + py); var info = core.status.thisEventClickArea; for (var i = 0; i < info.length; i++) { var obj = info[i]; if ( px >= obj.x && px <= obj.x + obj.width && py > obj.y && py < obj.y + obj.height ) { if (obj.todo) obj.todo(); break; } } return true; }, 51 ); core.registerAction( "onclick", "stopClick", function () { if (core.status.thisEventClickArea) return true; }, 51 ); this.addUIEventListener = function (x, y, width, height, todo) { if (!core.status.thisEventClickArea) return; var obj = { x: x, y: y, width: width, height: height, todo: todo, }; core.status.thisEventClickArea.push(obj); }; this.initThisEventInfo = function () { core.status.thisUIEventInfo = { page: 1, select: {}, }; core.status.thisEventClickArea = []; }; this.refreshBox = function () { if (!core.status.event.id) return; if (core.status.event.id == "toolbox") core.drawToolbox(); else core.drawEquipbox(); }; core.ui.closePanel = function () { if (core.status.hero && core.status.hero.flags) { // 清除全部临时变量 Object.keys(core.status.hero.flags).forEach(function (name) { if (name.startsWith("@temp@") || /^arg\d+$/.test(name)) { delete core.status.hero.flags[name]; } }); } this.clearUI(); core.maps.generateGroundPattern(); core.updateStatusBar(true); core.unlockControl(); core.status.event.data = null; core.status.event.id = null; core.status.event.selection = null; core.status.event.ui = null; core.status.event.interval = null; core.status.thisUIEventInfo = null; core.status.thisEventClickArea = null; }; this.getItemClsName = function (item) { if (item == null) return itemClsName; if (item.cls == "equips") { if (typeof item.equip.type == "string") return item.equip.type; var type = core.getEquipTypeById(item.id); return core.status.globalAttribute.equipName[type]; } else return itemClsName[item.cls] || item.cls; }; core.events.openToolbox = function (fromUserAction) { if (core.isReplaying()) return; if (!this._checkStatus("toolbox", fromUserAction)) return; core.initThisEventInfo(); let info = core.status.thisUIEventInfo; info.index = 1; core.setIndexAndSelect("select"); core.drawToolbox(); }; core.events.openEquipbox = function (fromUserAction) { if (core.isReplaying()) return; if (!this._checkStatus("equipbox", fromUserAction)) return; core.initThisEventInfo(); let info = core.status.thisUIEventInfo; info.select.type = 0; core.setIndexAndSelect("select"); core.drawEquipbox(); }; core.control._replay_toolbox = function () { if (!core.isPlaying() || !core.isReplaying()) return; if (!core.status.replay.pausing) return core.drawTip("请先暂停录像"); if (core.isMoving() || core.status.replay.animate || core.status.event.id) return core.drawTip("请等待当前事件的处理结束"); core.lockControl(); core.status.event.id = "toolbox"; core.drawToolbox(); }; core.control._replay_equipbox = function () { if (!core.isPlaying() || !core.isReplaying()) return; if (!core.status.replay.pausing) return core.drawTip("请先暂停录像"); if (core.isMoving() || core.status.replay.animate || core.status.event.id) return core.drawTip("请等待当前事件的处理结束"); core.lockControl(); core.status.event.id = "equipbox"; core.drawEquipbox(); }; core.control._replayAction_item = function (action) { if (action.indexOf("item:") != 0) return false; var itemId = action.substring(5); if (!core.canUseItem(itemId)) return false; if ( core.material.items[itemId].hideInReplay || core.status.replay.speed == 24 ) { core.useItem(itemId, false, core.replay); return true; } core.status.event.id = "toolbox"; core.initThisEventInfo(); var info = core.status.thisUIEventInfo; var items = core.getToolboxItems("all"); core.setPageItems(1); var index = items.indexOf(itemId) + 1; info.page = Math.ceil(index / info.maxItem); info.index = index % info.maxItem || info.maxItem; core.setIndexAndSelect("select"); core.setPageItems(info.page); core.drawToolbox(); setTimeout(function () { core.ui.closePanel(); core.useItem(itemId, false, core.replay); }, core.control.__replay_getTimeout()); return true; }; core.control._replayAction_equip = function (action) { if (action.indexOf("equip:") != 0) return false; var itemId = action.substring(6); var items = core.getToolboxItems("equips"); var index = items.indexOf(itemId) + 1; if (index < 1) return false; core.status.route.push(action); if ( core.material.items[itemId].hideInReplay || core.status.replay.speed == 24 ) { core.loadEquip(itemId, core.replay); return true; } core.status.event.id = "equipbox"; core.initThisEventInfo(); var info = core.status.thisUIEventInfo; core.setPageItems(1); info.page = Math.ceil(index / info.maxItem); info.index = index % info.maxItem || info.maxItem; core.setIndexAndSelect("select"); core.setPageItems(info.page); core.drawEquipbox(); setTimeout(function () { core.ui.closePanel(); core.loadEquip(itemId, core.replay); }, core.control.__replay_getTimeout()); return true; }; core.control._replayAction_unEquip = function (action) { if (action.indexOf("unEquip:") != 0) return false; var equipType = parseInt(action.substring(8)); if (!core.isset(equipType)) return false; core.status.route.push(action); if (core.status.replay.speed == 24) { core.unloadEquip(equipType, core.replay); return true; } core.status.event.id = "equipbox"; core.initThisEventInfo(); var info = core.status.thisUIEventInfo; core.setPageItems(1); info.select.type = equipType; core.setIndexAndSelect(); core.drawEquipbox(); setTimeout(function () { core.ui.closePanel(); core.unloadEquip(equipType, core.replay); }, core.control.__replay_getTimeout()); return true; }; core.registerReplayAction("item", core.control._replayAction_item); core.registerReplayAction("equip", core.control._replayAction_equip); core.registerReplayAction("unEquip", core.control._replayAction_unEquip); }, "技能树": function () { // 在此增加新插件 // // 已学习的技能等级 flags._hasSkill_ //打开技能树 core.myskilltree() //不需要自动连线可以在 core.myskilltree_draw_line开头取消return的注释 core.AllStatus = function () { //////在这个函数填写技能列表 var Skill_1 = { 召唤之书: { name: "召唤之书", text: "每种召唤物至多存在一只,当存在召唤物时,伤害以花花、灰太狼、牛牛、蝴蝶、兔兔、勇士顺序进行结算,直到当前序列召唤物死亡前,下一序列不受伤。吸血、反伤、固伤等特殊属性直接作用于勇士", image: "I393", maxLv: 1, pos: [5, 7], need: null, exp: 0, }, 冥想: { name: "冥想", text: "默认主动技能,战斗后恢复技能等级的MP", image: "I395", maxLv: 5, pos: [5, 11], need: null, exp: 20 + 30 * flags._hasSkill_["冥想"], //基础+每个等级需求提升 }, 召唤回收: { name: "召唤回收", text: "献祭召唤物,回复20MP,召唤物离场会导致对应图腾失效(涅槃图腾除外)", image: "I405", maxLv: 1, pos: [5, 5], need: [{ 召唤之书: 1 }], exp: 100, }, 蝴蝶: { name: "蝴蝶", text: "40MP,召唤50+30*Lv生命的蝴蝶,在场时提升0.5*LV点战后回蓝效果(向上取整)并提升Lv点经验获取", image: "I388", maxLv: 5, pos: [7, 9], need: [{ 召唤之书: 1 }], exp: 50 * flags._hasSkill_["蝴蝶"], }, 兔兔: { name: "兔兔", text: "40MP,召唤50+30*Lv生命的兔兔,在场时提勇士10%*LV金币获取", image: "I389", maxLv: 5, pos: [9, 9], need: [{ 召唤之书: 1 }], exp: 50 * flags._hasSkill_["兔兔"], }, 狼狼: { name: "狼狼", text: "40MP,召唤30+50*LV生命的灰太狼,在场时提升勇士2+4*LV点攻击", image: "I390", maxLv: 5, pos: [3, 9], need: [{ 召唤之书: 1 }], exp: 50 * flags._hasSkill_["狼狼"], }, 牛牛: { name: "牛牛", text: "40MP,召唤50+40*LV生命的牛牛,在场时提升勇士3%+5%*Lv伤害增幅", image: "I391", maxLv: 5, pos: [5, 9], need: [{ 召唤之书: 1 }], exp: 50 * flags._hasSkill_["牛牛"], }, 花花: { name: "花花", text: "40MP,召唤50+100*LV生命上限、具有同等生命的花花,战后回复15%已损失生命值", image: "I392", maxLv: 5, pos: [1, 9], need: [{ 召唤之书: 1 }], exp: 50 * flags._hasSkill_["花花"], }, }; var Skill_2 = { 图腾之书: { name: "图腾之书", text: "学习后各图腾提升为1级,获得图腾之书,所有图腾共享20次公共战斗冷却", image: "I394", maxLv: 1, pos: [1, 7], need: null, exp: 200, }, 再生: { name: "再生", text: "召唤兽:花花在场时可使用,生命恢复提升10%*LV,召唤兽:花花离场后移除", image: "I392", maxLv: 5, pos: [1, 5], need: [{ 图腾之书: 1 }], exp: 50 * flags._hasSkill_["再生"], }, 嗜血: { name: "嗜血", text: "召唤兽:灰太狼在场时可使用,勇士获得3%*LV吸血,召唤兽:灰太狼离场后移除", image: "I390", maxLv: 5, pos: [3, 5], need: [{ 图腾之书: 1 }], exp: 50 * flags._hasSkill_["嗜血"], }, 顽强: { name: "顽强", text: "召唤兽:牛牛在场时可使用,除牛牛外所有召唤物战后增加5*Lv生命(花花不可超过生命上限),召唤兽:牛牛离场后移除", image: "I391", maxLv: 5, pos: [5, 5], need: [{ 图腾之书: 1 }], exp: 50 * flags._hasSkill_["顽强"], }, 涅槃: { name: "涅槃", text: "召唤兽:蝴蝶在场时可使用,战斗时任意召唤兽死亡时触发,召唤兽临时消失,该召唤兽生命变更为50*LV并在战斗结束后重新召唤,此技能触发后战斗结束后移除", image: "I388", maxLv: 5, pos: [7, 5], need: [{ 图腾之书: 1 }], exp: 50 * flags._hasSkill_["涅槃"], }, 贪婪: { name: "贪婪", text: "召唤兽:兔兔在场时可使用,每次战斗后额外获得0.5*Lv点法力(向上取整)0和LV点金币,召唤兽:兔兔离场后移除", image: "I389", maxLv: 5, pos: [9, 5], need: [{ 图腾之书: 1 }], exp: 50 * flags._hasSkill_["贪婪"], }, 攻击: { name: "攻击", text: "被动,提升勇者2点攻击", image: "atk", maxLv: null, pos: [3, 9], need: null, exp: 20 + 5 * flags._hasSkill_["攻击"], }, 防御: { name: "防御", text: "被动,提升勇者2点防御", image: "def", maxLv: null, pos: [5, 9], need: null, exp: 20 + 5 * flags._hasSkill_["防御"], }, 魔力上限: { name: "魔力上限", text: "被动,提升勇者10点MP上限", image: "mana", maxLv: null, pos: [7, 9], need: null, exp: 30 + 10 * flags._hasSkill_["魔力上限"], }, 火球: { name: "火球", text: "消耗8MP,第一回合不进行普通攻击,改为发出一枚100%+LV*20%攻击伤害的火球", image: "I397", maxLv: 5, pos: [3, 11], need: null, exp: 30 * flags._hasSkill_["火球"], }, 嘲讽: { name: "嘲讽", text: "消耗10MP,本次强制怪物以勇士为目标,同时勇士获得5%*LV减伤", image: "I399", maxLv: 5, pos: [5, 11], need: null, exp: 30 + 30 * flags._hasSkill_["嘲讽"], }, 治疗: { name: "治疗", text: "消耗10点MP,战斗时勇者第一回合改为为自己施加100%+20%*lv防御数值的治疗术", image: "I396", maxLv: 5, pos: [7, 11], need: null, exp: 20 + 20 * flags._hasSkill_["治疗"], }, }; var AllSkill = [Skill_1, Skill_2]; ///////////把每页技能数组id填入这里,比如三页技能要加上Skill3 return AllSkill; }; core.myskilltree = function () { //进入 if (!flags._hasSkill_) flags._hasSkill_ = {}; core.clearUI(); core.status.holdingKeys = []; core.lockControl(); core.status.event.page = 0; core.status.event.data = 0; let Skill = core.AllStatus()[core.status.event.page]; core.status.event.pos = Object.values(Skill)[0].pos; //初始坐标 core.status.event.id = "myskilltree"; core.myskilltree_draw(); ///重绘页面 }; core.quit_myskilltree = function () { core.clearUI(); core.status.event.id = null; core.unlockControl(); if (core.isReplaying()) core.replay(); }; core.myskilltree_add = function () { //加点 let Skill = core.AllStatus()[core.status.event.page]; var Index = core.status.event.data; var id = Object.keys(Skill)[Index]; var data = Object.values(Skill)[Index]; let ness; if (data.need) { if (Array.isArray(data.need)) { //多前置 for (var need of data.need) { var need_name = Object.keys(need); //if (!Object.keys(flags._hasSkill_).includes(need)) if ( !flags._hasSkill_[need_name] || flags._hasSkill_[need_name] < need[need_name] ) { ness = true; } } } else { //单前置 if (!Object.keys(flags._hasSkill_).includes(data.need)) { ness = true; } } } if (ness) { core.drawTip(id + " 未习得前置"); } else if (hero.exp < data.exp) { core.drawTip(id + " 经验值不足"); } else if (flags._hasSkill_[id] === data.maxLv) { core.drawTip(id + " 技能已满级"); } else { ///学习成功 flags._hasSkill_[id] = flags._hasSkill_[id] + 1 || 1; hero.exp -= data.exp; core.myskilltree_get(id); core.status.route.push( "skill:" + Index + ":" + data.pos[0] + ":" + data.pos[1] + ":" + core.status.event.page ); } core.myskilltree_draw(); ///重绘页面 }; core.myskilltree_get = function (id) { ////技能升级效果 switch (id) { case "图腾之书": flags._hasSkill_["再生"] = 1; flags._hasSkill_["嗜血"] = 1; flags._hasSkill_["顽强"] = 1; flags._hasSkill_["涅槃"] = 1; flags._hasSkill_["贪婪"] = 1; core.getItem("I394"); break; case "攻击": core.status.hero.atk += 2; break; case "防御": core.status.hero.def += 2; break; case "魔力上限": core.status.hero.manamax += 10; break; case "嘲讽": core.getItem("I399"); break; case "治疗": core.getItem("I396"); break; } }; core.myskilltree_draw = function () { //绘制 总 core.clearMap("ui"); let Skill = core.AllStatus()[core.status.event.page]; var Index = core.status.event.data; ///推荐在此处 drawImage 绘制背景图 core.setAlpha("ui", 0.8); core.setFillStyle("ui", "#dddddd"); core.fillRect("ui", 0, 0, core.__PIXELS__, core.__PIXELS__, "#000000"); core.setAlpha("ui", 1); let name = Object.keys(Skill)[Index]; let text = Object.values(Skill)[Index].text; let exp = Object.values(Skill)[Index].exp; core.myskilltree_draw_text(name, text, exp); core.myskilltree_draw_tree(); core.setTextAlign("ui", "right"); core.fillText( "ui", "返回游戏", 342, 342, "#ffffff", ui.prototype._buildFont(15, true) ); core.setTextAlign("ui", "center"); core.fillText( "ui", "上页", 352 / 2 - 50, 342, "#ffffff", ui.prototype._buildFont(15, true) ); core.fillText( "ui", "下页", 352 / 2 + 50, 342, "#ffffff", ui.prototype._buildFont(15, true) ); core.fillText( "ui", core.status.event.page + 1 + "/" + core.AllStatus().length, 352 / 2, 342, "#ffffff", ui.prototype._buildFont(15, true) ); }; core.myskilltree_draw_tree = function () { //绘制 树 let x0 = 0, y0 = -80; //相对坐标 let nx = 32, ny = 32; //间隔 let Skill = core.AllStatus()[core.status.event.page]; for (var value of Object.values(Skill)) { //先绘制前置线条,否则会遮挡技能图标 let posx = value.pos[0]; let posy = value.pos[1]; //前置技能 if (value.need) { if (Array.isArray(value.need)) { //多前置 for (var need of value.need) { var need_name = Object.keys(need); //if (!Object.keys(flags._hasSkill_).includes(need)) if ( !flags._hasSkill_[need_name] || flags._hasSkill_[need_name] < need[need_name] ) { core.setFilter("ui", "brightness(50%)grayscale(70%)"); core.setAlpha("ui", 0.7); } let need_x = Skill[need_name].pos[0]; let need_y = Skill[need_name].pos[1]; core.myskilltree_draw_line( x0 + posx * nx + 16, y0 + posy * ny + 16, x0 + posx * nx + 16, y0 + need_y * ny + 16, 1 ); //y core.myskilltree_draw_line( x0 + posx * nx + 16, y0 + need_y * ny + 16, x0 + need_x * nx + 16, y0 + need_y * ny + 16, 2 ); //x } } else { //单前置 if (!Object.keys(flags._hasSkill_).includes(value.need)) { core.setFilter("ui", "brightness(50%)grayscale(70%)"); core.setAlpha("ui", 0.7); } let need_x = Skill[value.need].pos[0]; let need_y = Skill[value.need].pos[1]; core.myskilltree_draw_line( x0 + posx * nx + 16, y0 + posy * ny + 16, x0 + posx * nx + 16, y0 + need_y * ny + 16, 1 ); //y core.myskilltree_draw_line( x0 + posx * nx + 16, y0 + need_y * ny + 16, x0 + need_x * nx + 16, y0 + need_y * ny + 16, 2 ); //x } } core.setFilter("ui", ""); core.setAlpha("ui", 1); } core.setTextAlign("ui", "center"); let Ex = core.status.event.pos[0]; let Ey = core.status.event.pos[1]; //技能图标 for (var value of Object.values(Skill)) { let posx = value.pos[0]; let posy = value.pos[1]; let image = value.image; let boxx = x0 + posx * nx; let boxy = y0 + posy * ny; let name = value.name; let lv = flags._hasSkill_[name] || 0; //图标 core.myskilltree_draw_box(boxx, boxy, image); //未学习前置的技能 黑色滤镜 let ness; if (value.need) { if (Array.isArray(value.need)) { //多前置 for (var need of value.need) { var need_name = Object.keys(need); // if (!Object.keys(flags._hasSkill_).includes(need)) if ( !flags._hasSkill_[need_name] || flags._hasSkill_[need_name] < need[need_name] ) { core.setFilter("ui", "brightness(0%)"); core.setAlpha("ui", 0.7); ness = true; } } } else { //单前置 if (!Object.keys(flags._hasSkill_).includes(value.need)) { core.setFilter("ui", "brightness(0%)"); core.setAlpha("ui", 0.7); ness = true; } } } //未学习的技能 灰色滤镜 if (!ness && !flags._hasSkill_[name]) { core.setFilter("ui", "brightness(50%)grayscale(70%)"); core.setAlpha("ui", 0.7); } core.drawIcon("ui", image, boxx, boxy, 32, 32); //core.drawImage('ui', image, 0, 0, 32, 32, boxx, boxy, 32, 32); core.setFilter("ui", ""); core.setAlpha("ui", 1); if (Ex === posx && Ey === posy) core.strokeRoundRect( "ui", boxx - 2, boxy - 2, 36, 36, 4, "#ffff80", 2 ); core.setAlpha("ui", 0.6); core.fillRoundRect("ui", boxx, boxy + 16 + 8, 32, 16, 4, "#000000"); core.setAlpha("ui", 1); core.fillText( "ui", lv + (value.maxLv == null ? "" : "/" + value.maxLv), boxx + 16, boxy + 16 + 8 + 15, lv === value.maxLv ? "#ffff80" : "#ffffff", ui.prototype._buildFont(15, true) ); } }; core.myskilltree_draw_text = function (name, text, exp) { // 绘制 说明文本 let x0 = 0, y0 = 50; //相对坐标 core.setTextAlign("ui", "left"); core.fillText( "ui", name, x0 + 10, y0 - 20, "#ffffff", ui.prototype._buildFont(22, true) ); core.fillText( "ui", "升级经验:" + exp + "/" + hero.exp, x0 + 200, y0 - 20, "#ffffff", ui.prototype._buildFont(15, true) ); var height = null; var max_height = 110; if (text) { for (var fontSize = 17; fontSize >= 9; fontSize -= 2) { var config = { left: x0 + 10, top: y0 - 12, fontSize: 12, maxWidth: 352 - 20, bold: true, color: "#E1E1E1", }; height = 42 + core.getTextContentHeight(text, config); if (height < max_height || fontSize == 9) { core.drawTextContent("ui", text, config); break; } } } }; core.myskilltree_draw_box = function (x, y, image) { //盒子包装 let col = "#dddddd"; core.fillRoundRect("ui", x, y, 32, 32, 4, col); core.strokeRoundRect("ui", x - 2, y - 2, 36, 36, 4, col, 1); }; core.myskilltree_draw_line = function (x1, y1, x2, y2, type) { //线 包装 //return; let col = "#dddddd"; if (type === 1) { core.drawLine("ui", x1 - 4, y1, x2 - 4, y2, col, 1); core.drawLine("ui", x1 + 4, y1, x2 + 4, y2, col, 1); core.drawLine("ui", x1, y1, x2, y2, col, 4); } else if (type === 2) { core.drawLine("ui", x1, y1 - 4, x2, y2 - 4, col, 1); core.drawLine("ui", x1, y1 + 4, x2, y2 + 4, col, 1); core.drawLine("ui", x1, y1, x2, y2, col, 4); } }; core.myChooseOnKey = function (dir) { //计算方向键应选择的坐标 let Skill = core.AllStatus()[core.status.event.page]; let x = core.status.event.pos[0]; let y = core.status.event.pos[1]; var temp = []; var temp2 = []; for (var i in Object.values(Skill)) { let posx = Object.values(Skill)[i].pos[0]; let posy = Object.values(Skill)[i].pos[1]; let Index = Number(i); let ok = (dir === "up" && posy < y) || (dir === "down" && posy > y) || (dir === "left" && posx < x) || (dir === "right" && posx > x); let ok2 = ((dir === "up" || dir === "down") && x === posx) || ((dir === "left" || dir === "right") && y === posy); if (ok) temp.push([posx, posy, Index]); if (ok && ok2) temp2.push([posx, posy, Index]); } if (temp2[0]) { if (temp2.length === 1) { return temp2[0]; } else { let R = 999; let rx, ry, ri; for (var value of temp2) { let posx = value[0]; let posy = value[1]; let Index = value[2]; let r = Math.abs(posx - x + posy - y); if (r < R) { R = r; rx = posx; ry = posy; ri = Index; } } return [rx, ry, ri]; } } else if (temp.length === 1) { return temp[0]; } else { let R = 9999; let rx, ry, ri; for (var value of temp) { let posx = value[0]; let posy = value[1]; let Index = value[2]; let r = Math.pow(posx - x, 2) + Math.pow(posy - y, 2); if (r < R) { R = r; rx = posx; ry = posy; ri = Index; } } if (rx && ry) return [rx, ry, ri]; } }; this.myskilltree_keyDown = function (keycode) { if (core.status.event.id !== "myskilltree") return false; let Skill = core.AllStatus()[core.status.event.page]; let temp; if ( keycode === 37 && !core.myChooseOnKey("left") && core.status.event.page > 0 ) { core.status.event.page = core.status.event.page - 1; core.status.event.data = 0; Skill = core.AllStatus()[core.status.event.page]; core.status.event.pos = Object.values(Skill)[0].pos; } else if ( keycode === 39 && !core.myChooseOnKey("right") && core.status.event.page < core.AllStatus().length - 1 ) { core.status.event.page = core.status.event.page + 1; core.status.event.data = 0; Skill = core.AllStatus()[core.status.event.page]; core.status.event.pos = Object.values(Skill)[0].pos; } else if (keycode === 38 && core.myChooseOnKey("up")) { temp = core.myChooseOnKey("up"); } else if (keycode === 40 && core.myChooseOnKey("down")) { temp = core.myChooseOnKey("down"); } else if (keycode === 37 && core.myChooseOnKey("left")) { temp = core.myChooseOnKey("left"); } else if (keycode === 39 && core.myChooseOnKey("right")) { temp = core.myChooseOnKey("right"); } if (temp) { core.status.event.pos[0] = temp[0]; core.status.event.pos[1] = temp[1]; core.status.event.data = temp[2]; } if (keycode === 32 || keycode === 13 || keycode === 67) core.myskilltree_add(); core.myskilltree_draw(); ///重绘页面 return true; }; core.registerAction("keyDown", "myskilltree", "myskilltree_keyDown", 100); var myskilltree = function (keycode) { if (core.status.event.id !== "myskilltree") return false; if (keycode == 88 || keycode == 70 || keycode == 27) { ////x、q和Esc退出 core.quit_myskilltree(); } return true; ///全部拦截 }; core.registerAction("keyUp", "myskilltree", myskilltree, 100); this.myskilltree_onmove = function (x, y, px, py) { if (core.status.event.id !== "myskilltree") return false; //if (core.domStyle.isVertical) return false let x0 = 0, y0 = -80; //相对坐标 let nx = 32, ny = 32; //间隔 if (py >= 65 && py <= 320) { let Skill = core.AllStatus()[core.status.event.page]; for (var i in Object.values(Skill)) { let posx = Object.values(Skill)[i].pos[0]; let posy = Object.values(Skill)[i].pos[1]; let boxx = x0 + posx * nx; let boxy = y0 + posy * ny; if (px >= boxx && px <= boxx + 32 && py >= boxy && py <= boxy + 32) { if (core.status.event.data !== Number(i)) { core.status.event.pos[0] = posx; core.status.event.pos[1] = posy; core.status.event.data = Number(i); core.myskilltree_draw(); ///重绘页面 } //core.status.event.mouse = Number(i) //if(core.status.event.mouse) } } } return true; }; core.registerAction("onmove", "myskilltree", "myskilltree_onmove", 100); core.registerAction( "onclick", "myskilltree", function (x, y, px, py) { if (core.status.event.id !== "myskilltree") return false; let x0 = 0, y0 = -80; //相对坐标 let nx = 32, ny = 32; //间隔 let Skill = core.AllStatus()[core.status.event.page]; //console.log(px + ',' + py); if (py >= 65 && py <= 320) { for (var i in Object.values(Skill)) { let posx = Object.values(Skill)[i].pos[0]; let posy = Object.values(Skill)[i].pos[1]; let boxx = x0 + posx * nx; let boxy = y0 + posy * ny; if ( px >= boxx && px <= boxx + 32 && py >= boxy && py <= boxy + 32 ) { if (core.status.event.data === Number(i)) { core.myskilltree_add(); } else if (core.status.event.data !== Number(i)) { core.status.event.pos[0] = posx; core.status.event.pos[1] = posy; core.status.event.data = Number(i); core.myskilltree_draw(); ///重绘页面 } } } } else if (px >= 280 && py >= 320) { core.quit_myskilltree(); } else if ( px >= 90 && px <= 165 && py >= 320 && core.status.event.page > 0 ) { core.status.event.page = core.status.event.page - 1; core.status.event.data = 0; core.status.event.pos = Object.values(Skill)[0].pos; core.myskilltree_draw(); ///重绘页面 } else if ( px >= 190 && px <= 280 && py >= 320 && core.status.event.page < core.AllStatus().length - 1 ) { core.status.event.page = core.status.event.page + 1; core.status.event.data = 0; core.status.event.pos = Object.values(Skill)[0].pos; core.myskilltree_draw(); ///重绘页面 } return true; }, 100 ); control.prototype._replayAction_skill = function (action) { if (action.indexOf("skill:") != 0) return false; core.myskilltree(); var pos = action.substring(6).split(":"); core.status.event.data = Number(pos[0]); core.status.event.pos[0] = Number(pos[1]); core.status.event.pos[1] = Number(pos[2]); core.status.event.page = Number(pos[3]); core.myskilltree_add(); //core.myskilltree_draw(); core.replay(); if (core.status.replay.speed == 24) { core.quit_myskilltree(); return true; } setTimeout(function () { core.quit_myskilltree(); //core.ui.closePanel(); }, core.control.__replay_getTimeout()); return true; }; core.registerReplayAction("skill", control.prototype._replayAction_skill); }, "animate": function () { // -------------------- 插件说明 -------------------- // // github仓库:https://github.com/unanmed/animate // npm包名:mutate-animate // npm地址:https://www.npmjs.com/package/mutate-animate // 不要去尝试读这个插件,这个插件是经过了打包的,不是人类可读的( // 想读的话可以去github读 // 该插件是一个轻量型多功能动画插件,可以允许你使用内置或自定义的速率曲线或轨迹等 // 除此之外,你还可以自定义绘制函数,来让你的动画可视化 // -------------------- 安装说明 -------------------- // // 直接复制到插件中即可,注意所有插件中不能出现插件名为animate的插件 // 该插件分为动画和渐变两部分,教程分开,动画在前,渐变在后 // -------------------- 动画使用教程 -------------------- // // 1. 首先创建一个异步函数 // async function ani() { } // 2. 引入插件中的类和函数,引入内容要看个人需求,所有可用的函数在本插件末尾可以看到 // const { Animation, linear, bezier, circle, hyper, trigo, power, inverseTrigo, shake, sleep } = core.plugin.animate // 3. 在函数内部创建一个动画 // const animate = new Animation(); // 4. 为动画创建一个绘制函数,这里以绘制一个矩形为例,当然也可以使用core.fillRect替代ctx.fillRect来绘制矩形 // const ctx = core.createCanvas('animate', 0, 0, 416, 416, 100); // ctx.save(); // const fn = () => { // ctx.restore(); // ctx.save(); // ctx.clearRect(0, 0, 800, 800); // ctx.translate(animate.x, animate.y); // ctx.rotate(animate.angle * Math.PI / 180); // const size = animate.size; // ctx.fillRect(-30 * size, -30 * size, 60 * size, 60 * size); // } // animate.ticker.add(fn); // 5. 执行动画 // 下面先对一些概念进行解释 // 动画分为很多种,内置的有move(移动至某一点) rotate(旋转) scale(放缩) moveAs(以指定路径移动) shake(震动) // 对于不同的动画种类,其所对应的属性也不同,move moveAs shake均对应x和y这两个属性 // rotate对应angle,scale对应size。你也可以自定义属性,这个之后会提到 // 除了执行动画之外,这里还提供了三个等待函数,可以等待某个动画执行完毕,以及一个等待指定时长的函数 // 分别是animate.n(等待指定数量的动画执行完毕) // animate.w(等待指定类型的动画执行完毕,也可以是自定义类型) // animate.all(等待所有动画执行完毕) // sleep(等待指定时长) // 执行动画时,要求一个渐变函数,当然这个插件内置了非常丰富的渐变函数,也就是速率曲线。 // 线性渐变函数 linear(),该函数返回一个线性变化函数 // 三角渐变函数 trigo('sin' | 'sec', EaseMode),该函数返回一个指定属性的三角函数变化函数 // 其中EaseMode可以填'in' 'out' 'in-out' 'center' // 分别表示 慢-快 快-慢 慢-快-慢 快-慢-快 // 幂函数渐变 power(n, EaseMode),该函数返回一个以x^n变化的函数,n是指数 // 双曲渐变函数 hyper('sin' | 'tan' | 'sec', EaseMode),该函数返回一个双曲函数,分别是双曲正弦、双曲正切、双曲正割 // 反三角渐变函数 inverseTrigo('sin' | 'tan', EaseMode),该函数返回一个反三角函数 // 贝塞尔曲线渐变函数 bezier(...cps),参数为贝塞尔曲线的控制点纵坐标(横坐标不能自定义,毕竟一个时刻不能对应多个速率) // 示例:bezier(0.4, 0.2, 0.7); // 三个控制点的四次贝塞尔曲线渐变函数 // 了解完渐变函数以后,这里还有一个特殊的渐变函数-shake // shake(power, timing),这个函数是一个震荡函数,会让一个值来回变化,实现震动的效果 // 其中power是震动的最大值,timing是渐变函数,描述了power在震动时大小的变化 // 下面,我们就可以进行动画的执行了,我们以 运动 + 旋转 + 放缩为例 // animate.mode(hyper('sin', 'out')) // 设置渐变函数为 双曲正弦 快 -> 慢,注意不能加分号 // .time(1000) // 设置动画的执行时间为1000毫秒 // .move(300, 300) // 移动至[300, 300]的位置 // .relative() // 设置相对模式为相对之前,与之前为相加的关系 // .mode(power(3, 'center')) // 设置为 x^3 快-慢-快 的渐变函数 // .time(3000) // .rotate(720) // 旋转720度 // .absolute() // 设置相对模式为绝对 // .mode(trigo('sin', 'in')) // 设置渐变函数为 正弦 慢 -> 快 // .time(1500) // .scale(3); // 放缩大小至3倍 // 这样,我们就把三种基础动画都执行了一遍,同时,这种写法非常直观,出现问题时也可以很快地找到问题所在 // 下面,我们需要等待动画执行完毕,因为同一种动画不可能同时执行两个 // await animate.n(1); // 等待任意一个动画执行完毕,别把await忘了 // await animate.w('scale'); // 等待放缩动画执行完毕 // await animate.all(); // 等待所有动画执行完毕 // await sleep(1000); // 等待1000毫秒 // 下面,还有一个特殊的动画函数-moveAs // 这是一个非常强大的函数,它允许你让你的物体按照指定路线运动 // 说到这,我们需要先了解一下运动函数。 // 该插件内置了两个运动函数,分别是圆形运动和贝塞尔曲线运动 // 圆形运动 circle(r, n, timing, inverse),r是圆的半径,n是圈数,timing描述半径大小的变化,inverse说明了是否翻转timing函数,后面三个可以不填 // 贝塞尔曲线 bezierPath(start, end, ...cps) // 其中start和end是起点和结束点,应当填入[x, y]数组,cps是控制点,也是[x, y]数组 // 示例:bezierPath([0, 0], [200, 200], [100, 50], [300, 150], [200, 180]); // 这是一个起点为 [0, 0],终点为[200, 200],有三个控制点的四次贝塞尔曲线 // 下面,我们就可以使用路径函数了 // animate.mode(hyper('sin', 'in-out')) // 设置渐变曲线 // .time(5000) // .relative() // 设置为相对模式,这个比较必要,不然的话很可能出现瞬移 // .moveAs(circle(100, 5, linear())) // 创建一个5圈的半径从0至100逐渐变大的圆轨迹(是个螺旋线)并让物体沿着它运动 // // 最后,还有一个震动函数 shake(x, y),x和y表示了在横向上和纵向上的震动幅度,1表示为震动幅度的100% // 示例: // animate.mode(shake(5, hyper('sin', 'in')), true) // 这里第二个参数说明是震动函数 // .time(2000) // .shake(1, 0.5) // 这样,所有内置动画就已经介绍完毕 // 6. 自定义动画属性 // 本插件允许你自定义一个动画属性,但功能可能不会像自带的属性那么强大 // 你可以在创建动画之后使用animate.register(key, init)来注册一个自定义属性 // 其中key是自定义属性的名称,init是自定义属性的初始值,这个值应当在0-1之间变化 // 你可以通过animate.value[key]来获取你注册的自定义属性 // 对于自定义属性的动画,你应当使用animate.apply(key, n, first) // 其中,key是你的自定义属性的名称,n是其目标值,first是一个布尔值,说明了是否将该动画插入到目前所有的动画之前,即每帧会优先执行该动画 // 下面是一个不透明度的示例 // animate.register('opacity', 1); // 这句话应该放到刚创建动画之后 // ctx.globalAlpha = animate.value.opacity; // 这句话应当放到每帧绘制的函数里面,放在绘制之前 // animate.mode(bezier(0.9, 0.1, 0.05)) // 设置渐变函数 // .time(2000) // .absolute() // .apply('opacity', 0.3); // 将不透明度按照渐变曲线更改为0.3 // 7. 运行动画 // 还记得刚开始定义的async function 吗,直接调用它就能执行动画了! // 示例:ani(); // 执行刚刚写的所有动画 // 8. 自定义速率曲线和路径 // 该插件中,速率曲线和路径均可自定义 // 对于速率曲线,其类型为 (input: number) => number // 它接受一个范围在 0-1 的值,输出一个 0-1 的值,表示了动画的完成度,1表示动画已完成,0表示动画刚开始(当前大于1小于0也不会报错,也会执行相应的动画) // 对于路径,其类型为 (input: number) => [number, number] // 它与速率曲线类似,接收一个 0-1 的值,输出一个坐标数组 // 9. 多个属性绑定 // 该插件中,你可以绑定多个动画属性,你可以使用ani.bind(...attr)来绑定。 // 绑定之后,这三个动画属性可以被一个返回了长度为3的数组的渐变函数执行。 // 绑定使用ani.bind,设置渐变函数仍然使用ani.mode,注意它与单个动画属性是分开的,也就是它不会影响正常的渐变函数。 // 然后使用ani.applyMulti即可执行动画 // 例如: // // 自定义的一个三属性渐变函数 // function b(input) { // return [input * 100, input ** 2 * 100, input ** 3 * 100]; // } // ani.bind('a', 'b', 'c') // 这样会绑定abc这三个动画属性 // .mode(b) // 自定义的一个返回了长度为3的数组的函数 // .time(5000) // .absolute() // .applyMulti(); // 执行这个动画 // 9. 监听 动画的生命周期钩子 // 这个插件还允许你去监听动画的状态,可以监听动画的开始、结束、运行 // 你可以使用 animate.listen(type, fn)来监听,fn的类型是 (a: Animation, type: string) => void // 当然,一般情况下你不会用到这个功能,插件中已经帮你包装了三个等待函数,他们就是以这些监听为基础的 // 10. 自定义时间获取函数 // 你可以修改ani.getTime来修改动画的时间获取函数,例如想让动画速度变成一半可以写ani.getTime = () => Date.now() / 2 // 这样可以允许你随意控制动画的运行速度,暂停,甚至是倒退。该值默认为`Date.now` // -------------------- 渐变使用教程 -------------------- // // 相比于动画,渐变属于一种较为简便的动画,它可以让你在设置一个属性后使属性缓慢变化值目标值而不是突变至目标值 // 现在假设你已经了解了动画的使用,下面我们来了解渐变。 // 1. 创建一个渐变实例 // 与动画类似,你需要使用new来实例化一个渐变,当然别忘了引入 // const { Transition } = core.plugin.animate; // const tran = new Transition(); // 2. 绘制 // const ctx = core.createCanvas('transition', 0, 0, 416, 416, 100); // ctx.save(); // const fn = () => { // ctx.restore(); // ctx.save(); // ctx.clearRect(0, 0, 800, 800); // ctx.beginPath(); // ctx.arc(tran.value.x, tran.value.y, 50, 0, Math.PI * 2); // 使用tran.value.xxx获取当前的属性 // ctx.fill(); // // 当然也可以用样板的api,例如core.fillCircle();等 // } // animate.ticker.add(fn); // 3. 设置渐变 // 同样,与动画类似,你可以使用tran.time()设置渐变时间,使用tran.mode()设置渐变函数,使用tran.absolute()和tran.relative()设置相对模式 // 例如: // tran.time(1000) // .mode(hyper('sin', 'out')) // .absolute(); // 4. 初始化渐变属性 // 与动画不同的是,动画在执行一个自定义属性前都需要register,而渐变不需要。 // 你可以通过tran.value.xxx = yyy来设置动画属性或使用tran.transition('xxx', yyy)来设置 // 你的首次赋值即是初始化了渐变属性,这时是不会执行渐变的,例如: // tran.value.x = 200; // tran.transition('y', 200); // 上述例子便是将 x 和 y 初始化成了200 // 5. 执行渐变 // 初始化完成后,便可以直接执行渐变了,有两种方法 // tran.value.x = 400; // 将 x 缓慢移动至400 // tran.transition('y', 400); // 将 y 缓慢移动至400 // 6. 自定义时间获取函数 // 与动画类似,你依然可以通过修改tran.getTime来修改时间获取函数 if (main.replayChecking) return (core.plugin.animate = {}); var M = Object.defineProperty; var E = (n, s, t) => s in n ? M(n, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : (n[s] = t); var o = (n, s, t) => (E(n, typeof s != "symbol" ? s + "" : s, t), t); let b = []; const k = (n) => { for (const s of b) if (s.status === "running") try { for (const t of s.funcs) t(n - s.startTime); } catch (t) { s.destroy(), console.error(t); } requestAnimationFrame(k); }; requestAnimationFrame(k); class I { constructor() { o(this, "funcs", []); o(this, "status", "stop"); o(this, "startTime", 0); (this.status = "running"), b.push(this), requestAnimationFrame((s) => (this.startTime = s)); } add(s, t = !1) { return t ? this.funcs.unshift(s) : this.funcs.push(s), this; } remove(s) { const t = this.funcs.findIndex((e) => e === s); if (t === -1) throw new ReferenceError( "You are going to remove nonexistent ticker function." ); return this.funcs.splice(t, 1), this; } clear() { this.funcs = []; } destroy() { this.clear(), this.stop(); } stop() { (this.status = "stop"), (b = b.filter((s) => s !== this)); } } class F { constructor() { o(this, "timing"); o(this, "relation", "absolute"); o(this, "easeTime", 0); o(this, "applying", {}); o(this, "getTime", Date.now); o(this, "ticker", new I()); o(this, "value", {}); o(this, "listener", {}); this.timing = (s) => s; } async all() { if (Object.values(this.applying).every((s) => s === !0)) throw new ReferenceError("There is no animates to be waited."); await new Promise((s) => { const t = () => { Object.values(this.applying).every((e) => e === !1) && (this.unlisten("end", t), s("all animated.")); }; this.listen("end", t); }); } async n(s) { const t = Object.values(this.applying).filter((i) => i === !0).length; if (t < s) throw new ReferenceError( `You are trying to wait ${s} animate, but there are only ${t} animate animating.` ); let e = 0; await new Promise((i) => { const r = () => { e++, e === s && (this.unlisten("end", r), i(`${s} animated.`)); }; this.listen("end", r); }); } async w(s) { if (this.applying[s] === !1) throw new ReferenceError(`The ${s} animate is not animating.`); await new Promise((t) => { const e = () => { this.applying[s] === !1 && (this.unlisten("end", e), t(`${s} animated.`)); }; this.listen("end", e); }); } listen(s, t) { var e, i; (i = (e = this.listener)[s]) != null || (e[s] = []), this.listener[s].push(t); } unlisten(s, t) { const e = this.listener[s].findIndex((i) => i === t); if (e === -1) throw new ReferenceError( "You are trying to remove a nonexistent listener." ); this.listener[s].splice(e, 1); } hook(...s) { const t = Object.entries(this.listener).filter((e) => s.includes(e[0])); for (const [e, i] of t) for (const r of i) r(this, e); } } function T(n) { return n != null; } async function R(n) { return new Promise((s) => setTimeout(s, n)); } class Y 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 (T(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: i, shakeTiming: r } = this, h = this.getTime(); if ((this.hook("start", "shakestart"), i <= 0)) return this.end(!1, "shake"), this; const l = () => { const c = this.getTime() - h; if (c > i) { this.ticker.remove(l), (this.applying.shake = !1), (this.sx = 0), (this.sy = 0), this.hook("end", "shakeend"); return; } const a = c / i, m = r(a); (this.sx = m * t), (this.sy = m * e); }; return this.ticker.add(l), (this.animateFn.system.shake = l), this; } moveAs(t) { this.applying.moveAs && this.end(!0, "moveAs"), (this.applying.moveAs = !0), (this.path = t); const { easeTime: e, relation: i, timing: r } = this, h = this.getTime(), [l, u] = [this.x, this.y], [c, a] = (() => { if (i === "absolute") return t(1); { const [d, f] = t(1); return [l + d, u + f]; } })(); if ((this.hook("start", "movestart"), e <= 0)) return this.end(!1, "moveAs"), this; const m = () => { const f = this.getTime() - h; if (f > e) { this.end(!0, "moveAs"); return; } const v = f / e, [g, w] = t(r(v)); i === "absolute" ? ((this.ox = g), (this.oy = w)) : ((this.ox = l + g), (this.oy = u + w)); }; return ( this.ticker.add(m, !0), (this.animateFn.system.moveAs = m), (this.targetValue.system.moveAs = [c, a]), 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, i = !1) { 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 r = this.value[t], h = this.getTime(), { timing: l, relation: u, easeTime: c } = this, a = u === "absolute" ? e - r : e; if ((this.hook("start"), c <= 0)) return this.end(!1, t), this; const m = () => { const f = this.getTime() - h; if (f > c) { this.end(!1, t); return; } const v = f / c, g = l(v); this.value[t] = r + g * a; }; return ( this.ticker.add(m, i), (this.animateFn.custom[t] = m), (this.targetValue.custom[t] = a + r), this ); } applyMulti(t = !1) { this.applying["@@bind"] === !0 && this.end(!1, "@@bind"), (this.applying["@@bind"] = !0); const e = this.bindInfo, i = e.map((m) => this.value[m]), r = this.getTime(), { multiTiming: h, relation: l, easeTime: u } = this, c = h(1); if (c.length !== i.length) throw new TypeError( `The number of binded animate attributes and timing function returns's length does not match. binded: ${e.length}, timing: ${c.length}` ); if ((this.hook("start"), u <= 0)) return this.end(!1, "@@bind"), this; const a = () => { const d = this.getTime() - r; if (d > u) { this.end(!1, "@@bind"); return; } const f = d / u, v = h(f); e.forEach((g, w) => { l === "absolute" ? (this.value[g] = v[w]) : (this.value[g] = i[w] + v[w]); }); }; return ( this.ticker.add(a, t), (this.animateFn.custom["@@bind"] = a), (this.targetValue.system["@@bind"] = c), this ); } applySys(t, e, i) { i !== "move" && this.applying[i] === !0 && this.end(!0, i), (this.applying[i] = !0); const r = this[t], h = this.getTime(), l = this.timing, u = this.relation, c = this.easeTime, a = u === "absolute" ? e - r : e; if ((this.hook("start", `${i}start`), c <= 0)) return this.end(!1, i); const m = () => { const f = this.getTime() - h; if (f > c) { this.end(!0, i); return; } const v = f / c, g = l(v); (this[t] = r + a * g), t !== "oy" && this.hook(i); }; this.ticker.add(m, !0), t === "ox" ? (this.animateFn.system.move[0] = m) : t === "oy" ? (this.animateFn.system.move[1] = m) : (this.animateFn.system[i] = m), i === "move" ? (t === "ox" && (this.targetValue.system.move[0] = a + r), t === "oy" && (this.targetValue.system.move[1] = a + r)) : i !== "shake" && (this.targetValue.system[i] = a + 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 [i, r] = this.targetValue.system.move; (this.ox = i), (this.oy = r), this.hook("moveend", "end"); } else if (e === "moveAs") { const [i, r] = this.targetValue.system.moveAs; (this.ox = i), (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, h) => { this.value[r] = this.targetValue.system["@@bind"][h]; }) : ((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 j extends F { constructor() { super(); o(this, "now", {}); o(this, "target", {}); o(this, "transitionFn", {}); o(this, "value"); o(this, "handleSet", (t, e, i) => (this.transition(e, i), !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 (!T(this.now[t])) return (this.now[t] = e), this; this.applying[t] && this.end(t, !0), (this.applying[t] = !0), this.hook("start"); const i = this.getTime(), r = this.easeTime, h = this.timing, l = this.now[t], u = e + (this.relation === "absolute" ? 0 : l), c = u - l; this.target[t] = u; const a = () => { const d = this.getTime() - i; if (d >= r) { this.end(t); return; } const f = d / r; (this.now[t] = h(f) * c + l), this.hook("running"); }; return ( (this.transitionFn[t] = a), r <= 0 ? (this.end(t), this) : (this.ticker.add(a), this) ); } end(t, e = !1) { const i = this.transitionFn[t]; if (!T(i)) 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 x = (...n) => n.reduce((s, t) => s + t, 0), y = (n) => { if (n === 0) return 1; let s = n; for (; n > 1;) n--, (s *= n); return s; }, A = (n, s) => Math.round(y(s) / (y(n) * y(s - n))), p = (n, s, t = (e) => 1 - s(1 - e)) => n === "in" ? s : n === "out" ? t : n === "in-out" ? (e) => (e < 0.5 ? s(e * 2) / 2 : 0.5 + t((e - 0.5) * 2) / 2) : (e) => (e < 0.5 ? t(e * 2) / 2 : 0.5 + s((e - 0.5) * 2) / 2), $ = Math.cosh(2), z = Math.acosh(2), V = Math.tanh(3), P = Math.atan(5); function O() { return (n) => n; } function q(...n) { const s = [0].concat(n); s.push(1); const t = s.length, e = Array(t) .fill(0) .map((i, r) => A(r, t - 1)); return (i) => { const r = e.map((h, l) => h * s[l] * (1 - i) ** (t - l - 1) * i ** l); return x(...r); }; } function U(n, s) { if (n === "sin") { const t = (i) => Math.sin((i * Math.PI) / 2); return p(s, (i) => 1 - t(1 - i), t); } if (n === "sec") { const t = (i) => 1 / Math.cos(i); return p(s, (i) => t((i * Math.PI) / 3) - 1); } throw new TypeError( "Unexpected parameters are delivered in trigo timing function." ); } function C(n, s) { if (!Number.isInteger(n)) throw new TypeError( "The first parameter of power timing function only allow integer." ); return p(s, (e) => e ** n); } function G(n, s) { if (n === "sin") return p(s, (e) => (Math.cosh(e * 2) - 1) / ($ - 1)); if (n === "tan") { const t = (i) => (Math.tanh(i * 3) * 1) / V; return p(s, (i) => 1 - t(1 - i), t); } if (n === "sec") { const t = (i) => 1 / Math.cosh(i); return p(s, (i) => 1 - (t(i * z) - 0.5) * 2); } throw new TypeError( "Unexpected parameters are delivered in hyper timing function." ); } function N(n, s) { if (n === "sin") { const t = (i) => (Math.asin(i) / Math.PI) * 2; return p(s, (i) => 1 - t(1 - i), t); } if (n === "tan") { const t = (i) => Math.atan(i * 5) / P; return p(s, (i) => 1 - t(1 - i), t); } throw new TypeError( "Unexpected parameters are delivered in inverse trigo timing function." ); } function B(n, s = () => 1) { let t = -1; return (e) => ( (t *= -1), e < 0.5 ? n * s(e * 2) * t : n * s((1 - e) * 2) * t ); } function D(n, s = 1, t = [0, 0], e = 0, i = (h) => 1, r = !1) { return (h) => { const l = s * h * Math.PI * 2 + (e * Math.PI) / 180, u = Math.cos(l), c = Math.sin(l), a = n * i(i(r ? 1 - h : h)); return [a * u + t[0], a * c + t[1]]; }; } function H(n, s, ...t) { const e = [n].concat(t); e.push(s); const i = e.length, r = Array(i) .fill(0) .map((h, l) => A(l, i - 1)); return (h) => { const l = r.map( (c, a) => c * e[a][0] * (1 - h) ** (i - a - 1) * h ** a ), u = r.map((c, a) => c * e[a][1] * (1 - h) ** (i - a - 1) * h ** a); return [x(...l), x(...u)]; }; } if ("animate" in core.plugin) throw new ReferenceError(`插件中已存在名为animate的属性!`); core.plugin.animate = { Animation: Y, AnimationBase: F, Ticker: I, Transition: j, sleep: R, circle: D, bezierPath: H, linear: O, bezier: q, trigo: U, power: C, hyper: G, inverseTrigo: N, shake: B, }; }, "func": function () { // 功能函数集,具体有哪些函数看每个函数前的注释即可 // 安装方式:直接复制到插件里面,注意新建插件自带的 function () { } 不能删 // 使用方式:可以直接使用对象解构按需引入 // 例如:const { has, slide } = core.plugin.utils; // slide([1, 2, 3], -1); // [2, 3, 1] /** * 滑动数组,使数组元素平移若干项 * @example slide([1, 2, 3], -1); // [2, 3, 1] * @example slide([1, 3, 5], 10); // [5, 3, 1]; * @param {any[]} arr 需要滑动的数组 * @param {number} delta 滑动的项数,正负均可 */ function slide(arr, delta) { if (delta === 0) return arr; delta %= arr.length; if (delta > 0) { arr.unshift(...arr.splice(arr.length - delta, delta)); return arr; } if (delta < 0) { arr.push(...arr.splice(0, -delta)); return arr; } } /** * 获取一个方向的反方向 * @example backDir('up'); // 'down' * @example backDir('leftup'); // 'rightdown' * @param {string} dir 方向 */ function backDir(dir) { const map = { up: "down", down: "up", left: "right", right: "left", leftup: "rightdown", leftdown: "rightup", rightdown: "leftup", rightup: "leftdown", }; if (!dir in map) { throw new TypeError( `Wrong dir is delivered when getting back direction.` ); } return map[dir]; } /** * 判断一个值是否不是undefined和null * @example has(0); // true * @example has(false); // true * @example has(NaN); // true * @example has(null); // false * @param {any} v 要判断的值 */ function has(v) { return v !== null && v !== void 0; } /** * 解析css字符串为CSSStyleDeclaration对象 * @example * parseCss('background-color: cyan; cursor: pointer; user-select: none'); * // 输出 { backgroundColor: 'cyan', cursor: 'pointer', userSelect: 'none' } * @param {string} css 要解析的css字符串 */ function parseCss(css) { const str = css.replace(/[\n\s\t]*/g, "").replace(/;*/g, ";"); const styles = str.split(";"); const res = {}; for (const one of styles) { const [key, data] = one.split(":"); const cssKey = key.replace(/\-([a-z])/g, (str, $1) => $1.toUpperCase()); res[cssKey] = data; } return res; } /** * 等待一段时间,需在async function中使用,否则报错 * @example await sleep(500); // 等待500毫秒 * @param {number} time 等待的毫秒数 */ async function sleep(time) { return new Promise((res) => setTimeout(res, time)); } /** * 在下一帧的下一帧执行一个函数 * @example nextFrame(() => console.log(1)); // 两帧后在控制台输出1 * @param cb 执行的函数 */ function nextFrame(cb) { requestAnimationFrame(() => { requestAnimationFrame(cb); }); } /** * 将一个css颜色解析成一个rgba数组 * 目前仅支持 #RGB #RGBA #RRGGBB #RRGGBBAA rgb() rgba() hsl() hsla() css自带颜色 这几种的转换 * @exmaple parseColor('#fff'); // [255, 255, 255] * @example parseColor('#abcd'); // [170, 187, 204, 0.8666666666666667] * @example parseColor('rgba(170, 230, 13, 0.2)'); // [170, 230, 13, 0.2] * @example parseColor('cyan'); // [0, 255, 255] * @example parseColor('lightcoral'); // [240, 128, 128] * @example parseColor('hsla(0.2, 0.3, 0.4, 0.2)'); // [120, 133, 71, 0.2] * @example parseColor('rgba(20%, 50, 33%, 0.2)'); // [51, 50, 84.15, 0.2] * @param color 要解析的颜色字符串 */ function parseColor(color) { if (color.startsWith("rgb")) { // rgb const match = color.match(/rgba?\([\d\,\s\.%]+\)/); if (!has(match)) throw new Error(`Invalid color is delivered!`); const l = color.includes("a"); return match[0] .slice(l ? 5 : 4, -1) .split(",") .map((v, i) => { const vv = v.trim(); if (vv.endsWith("%")) { if (i === 3) { return parseInt(vv) / 100; } else { return (parseInt(vv) * 255) / 100; } } else return parseFloat(vv); }) .slice(0, l ? 4 : 3); } else if (color.startsWith("#")) { // 十六进制 const content = color.slice(1); if (![3, 4, 6, 8].includes(content.length)) { throw new Error(`Invalid color is delivered!`); } if (content.length <= 4) { const res = content.split("").map((v) => Number(`0x${v}${v}`)); if (res.length === 4) res[3] /= 255; return res; } else { const res = Array(content.length / 2) .fill(1) .map((v, i) => Number(`0x${content[i * 2]}${content[i * 2 + 1]}`)); if (res.length === 4) res[3] /= 255; return res; } } else if (color.startsWith("hsl")) { // hsl,转成rgb后输出 const match = color.match(/hsla?\([\d\,\s\.%]+\)/); if (!has(match)) throw new Error(`Invalid color is delivered!`); const l = color.includes("a"); const hsl = match[0] .slice(l ? 5 : 4, -1) .split(",") .map((v) => { const vv = v.trim(); if (vv.endsWith("%")) return parseInt(vv) / 100; else return parseFloat(vv); }); const rgb = hslToRgb(hsl[0], hsl[1], hsl[2]); return l ? rgb.concat([hsl[3]]) : rgb; } else { // 单词 const rgb = cssColors[color]; if (!has(rgb)) { throw new Error(`Invalid color is delivered!`); } return parseColor(rgb); } } /** * hsl转rgb * @param h 色相 * @param s 饱和度 * @param l 亮度 */ function hslToRgb(h, s, l) { if (s == 0) { return [0, 0, 0]; } else { const hue2rgb = (p, q, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; }; const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; const r = hue2rgb(p, q, h + 1 / 3); const g = hue2rgb(p, q, h); const b = hue2rgb(p, q, h - 1 / 3); return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; } } /** * 确保一个变量是一个数组,不是的话转为数组并返回,是的话直接返回传入的数组 * @param arr 要判断的变量 * @example ensureArray(1); // [1] * @example ensureArray([1, 2]); // [1, 2] * @example ensureArray('test'); // ['test'] */ function ensureArray(arr) { // @ts-ignore return arr instanceof Array ? arr : [arr]; } /** * 返回一个坐标在某个方向上移动 d 格后的坐标 * @param d 移动多少格,默认为1 * @example ofDir(7, 7, 'left'); // [6, 7] * @example ofDir(10, 8, 'leftup', 5); // [5, 3] */ function ofDir(x, y, dir, d = 1) { const { x: dx, y: dy } = core.utils.scan2[dir]; return [x + dx * d, y + dy * d]; } const cssColors = { black: "#000000", silver: "#c0c0c0", gray: "#808080", white: "#ffffff", maroon: "#800000", red: "#ff0000", purple: "#800080", fuchsia: "#ff00ff", green: "#00FF00", lime: "#00ff00", olive: "#808000", yellow: "#ffff00", navy: "#000080", blue: "#0000ff", teal: "#008080", aqua: "#00ffff", orange: "#ffa500", aliceblue: "#f0f8ff", antiquewhite: "#faebd7", aquamarine: "#7fffd4", azure: "#f0ffff", beige: "#f5f5dc", bisque: "#ffe4c4", blanchedalmond: "#ffebcd", blueviolet: "#8a2be2", brown: "#a52a2a", burlywood: "#deb887", cadetblue: "#5f9ea0", chartreuse: "#7fff00", chocolate: "#d2691e", coral: "#ff7f50", cornflowerblue: "#6495ed", cornsilk: "#fff8dc", crimson: "#dc143c", cyan: "#00ffff", darkblue: "#00008b", darkcyan: "#008b8b", darkgoldenrod: "#b8860b", darkgray: "#a9a9a9", darkgreen: "#006400", darkgrey: "#a9a9a9", darkkhaki: "#bdb76b", darkmagenta: "#8b008b", darkolivegreen: "#556b2f", darkorange: "#ff8c00", darkorchid: "#9932cc", darkred: "#8b0000", darksalmon: "#e9967a", darkseagreen: "#8fbc8f", darkslateblue: "#483d8b", darkslategray: "#2f4f4f", darkslategrey: "#2f4f4f", darkturquoise: "#00ced1", darkviolet: "#9400d3", deeppink: "#ff1493", deepskyblue: "#00bfff", dimgray: "#696969", dimgrey: "#696969", dodgerblue: "#1e90ff", firebrick: "#b22222", floralwhite: "#fffaf0", forestgreen: "#228b22", gainsboro: "#dcdcdc", ghostwhite: "#f8f8ff", gold: "#ffd700", goldenrod: "#daa520", greenyellow: "#adff2f", grey: "#808080", honeydew: "#f0fff0", hotpink: "#ff69b4", indianred: "#cd5c5c", indigo: "#4b0082", ivory: "#fffff0", khaki: "#f0e68c", lavender: "#e6e6fa", lavenderblush: "#fff0f5", lawngreen: "#7cfc00", lemonchiffon: "#fffacd", lightblue: "#add8e6", lightcoral: "#f08080", lightcyan: "#e0ffff", lightgoldenrodyellow: "#fafad2", lightgray: "#d3d3d3", lightgreen: "#90ee90", lightgrey: "#d3d3d3", lightpink: "#ffb6c1", lightsalmon: "#ffa07a", lightseagreen: "#20b2aa", lightskyblue: "#87cefa", lightslategray: "#778899", lightslategrey: "#778899", lightsteelblue: "#b0c4de", lightyellow: "#ffffe0", limegreen: "#32cd32", linen: "#faf0e6", magenta: "#ff00ff", mediumaquamarine: "#66cdaa", mediumblue: "#0000cd", mediumorchid: "#ba55d3", mediumpurple: "#9370db", mediumseagreen: "#3cb371", mediumslateblue: "#7b68ee", mediumspringgreen: "#00fa9a", mediumturquoise: "#48d1cc", mediumvioletred: "#c71585", midnightblue: "#191970", mintcream: "#f5fffa", mistyrose: "#ffe4e1", moccasin: "#ffe4b5", navajowhite: "#ffdead", oldlace: "#fdf5e6", olivedrab: "#6b8e23", orangered: "#ff4500", orchid: "#da70d6", palegoldenrod: "#eee8aa", palegreen: "#98fb98", paleturquoise: "#afeeee", palevioletred: "#db7093", papayawhip: "#ffefd5", peachpuff: "#ffdab9", peru: "#cd853f", pink: "#ffc0cb", plum: "#dda0dd", powderblue: "#b0e0e6", rosybrown: "#bc8f8f", royalblue: "#4169e1", saddlebrown: "#8b4513", salmon: "#fa8072", sandybrown: "#f4a460", seagreen: "#2e8b57", seashell: "#fff5ee", sienna: "#a0522d", skyblue: "#87ceeb", slateblue: "#6a5acd", slategray: "#708090", slategrey: "#708090", snow: "#fffafa", springgreen: "#00ff7f", steelblue: "#4682b4", tan: "#d2b48c", thistle: "#d8bfd8", tomato: "#ff6347", turquoise: "#40e0d0", violet: "#ee82ee", wheat: "#f5deb3", whitesmoke: "#f5f5f5", yellowgreen: "#9acd32", transparent: "#0000", }; if (has(core.plugin.utils)) { throw new ReferenceError( `core.plugin上已经有'utils2'属性,因此功能函数插件将无法使用!` ); } core.plugin.utils = { has, slide, backDir, parseCss, sleep, nextFrame, parseColor, hslToRgb, ensureArray, ofDir, fillTextVertical }; // Utility.js // 通用函數插件 // 本插件與古祠發佈的《功能插件 --- 实用功能函数集》不同,函數是定義在全域的。 // 自訂常見事件模板插件(editorBlocklyconfigPlus.js)的前置插件 /** * 使js暫停指定時間 * async環境下await Sleep(500) * @param {number} millisecond 暫停毫秒數 */ async function Sleep(millisecond) { return new Promise((resolve) => setTimeout(resolve, millisecond)); }; /** * 使js暫停一幀 * async環境下await SleepFrame() */ async function SleepFrame() { return new Promise((resolve) => requestAnimationFrame(resolve)); }; /** * editor_file的isset函數 */ function isset(val) { if (val == undefined || val == null) { return false; } return true; }; function fillTextVertical(name, text, x, y, style, fontsize, font) { //竖向文字绘制 const ctx = core.ui.getContextByName(name); if (!ctx) return; const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); const length = text.length; core.maps._setHDCanvasSize(context, fontsize * 2, fontsize * length * 1.5, ) if (style) context.fillStyle = core.arrayToRGBA(style); if (fontsize) context.font = core.ui._buildFont(fontsize, true, false, font); let arrText = text.split(""); let arrWidth = arrText.map(function (letter) { return context.measureText(letter).width; }); let align = context.textAlign; let baseline = context.textBaseline; let sx = fontsize, sy = fontsize; if (align == "left") { sx = sx + Math.max.apply(null, arrWidth) / 2; } else if (align == "right") { sx = sx - Math.max.apply(null, arrWidth) / 2; } if ( baseline == "bottom" || baseline == "alphabetic" || baseline == "ideographic" ) { sy = sy - arrWidth[0] / 2; } else if (baseline == "top" || baseline == "hanging") { sy = sy + arrWidth[0] / 2; } context.textAlign = "center"; context.textBaseline = "middle"; // 开始逐字绘制 arrText.forEach(function (letter, index) { // 确定下一个字符的纵坐标位置 context.fillText(letter, sx, sy); // 旋转坐标系还原成初始态 //context.setTransform(1, 0, 0, 1, 0, 0); // 确定下一个字符的纵坐标位置 var letterWidth = (fontsize * length * 1.5 - 2 * fontsize) / (length - 1); sy = sy + letterWidth; }); // 水平垂直对齐方式还原 context.textAlign = align; context.textBaseline = baseline; const ratio = core.domStyle.scale * devicePixelRatio //绘制到目标位置 ctx.drawImage( canvas, (x - fontsize), (y - fontsize * length * 1.5 / 2), canvas.width / ratio, canvas.height / ratio ); } /** * editor_file的checkCallback函數 */ function checkCallback(callback) { if (!isset(callback)) { printe("未设置callback"); throw "未设置callback"; } }; }, "自定义常用事件": function () { // editorBlocklyconfigPlus.js // 自訂常見事件模板插件 // 本插件引用了通用函數插件(Utility.js) // 適用樣板:2.10.3 // 請注意: // 此插件對事件編輯器(editor_blocklyconfig)進行複寫,若還有其它針對事件編輯器做複寫的插件,請謹慎使用! // 此插件對表格操作行為(editor_mode.doActionList)進行複寫,若還有其它對表格操作行為做複寫的插件,請謹慎使用! // 使用方法: // 現在在主頁下拉選單多了個常用事件模版,在那邊可以自由設定常用事件模板。 // 設定完後按F5刷新,再到事件編輯器看就有你設定好的常用事件模板了。 if (main.mode == "editor") { //#region 配置表格初始化 let TableFileName = "project/table/CommonEventTemplate_comment.js"; let TableRow = ` var CommonEventTemplate_comment = {"_type": "object", "_data": { "CommonEventTemplate": { "_type": "object", "_data": function (key) { var obj = { "检测音乐如果没有开启则系统提示开启": { "_leaf": true, "_type": "event", "_event": "commonEvent", "_data": "检测音乐如果没有开启则系统提示开启" }, "仿新新魔塔一次性商人": { "_leaf": true, "_type": "event", "_event": "commonEvent", "_data": "仿新新魔塔一次性商人" }, "全地图选中一个点": { "_leaf": true, "_type": "event", "_event": "commonEvent", "_data": "全地图选中一个点" }, "多阶段Boss战斗": { "_leaf": true, "_type": "event", "_event": "commonEvent", "_data": "多阶段Boss战斗" }, } if (obj[key]) return obj[key]; return { "_leaf": true, "_type": "event", "_event": "commonEvent", "_data": "常見事件模板" } } } }} `; if (!events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate) { /** * @type {{[EvnetName:actionParserJson]}} */ events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate = { 检测音乐如果没有开启则系统提示开启: [{ type: "if", condition: "!core.musicStatus.bgmStatus", true: [ "\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳", ], false: [], }, ], 仿新新魔塔一次性商人: [{ type: "if", condition: "switch:A", true: [ "\t[行商,trader]\b[this]这是购买我的道具后我给玩家的提示。", { type: "comment", text: "下一条指令可视情况使用或不使用", }, { type: "hide", remove: true, time: 250, }, ], false: [{ type: "confirm", text: "我有3把黄钥匙,\n你出50金币就卖给你。", yes: [{ type: "if", condition: "status:money>=50", true: [{ type: "setValue", name: "status:money", operator: "-=", value: "50", }, { type: "setValue", name: "item:yellowKey", operator: "+=", value: "3", }, { type: "playSound", name: "确定", stop: true, }, { type: "setValue", name: "switch:A", value: "true", }, ], false: [{ type: "playSound", name: "操作失败", }, "\t[行商,trader]\b[this]你的金币不足!", ], }, ], no: [], }, ], }, ], 全地图选中一个点: [{ type: "comment", text: "全地图选中一个点,需要用鼠标或触屏操作", }, { type: "setValue", name: "temp:X", value: "status:x", }, { type: "setValue", name: "temp:Y", value: "status:y", }, { type: "tip", text: "再次点击闪烁位置确认", }, { type: "while", condition: "true", data: [{ type: "drawSelector", image: "winskin.png", code: 1, x: "32*temp:X", y: "32*temp:Y", width: 32, height: 32, }, { type: "wait", }, { type: "if", condition: "(flag:type === 1)", true: [{ type: "if", condition: "((temp:X===flag:x)&&(temp:Y===flag:y))", true: [{ type: "break", n: 1, }, ], }, { type: "setValue", name: "temp:X", value: "flag:x", }, { type: "setValue", name: "temp:Y", value: "flag:y", }, ], }, ], }, { type: "drawSelector", code: 1, }, { type: "comment", text: "流程进行到这里可以对[X,Y]点进行处理,比如", }, { type: "closeDoor", id: "yellowDoor", loc: ["temp:X", "temp:Y"], }, ], 多阶段Boss战斗: [{ type: "comment", text: "多阶段boss,请直接作为战后事件使用", }, { type: "setValue", name: "switch:A", operator: "+=", value: "1", }, { type: "switch", condition: "switch:A", caseList: [{ case: "1", action: [{ type: "setBlock", number: "redSlime", }, "\t[2阶段boss,redSlime]\b[this]你以为你已经打败我了吗?没听说过史莱姆有九条命吗?", ], }, { case: "2", action: [{ type: "setBlock", number: "blackSlime", }, "\t[3阶段boss,blackSlime]\b[this]不能消灭我的,只会让我更强大!", ], }, { case: "3", action: [{ type: "setBlock", number: "slimelord", }, "\t[4阶段boss,slimelord]\b[this]我还能打!", ], }, { case: "4", action: ["\t[4阶段boss,slimelord]我一定会回来的!"], }, ], }, ], }; } //#endregion // 新增模板選項 let editModeSelect = document.getElementById("editModeSelect"); let newEditModeOption = document.createElement("option"); newEditModeOption.value = "CommonEventTemplate"; newEditModeOption.text = "常見事件模板"; editModeSelect.add(newEditModeOption); //檢查可用的編輯模板ID let leftIDNumber = 11 - 1; let ExistLeftElement = document.querySelector(".main"); while (ExistLeftElement) { leftIDNumber++; ExistLeftElement = document.getElementById(`left${leftIDNumber}`); } //新增編輯模板 let MainDiv = document.querySelector(".main"); let CommonEventTemplateMainDiv = document.createElement("div"); CommonEventTemplateMainDiv.id = `left${leftIDNumber}`; CommonEventTemplateMainDiv.className = "leftTab"; CommonEventTemplateMainDiv.style.zIndex = "-1"; CommonEventTemplateMainDiv.style.opacity = "0"; CommonEventTemplateMainDiv.innerHTML = `

常見事件模板           

条目 注释
`; MainDiv.appendChild(CommonEventTemplateMainDiv); (async function () { //等待編輯器初始化 while (!editor_mode.ids) { await Sleep(100); } //新增編輯模板ID editor_mode.ids["CommonEventTemplate"] = `left${leftIDNumber}`; editor_mode.init_dom_ids(); //切換至常見事件模板 editor_mode.CommonEventTemplate = function (callback) { var objs = []; editor.file.editCommonEventTemplate([], function (objs_) { objs = objs_; //console.log(objs_) }); //只查询不修改时,内部实现不是异步的,所以可以这么写 var tableinfo = editor.table.objToTable(objs[0], objs[1]); document.getElementById( "table_298572d8-93dd-4c6e-a278-6a7d49831e3a" ).innerHTML = tableinfo.HTML; tableinfo.listen(tableinfo.guids); if (Boolean(callback)) callback(); }; //檢查配置表格存在 let TableRowExist = null; fs.readFile(TableFileName, "base64", function (err, data) { if (err) { console.log(`察覺常見事件模板配置表格不存在,原因:${err}`); console.log("新建一個常見事件模板配置表格。"); TableRowExist = false; } else { TableRowExist = true; } }); //等待配置表格載入完畢(最多0.3秒,超過則視為失敗) for (let i = 0; i < 3; i++) { if (TableRowExist == null) { await Sleep(100); } } //配置表格初始化 if (TableRowExist != true) { fs.mkdir("project/table", function (err, data) { if (err) throw `常見事件模板配置表格目錄初始化失敗,原因:${err}`; }); fs.writeFile( TableFileName, editor.util.encode64(TableRow || ""), "base64", function (err, data) { if (err) throw `常見事件模板配置表格文件初始化失敗,原因:${err}`; } ); } //載入配置表格 //editor.file.loadCommentjs(callback); (function () { var key = "CommonEventTemplate_comment"; var script = document.createElement("script"); script.src = "project/table/" + key + ".js"; document.body.appendChild(script); script.onload = function () { editor.file[key] = eval(key.replace(".", "_")); var loaded = Boolean(editor.file[key]); }; })(); //按下配置表格 editor_multi.CommonEventTemplateEditCommentJs = function (mod) { editor_multi.lintAutocomplete = true; editor_multi.setLint(); editor_multi.importFile(TableFileName); }; //定義表格操作行為 editor_mode.OriginDoActionList = editor_mode.doActionList; editor_mode.doActionList = function (mode, actionList, callback) { if (editor_mode.mode == "CommonEventTemplate") { if (actionList.length == 0) return; printf("修改中..."); var cb = function (objs_) { if (objs_.slice(-1)[0] != null) { printe(objs_.slice(-1)[0]); throw objs_.slice(-1)[0]; } var str = "修改成功!"; if ( data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.name == "template" ) str += "
请注意:全塔属性的name尚未修改,请及时予以设置。"; printf(str); if (callback) callback(); }; editor.file.editCommonEventTemplate(actionList, cb); } else { editor_mode.OriginDoActionList(mode, actionList, callback); } }; //添加表格列 editor.table.CommonEventTemplateAddFunc = function () { let obj = events_c12a15a8_c380_4b28_8144_256cba95f760; // 1.输入id let newid = prompt("请输入新项的ID(支持中文)"); if (newid == null || newid.length == 0) { return; } // 2.检查id是否符合规范或与已有id重复 var conflict = true; var basefield = "".replace(/\[[^\[]*\]$/, ""); try { var baseobj = eval("obj" + basefield); conflict = newid in baseobj; } catch (ee) { // 理论上这里不会发生错误 printe(ee); throw ee; } if (conflict) { printe("id已存在, 请直接修改该项的值"); return; } // 3.添加 editor_mode.addAction(["add", basefield + "['" + newid + "']", null]); editor_mode.onmode("save", function () { printf("添加成功,刷新后生效;也可以继续新增其他项目。"); }); //自动保存 删掉此行的话点保存按钮才会保存 }; //對表格的存讀 editor.file.editCommonEventTemplate = function (actionList, callback) { /*actionList:[ ["change","['test']",['123']], ] 为[]时只查询不修改 */ var data_obj = events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate; checkCallback(callback); if (isset(actionList) && actionList.length > 0) { actionList.forEach(function (value) { value[1] = "['CommonEventTemplate']" + value[1]; }); editor.file.saveSetting("events", actionList, function (err) { callback([err]); }); } else { callback([ Object.assign({}, data_obj), editor.file.CommonEventTemplate_comment._data.CommonEventTemplate, null, ]); } }; })(); //複寫事件編輯器(editor_blocklyconfig) editor_blocklyconfig = function () { // start mark sfergsvae (function () { var getCategory = function (name, custom) { for (var node of document.getElementById("toolbox").children) { if (node.getAttribute("name") == name) return node; } var node = document.createElement("category"); node.setAttribute("name", name); if (custom) node.setAttribute("custom", custom); document.getElementById("toolbox").appendChild(node); return node; }; var toolboxObj = { 入口方块: [ '', MotaActionFunctions.actionParser.parse( [ "欢迎使用事件编辑器", "本事件触发一次后会消失", { type: "hide", time: 500 }, ], "event" ), MotaActionFunctions.actionParser.parse({ condition: "flag:__door__===2", currentFloor: true, priority: 0, delayExecute: false, multiExecute: false, data: [{ type: "openDoor", loc: [10, 5] }], }, "autoEvent" ), MotaActionBlocks["changeFloor_m"].xmlText(), MotaActionFunctions.actionParser.parse( [{ id: "shop1", text: "\t[贪婪之神,moneyShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:", textInList: "1F金币商店", choices: [{ text: "生命+800", need: "status:money>=20+2*flag:shop1", action: [{ type: "comment", text: "新版商店中需要手动扣减金币和增加访问次数", }, { type: "setValue", name: "status:money", operator: "-=", value: "20+2*flag:shop1", }, { type: "setValue", name: "flag:shop1", operator: "+=", value: "1", }, { type: "setValue", name: "status:hp", operator: "+=", value: "800", }, ], }, ], }, { id: "itemShop", item: true, textInList: "道具商店", choices: [{ id: "yellowKey", number: 10, money: 10 }], }, { id: "keyShop1", textInList: "回收钥匙商店", commonEvent: "回收钥匙商店", args: "", }, ], "shop" ), MotaActionBlocks["common_m"].xmlText(), MotaActionBlocks["beforeBattle_m"].xmlText(), MotaActionBlocks["afterBattle_m"].xmlText(), MotaActionBlocks["afterGetItem_m"].xmlText(), MotaActionBlocks["afterOpenDoor_m"].xmlText(), MotaActionBlocks["firstArrive_m"].xmlText(), MotaActionBlocks["eachArrive_m"].xmlText(), MotaActionBlocks["level_m"].xmlText(), MotaActionFunctions.actionParser.parse( [ ["MTx", ""] ], "floorPartition" ), MotaActionBlocks["commonEvent_m"].xmlText(), MotaActionBlocks["item_m"].xmlText(), MotaActionFunctions.actionParser.parse( [{ title: "简单", name: "Easy", hard: 1, action: [ { type: "comment", text: "在这里写该难度需执行的事件" }, ], }, ], "levelChoose" ), MotaActionFunctions.actionParser.parse({ type: 0, value: { atk: 10 }, percentage: { speed: 10 }, }, "equip" ), MotaActionFunctions.actionParser.parse( [{ name: "bg.jpg", x: 0, y: 0, canvas: "bg", }, ], "floorImage" ), MotaActionFunctions.actionParser.parse({ time: 160, openSound: "door.mp3", closeSound: "door.mp3", keys: { yellowKey: 1, orangeKey: 1 }, }, "doorInfo" ), MotaActionBlocks["faceIds_m"].xmlText(), MotaActionBlocks["mainStyle_m"].xmlText(), MotaActionFunctions.actionParser.parse({ 背景音乐: "bgm.mp3", 确定: "confirm.mp3", 攻击: "attack.mp3", 背景图: "bg.jpg", 领域: "zone", 文件名: "file.jpg", }, "nameMap" ), MotaActionFunctions.actionParser.parse( [{ name: "hero.png", width: 32, height: 32, prefix: "hero_" }], "splitImages" ), ], 显示文字: [ MotaActionBlocks["text_0_s"].xmlText(), MotaActionBlocks["text_1_s"].xmlText(), MotaActionFunctions.actionParser.parseList( "\t[小妖精,fairy]\f[fairy.png,0,0]欢迎使用事件编辑器(双击方块可直接预览)" ), MotaActionBlocks["moveTextBox_s"].xmlText(), MotaActionBlocks["clearTextBox_s"].xmlText(), MotaActionBlocks["comment_s"].xmlText(), MotaActionBlocks["autoText_s"].xmlText(), MotaActionBlocks["scrollText_s"].xmlText(), MotaActionBlocks["setText_s"].xmlText(), MotaActionBlocks["tip_s"].xmlText(), MotaActionBlocks["confirm_s"].xmlText(), MotaActionBlocks["choices_s"].xmlText([ "选择剑或者盾", "流浪者", "man", 0, "", MotaActionBlocks["choicesContext"].xmlText([ "剑", "", "", null, "", "", MotaActionFunctions.actionParser.parseList([ { type: "openDoor", loc: [3, 3] }, ]), ]), ]), MotaActionBlocks["win_s"].xmlText(), MotaActionBlocks["lose_s"].xmlText(), MotaActionBlocks["restart_s"].xmlText(), ], 数据相关: [ MotaActionBlocks["setValue_s"].xmlText([ MotaActionBlocks["idIdList_e"].xmlText(["status", "生命"]), "=", "", false, ]), MotaActionBlocks["setEnemy_s"].xmlText(), MotaActionBlocks["setEnemyOnPoint_s"].xmlText(), MotaActionBlocks["resetEnemyOnPoint_s"].xmlText(), MotaActionBlocks["moveEnemyOnPoint_s"].xmlText(), MotaActionBlocks["moveEnemyOnPoint_1_s"].xmlText(), MotaActionBlocks["setEquip_s"].xmlText(), MotaActionBlocks["setFloor_s"].xmlText(), MotaActionBlocks["setGlobalAttribute_s"].xmlText(), MotaActionBlocks["setGlobalValue_s"].xmlText(), MotaActionBlocks["setGlobalFlag_s"].xmlText(), MotaActionBlocks["setNameMap_s"].xmlText(), MotaActionBlocks["input_s"].xmlText(), MotaActionBlocks["input2_s"].xmlText(), MotaActionBlocks["update_s"].xmlText(), MotaActionBlocks["moveAction_s"].xmlText(), MotaActionBlocks["changeFloor_s"].xmlText(), MotaActionBlocks["changePos_s"].xmlText(), MotaActionBlocks["battle_s"].xmlText(), MotaActionBlocks["useItem_s"].xmlText(), MotaActionBlocks["loadEquip_s"].xmlText(), MotaActionBlocks["unloadEquip_s"].xmlText(), MotaActionBlocks["openShop_s"].xmlText(), MotaActionBlocks["disableShop_s"].xmlText(), MotaActionBlocks["setHeroIcon_s"].xmlText(), MotaActionBlocks["follow_s"].xmlText(), MotaActionBlocks["unfollow_s"].xmlText(), ], 地图处理: [ MotaActionBlocks["battle_1_s"].xmlText(), MotaActionBlocks["openDoor_s"].xmlText(), MotaActionBlocks["specialDoor_s"].xmlText(), MotaActionBlocks["closeDoor_s"].xmlText(), MotaActionBlocks["show_s"].xmlText(), MotaActionBlocks["hide_s"].xmlText(), MotaActionBlocks["setBlock_s"].xmlText(), MotaActionBlocks["setBlockOpacity_s"].xmlText(), MotaActionBlocks["setBlockFilter_s"].xmlText(), MotaActionBlocks["turnBlock_s"].xmlText(), MotaActionBlocks["moveHero_s"].xmlText(), MotaActionBlocks["move_s"].xmlText(), MotaActionBlocks["jumpHero_s"].xmlText(), MotaActionBlocks["jumpHero_1_s"].xmlText(), MotaActionBlocks["jump_s"].xmlText(), MotaActionBlocks["jump_1_s"].xmlText(), MotaActionBlocks["showBgFgMap_s"].xmlText(), MotaActionBlocks["hideBgFgMap_s"].xmlText(), MotaActionBlocks["setBgFgBlock_s"].xmlText(), MotaActionBlocks["showFloorImg_s"].xmlText(), MotaActionBlocks["hideFloorImg_s"].xmlText(), ], 事件控制: [ MotaActionBlocks["if_1_s"].xmlText(), MotaActionBlocks["if_s"].xmlText(), MotaActionFunctions.actionParser.parseList({ type: "switch", condition: "判别值", caseList: [{ action: [ { type: "comment", text: "当判别值是值的场合执行此事件" }, ], }, { case: "default", action: [{ type: "comment", text: "当没有符合的值的场合执行default事件", }, ], }, ], }), MotaActionFunctions.actionParser.parseList({ type: "for", name: "temp:A", from: "0", to: "12", step: "1", data: [], }), MotaActionFunctions.actionParser.parseList({ type: "forEach", name: "temp:A", list: ["status:atk", "status:def"], data: [], }), MotaActionBlocks["while_s"].xmlText(), MotaActionBlocks["dowhile_s"].xmlText(), MotaActionBlocks["break_s"].xmlText(), MotaActionBlocks["continue_s"].xmlText(), MotaActionBlocks["exit_s"].xmlText(), MotaActionBlocks["trigger_s"].xmlText(), MotaActionBlocks["insert_1_s"].xmlText(), MotaActionBlocks["insert_2_s"].xmlText(), ], 特效表现: [ MotaActionBlocks["sleep_s"].xmlText(), MotaActionFunctions.actionParser.parseList({ type: "wait", timeout: 0, data: [{ case: "keyboard", keycode: "13,32", action: [{ type: "comment", text: "当按下回车(keycode=13)或空格(keycode=32)时执行此事件\n超时剩余时间会写入flag:timeout", }, ], }, { case: "mouse", px: [0, 32], py: [0, 32], action: [{ type: "comment", text: "当点击地图左上角时执行此事件\n超时剩余时间会写入flag:timeout", }, ], }, { case: "condition", condition: "flag:type==0\n&&flag:keycode==13", action: [{ type: "comment", text: "当满足自定义条件时会执行此事件\n超时剩余时间会写入flag:timeout", }, ], }, { case: "timeout", action: [ { type: "comment", text: "当超时未操作时执行此事件" }, ], }, ], }), MotaActionBlocks["waitAsync_s"].xmlText(), MotaActionBlocks["stopAsync_s"].xmlText(), MotaActionBlocks["vibrate_s"].xmlText(), MotaActionBlocks["animate_s"].xmlText(), MotaActionBlocks["animate_1_s"].xmlText(), MotaActionBlocks["animateResize_s"].xmlText(), MotaActionBlocks["moveAnimate_s"].xmlText(), MotaActionBlocks["pauseAnimate_s"].xmlText(), MotaActionBlocks["remuseAnimate_s"].xmlText(), MotaActionBlocks["stopAnimate_s"].xmlText(), MotaActionBlocks["setViewport_s"].xmlText(), MotaActionBlocks["setViewport_1_s"].xmlText(), MotaActionBlocks["lockViewport_s"].xmlText(), MotaActionBlocks["showStatusBar_s"].xmlText(), MotaActionBlocks["hideStatusBar_s"].xmlText(), MotaActionBlocks["setHeroOpacity_s"].xmlText(), MotaActionBlocks["setCurtain_0_s"].xmlText(), MotaActionBlocks["setCurtain_1_s"].xmlText(), MotaActionBlocks["screenFlash_s"].xmlText(), MotaActionBlocks["setWeather_s"].xmlText(), MotaActionBlocks["callBook_s"].xmlText(), MotaActionBlocks["callSave_s"].xmlText(), MotaActionBlocks["autoSave_s"].xmlText(), MotaActionBlocks["forbidSave_s"].xmlText(), MotaActionBlocks["callLoad_s"].xmlText(), ], 音像处理: [ MotaActionBlocks["setanimate_s"].xmlText(), MotaActionBlocks["deleteanimate_s"].xmlText(), MotaActionBlocks["playanimate_s"].xmlText(), MotaActionBlocks["clearanimate_s"].xmlText(), MotaActionBlocks["animateloop_s"].xmlText(), MotaActionBlocks["animatereverse_s"].xmlText(), MotaActionBlocks["animatepause_s"].xmlText(), MotaActionBlocks["animatemove_s"].xmlText(), MotaActionBlocks["showImage_s"].xmlText(), MotaActionBlocks["showImage_1_s"].xmlText(), MotaActionBlocks["hideImage_s"].xmlText(), MotaActionBlocks["showTextImage_s"].xmlText(), MotaActionBlocks["moveImage_s"].xmlText(), MotaActionBlocks["rotateImage_s"].xmlText(), MotaActionBlocks["scaleImage_s"].xmlText(), MotaActionBlocks["showGif_s"].xmlText(), MotaActionBlocks["playBgm_s"].xmlText(), MotaActionBlocks["pauseBgm_s"].xmlText(), MotaActionBlocks["resumeBgm_s"].xmlText(), MotaActionBlocks["loadBgm_s"].xmlText(), MotaActionBlocks["freeBgm_s"].xmlText(), MotaActionBlocks["playSound_s"].xmlText(), MotaActionBlocks["playSound_1_s"].xmlText(), MotaActionBlocks["stopSound_s"].xmlText(), MotaActionBlocks["setVolume_s"].xmlText(), MotaActionBlocks["setBgmSpeed_s"].xmlText(), ], UI绘制: [ MotaActionBlocks["previewUI_s"].xmlText(), MotaActionBlocks["clearMap_s"].xmlText(), MotaActionBlocks["setAttribute_s"].xmlText(), MotaActionBlocks["setFilter_s"].xmlText(), MotaActionBlocks["fillText_s"].xmlText(), MotaActionBlocks["fillBoldText_s"].xmlText(), MotaActionBlocks["drawTextContent_s"].xmlText(), MotaActionBlocks["fillRect_s"].xmlText(), MotaActionBlocks["strokeRect_s"].xmlText(), MotaActionBlocks["drawLine_s"].xmlText(), MotaActionBlocks["drawArrow_s"].xmlText(), MotaActionBlocks["fillPolygon_s"].xmlText(), MotaActionBlocks["strokePolygon_s"].xmlText(), MotaActionBlocks["fillEllipse_s"].xmlText(), MotaActionBlocks["strokeEllipse_s"].xmlText(), MotaActionBlocks["fillArc_s"].xmlText(), MotaActionBlocks["strokeArc_s"].xmlText(), MotaActionBlocks["drawImage_s"].xmlText(), MotaActionBlocks["drawImage_1_s"].xmlText(), MotaActionBlocks["drawIcon_s"].xmlText(), MotaActionBlocks["drawBackground_s"].xmlText(), MotaActionBlocks["drawSelector_s"].xmlText(), MotaActionBlocks["drawSelector_1_s"].xmlText(), ], 原生脚本: [ MotaActionBlocks["function_s"].xmlText(), MotaActionBlocks["unknown_s"].xmlText(), ], 值块: [ MotaActionBlocks["setValue_s"].xmlText([ MotaActionBlocks["idIdList_e"].xmlText(["status", "生命"]), "=", "", false, ]), MotaActionBlocks["expression_arithmetic_0"].xmlText(), MotaActionBlocks["idFlag_e"].xmlText(), MotaActionBlocks["idTemp_e"].xmlText(), MotaActionBlocks["negate_e"].xmlText(), MotaActionBlocks["unaryOperation_e"].xmlText(), MotaActionBlocks["bool_e"].xmlText(), MotaActionBlocks["idString_e"].xmlText(), MotaActionBlocks["idIdList_e"].xmlText(), MotaActionBlocks["idFixedList_e"].xmlText(), MotaActionBlocks["enemyattr_e"].xmlText(), MotaActionBlocks["blockId_e"].xmlText(), MotaActionBlocks["blockNumber_e"].xmlText(), MotaActionBlocks["blockCls_e"].xmlText(), MotaActionBlocks["hasEquip_e"].xmlText(), MotaActionBlocks["equip_e"].xmlText(), MotaActionBlocks["nextXY_e"].xmlText(), MotaActionBlocks["isReplaying_e"].xmlText(), MotaActionBlocks["hasVisitedFloor_e"].xmlText(), MotaActionBlocks["isShopVisited_e"].xmlText(), MotaActionBlocks["canBattle_e"].xmlText(), MotaActionBlocks["damage_e"].xmlText(), MotaActionBlocks["damage_1_e"].xmlText(), MotaActionBlocks["rand_e"].xmlText(), MotaActionBlocks["evalString_e"].xmlText(), ], 常见事件模板: [ '', ], 最近使用事件: [ '', ], }; var toolboxgap = ''; //xml_text = MotaActionFunctions.actionParser.parse(obj,type||'event') //MotaActionBlocks['idString_e'].xmlText() //#region 動態常見事件模板 let CommonEventTemplateHTML = []; for (let commonEventName in events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate) { if ( events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate.hasOwnProperty( commonEventName ) ) { let actionParserJson = Array.from( events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate[ commonEventName ] ?? [] ); let labelHTML = ""; let blockHTML = ""; labelHTML = ``; if (actionParserJson.length > 1) { actionParserJson = { type: "if", condition: "true", true: actionParserJson, }; } else if (actionParserJson.length < 1) { actionParserJson = [ "空的常用事件模板。\n請在主頁下拉菜單中,選擇常用事件模板,進行編輯。\n編輯後需按F5刷新事件編輯器。", ]; } blockHTML = MotaActionFunctions.actionParser.parseList(actionParserJson); CommonEventTemplateHTML.push(labelHTML); CommonEventTemplateHTML.push(blockHTML); } } toolboxObj["常见事件模板"] = CommonEventTemplateHTML; //#endregion for (var name in toolboxObj) { var custom = null; if (name == "最近使用事件") custom = "searchBlockCategory"; if (name == "入口方块") custom = "entranceCategory"; getCategory(name, custom).innerHTML = toolboxObj[name].join(toolboxgap); } var blocklyArea = document.getElementById("blocklyArea"); var blocklyDiv = document.getElementById("blocklyDiv"); var workspace = Blockly.inject(blocklyDiv, { media: "_server/blockly/media/", toolbox: document.getElementById("toolbox"), zoom: { controls: true, wheel: false, //滚轮改为上下(shift:左右)翻滚 startScale: 1.0, maxScale: 3, minScale: 0.3, scaleSpeed: 1.08, }, trashcan: false, }); editor_blockly.isCommonEntry = function () { var commonEntries = [ "beforeBattle", "afterBattle", "afterOpenDoor", "firstArrive", "eachArrive", "commonEvent", "item", ]; return commonEntries.indexOf(editor_blockly.entryType) >= 0; }; editor_blockly.entranceCategoryCallback = function (workspace) { var list = toolboxObj["入口方块"]; var xmlList = []; var eventType = (editor_blockly.isCommonEntry() ? "common" : editor_blockly.entryType) + "_m"; for (var ii = 0, blockText; (blockText = list[ii]); ii++) { if ( new RegExp('').exec(blockText) ) { var block = Blockly.Xml.textToDom( "" + blockText + "" ).firstChild; block.setAttribute("gap", 5); xmlList.push(block); } } return xmlList; }; workspace.registerToolboxCategoryCallback( "entranceCategory", editor_blockly.entranceCategoryCallback ); editor_blockly.searchBlockCategoryCallback = function (workspace) { var xmlList = []; var labels = editor_blockly.searchBlock(); for (var i = 0; i < labels.length; i++) { var blockText = "" + MotaActionBlocks[labels[i]].xmlText() + ""; var block = Blockly.Xml.textToDom(blockText).firstChild; block.setAttribute("gap", 5); xmlList.push(block); } return xmlList; }; workspace.registerToolboxCategoryCallback( "searchBlockCategory", editor_blockly.searchBlockCategoryCallback ); var onresize = function (e) { blocklyDiv.style.width = blocklyArea.offsetWidth + "px"; blocklyDiv.style.height = blocklyArea.offsetHeight + "px"; Blockly.svgResize(workspace); }; if (typeof editor !== "undefined" && !editor.isMobile) window.addEventListener("resize", onresize, false); onresize(); //Blockly.svgResize(workspace); //Blockly.bindEventWithChecks_(workspace.svgGroup_,"wheel",workspace,function(e){}); document.getElementById("blocklyDiv").onmousewheel = function (e) { //console.log(e); e.preventDefault(); var hvScroll = e.shiftKey ? "hScroll" : "vScroll"; var mousewheelOffsetValue = (20 / 380) * workspace.scrollbar[hvScroll].handleLength_ * 3; workspace.scrollbar[hvScroll].handlePosition_ += (e.deltaY || 0) + (e.detail || 0) > 0 ? mousewheelOffsetValue : -mousewheelOffsetValue; workspace.scrollbar[hvScroll].onScroll_(); // workspace.setScale(workspace.scale); }; var doubleClickCheck = [ [0, "abc"] ]; function omitedcheckUpdateFunction(event) { if (event.type === "create") { editor_blockly.addIntoLastUsedType(event.blockId); } if (event.type === "ui" && event.element == "click") { var newClick = [new Date().getTime(), event.blockId]; var lastClick = doubleClickCheck.shift(); doubleClickCheck.push(newClick); if (newClick[0] - lastClick[0] < 500) { if (newClick[1] === lastClick[1]) { editor_blockly.doubleClickBlock(newClick[1]); } } } // Only handle these events if (["create", "move", "change", "delete"].indexOf(event.type) < 0) return; if (editor_blockly.workspace.topBlocks_.length >= 2) { editor_blockly.setValue("入口方块只能有一个"); return; } var eventType = editor_blockly.entryType; if (editor_blockly.workspace.topBlocks_.length == 1) { var blockType = editor_blockly.workspace.topBlocks_[0].type; if ( blockType !== eventType + "_m" && !(editor_blockly.isCommonEntry() && blockType == "common_m") ) { editor_blockly.setValue("入口方块类型错误"); return; } } try { var code = Blockly.JavaScript.workspaceToCode(workspace).replace( /\\(i|c|d|e|g|z)/g, "\\\\$1" ); editor_blockly.setValue(code); } catch (error) { editor_blockly.setValue(String(error)); if (error instanceof OmitedError) { var blockName = error.blockName; var varName = error.varName; var block = error.block; } // console.log(error); } } workspace.addChangeListener(omitedcheckUpdateFunction); workspace.addChangeListener(Blockly.Events.disableOrphans); editor_blockly.workspace = workspace; MotaActionFunctions.workspace = function () { return editor_blockly.workspace; }; // 因为在editor_blockly.parse里已经HTML转义过一次了,所以这里要覆盖掉以避免在注释中出现<等 MotaActionFunctions.xmlText = function ( ruleName, inputs, isShadow, comment, collapsed, disabled ) { var rule = MotaActionBlocks[ruleName]; var blocktext = isShadow ? "shadow" : "block"; var xmlText = []; xmlText.push( "<" + blocktext + ' type="' + ruleName + '"' + (collapsed ? ' collapsed="true"' : "") + (disabled ? ' disabled="true"' : "") + ">" ); if (!inputs) inputs = []; for (var ii = 0, inputType; (inputType = rule.argsType[ii]); ii++) { var input = inputs[ii]; var _input = ""; var noinput = input === null || input === undefined; if ( noinput && inputType === "field" && MotaActionBlocks[rule.argsGrammarName[ii]].type !== "field_dropdown" ) continue; if (noinput && inputType === "field") { noinput = false; input = rule.fieldDefault(rule.args[ii]); } if (noinput) input = ""; if ( inputType === "field" && MotaActionBlocks[rule.argsGrammarName[ii]].type === "field_checkbox" ) input = input ? "TRUE" : "FALSE"; if (inputType !== "field") { var subList = false; var subrulename = rule.argsGrammarName[ii]; var subrule = MotaActionBlocks[subrulename]; if (subrule instanceof Array) { subrulename = subrule[subrule.length - 1]; subrule = MotaActionBlocks[subrulename]; subList = true; } _input = subrule.xmlText([], true); if (noinput && !subList && !isShadow) { //无输入的默认行为是: 如果语句块的备选方块只有一个,直接代入方块 input = subrule.xmlText(); } } xmlText.push("<" + inputType + ' name="' + rule.args[ii] + '">'); xmlText.push(_input + input); xmlText.push(""); } if (comment) { xmlText.push(""); xmlText.push(comment); xmlText.push(""); } var next = inputs[rule.args.length]; if (next) { //next xmlText.push(""); xmlText.push(next); xmlText.push(""); } xmlText.push(""); return xmlText.join(""); }; })(); // end mark sfergsvae } .toString() .split("// start mark sfergsvae")[1] .split("// end mark sfergsvae")[0]; } }, "夹击激光动画": function () { function createCanvas(name, zIndex) { if (!name) return; var canvas = document.createElement('canvas'); canvas.id = name; canvas.className = 'gameCanvas'; // 将图层插入进游戏内容 document.getElementById('gameDraw').appendChild(canvas); canvas.style.zIndex = zIndex || 0; var ctx = canvas.getContext('2d'); core.canvas[name] = ctx; canvas.width = core.__PIXELS__; canvas.height = core.__PIXELS__; return canvas; } var bg3Canvas = createCanvas('bg3', 25); if (main.mode == "editor") { // 与编辑器显伤的神秘联动((( editor.dom.mapEdit.insertBefore(bg3Canvas, core.canvas.event.canvas); bg3Canvas.style.zIndex = null; } core.bigmap.canvas = ['bg', 'bg3', 'event', 'event2', 'fg', 'damage']; core.plugin._betCanvas = "bg3"; this._drawBetweenAttack = function (x, y, pos, frame) { var ctx = core.plugin._betCanvas; var w = 32, h = 68, ix = x * 32, iy = y * 32; // 左右夹击 if (pos[0]) core.drawImage(ctx, "light.png", 32 * (frame - 1), 0, 32, 68, ix, iy - 18, w, h, 90 * Math.PI / 180); // 上下夹击 if (pos[1]) core.drawImage(ctx, "light.png", 32 * (frame - 1), 0, 32, 68, ix, iy - 18, w, h); } core.registerAnimationFrame("betweenAttack", true, function (timestamp) { if (!flags.betweenAttackData) { core.clearMap(core.plugin._betCanvas, 0, 0, core.__PIXELS__, core.__PIXELS__); return; } var time = core.events._timestamp; if (time && timestamp - time < 400) return; core.clearMap(core.plugin._betCanvas, 0, 0, core.__PIXELS__, core.__PIXELS__); core.events._timestamp = timestamp; var data = flags.betweenAttackData || {}; flags._frame = flags._frame || 1; var frame = flags._frame; for (var loc in data) { var l = loc.split(","); var x = parseInt(l[0]), y = parseInt(l[1]); core.plugin._drawBetweenAttack(x, y, data[loc], frame); } flags._frame = frame + 1; if (flags._frame > 4) flags._frame = 1; }); var origin_extraDamage = core.control._updateDamage_extraDamage; core.control._updateDamage_extraDamage = function (floorId, onMap) { flags.betweenAttackData = null; if (!flags.useBetweenLight) return origin_extraDamage.call(core.control, floorId, onMap); else { core.status.damage.extraData = []; if (!core.flags.displayExtraDamage) return; var width = core.floors[floorId].width, height = core.floors[floorId].height; var startX = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posX - core.bigmap.extend) : 0; var endX = onMap && core.bigmap.v2 ? Math.min(width, core.bigmap.posX + core.__SIZE__ + core.bigmap.extend + 1) : width; var startY = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posY - core.bigmap.extend) : 0; var endY = onMap && core.bigmap.v2 ? Math.min(height, core.bigmap.posY + core.__SIZE__ + core.bigmap.extend + 1) : height; var getEnemy = function (x, y) { var id = core.getBlockId(x, y, floorId); var e = core.material.enemys[id]; if (main.mode == "editor") e = core.enemys.enemys[id]; return e; } for (var x = startX; x < endX; x++) { for (var y = startY; y < endY; y++) { var alpha = 1; if (core.noPass(x, y, floorId)) { if (core.flags.extraDamageType == 2) alpha = 0; else if (core.flags.extraDamageType == 1) alpha = 0.6; } var loc = x + "," + y; var damage = core.status.checkBlock.damage[loc] || 0; if (damage > 0) { // 该点伤害 damage = core.formatBigNumber(damage, true); var left = false, top = false; var e_left = getEnemy(x - 1, y), e_right = getEnemy(x + 1, y); var e_bottom = getEnemy(x, y - 1), e_top = getEnemy(x, y + 1); if (core.hasSpecial(e_left, 16) && core.hasSpecial(e_right, 16) && e_left.id == e_right.id) left = true; if (core.hasSpecial(e_bottom, 16) && core.hasSpecial(e_top, 16) && e_bottom.id == e_top.id) top = true; flags.betweenAttackData = flags.betweenAttackData || {}; if (flags.betweenAttackData[x + "," + y]) continue; var data = [left, top]; let [px, py] = [32 * x + 16, 32 * (y + 1) - 14]; if (left || top) { px += 15; py -= 10; flags.betweenAttackData[x + "," + y] = data; } core.plugin._drawBetweenAttack(x, y, data, 1); core.status.damage.extraData.push({ text: damage, px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha }); } else { // 检查捕捉 if (core.status.checkBlock.ambush[x + "," + y]) { core.status.damage.extraData.push({ text: '!', px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha }); } } } } } } }, "pop": function () { // 在此增加新插件 function popMove() { var ctx = core.getContextByName("popMove"); //if (!ctx) ctx = core.createCanvas( "popMove", 0, 0, core.__PIXELS__, core.__PIXELS__, 71 ); ctx.canvas.classList.add("gameCanvas", "anti-aliasing"); core.clearMap(ctx); if (core.status.replay.speed <= 3 && !flags.stopPop) { var list = core.status.popMove || []; var count = 0; list.forEach(function (one) { // 由frame计算出dy one.frame++; // 绘制 if (one.frame >= 0) core.setAlpha(ctx, 1 - one.frame / 30); else core.setAlpha(ctx, 1); var length = Math.sqrt( Math.pow(one.px2 - one.px, 2) + Math.pow(one.py2 - one.py, 2) ); //console.log(length) var li = length / 16; var lx = (one.px2 - one.px) / li; var ly = (one.py2 - one.py) / li; for (var i = 0; i < li; i += 1) { core.setAlpha(ctx, (1 - one.frame / 30) * ((i / li) * 0.8 + 0.2)); core.drawLine( "popMove", one.px + i * lx, one.py + i * ly, one.px + (i + 0.5) * lx, one.py + (i + 0.5) * ly, "#E1E1E1", 6 ); if (i == 0) core.strokeCircle("popMove", one.px, one.py, 6, "#E1E1E1", 3); } core.strokeCircle("popMove", one.px2, one.py2, 6, "#E1E1E1", 3); core.strokeCircle( "popMove", one.px2, one.py2, 6 + (16 * one.frame) / 30, "#E1E1E1", 6 * (1 - one.frame / 30) ); //core.drawLine('popMove', one.px, one.py, one.px2, one.py2, '#E1E1E1', 6); if (one.frame >= 30) count++; }); if (count > 0) list.splice(0, count); } } if (!main.replayChecking) { core.registerAnimationFrame("popMove", true, popMove); } this.addPopMove = function (px, py, px2, py2, frame) { var data = { px: px, py: py, px2: px2, py2: py2, frame: frame || 0 }; if (core.status.replay.speed <= 3) { if (!core.status.popMove) core.status.popMove = [data]; else core.status.popMove.push(data); } }; }, "帧动画": function () { const animate2 = document.createElement("canvas"); //画布设置 animate2.style.zIndex = 71; animate2.id = "animate2"; animate2.classList.add("gameCanvas", "anti-aliasing"); animate2.style.display = "block"; animate2.width = 416; animate2.height = 416; animate2.style.width = core.__PIXELS__ * core.domStyle.scale + "px"; animate2.style.height = core.__PIXELS__ * core.domStyle.scale + "px"; main.dom.animate2 = animate2; const anctx = animate2.getContext("2d"); main.dom.gameDraw.appendChild(animate2); core.plugin.playing = new Set(); const { Transition, linear, bezier, circle, hyper, trigo, power, inverseTrigo, shake, sleep } = core.plugin.animate; const tran = new Transition(); this.animatemove = function (id, px, py, relative, time, style) { if (!id) return core.plugin.playing.forEach(v => { if (v.id === id) { if (v.hero) return if (!style) tran.mode(linear()) else if (style === "in") { tran.mode(trigo('sin', "in")) } else if (style === "out") { tran.mode(trigo('sin', "out")) } else if (style === "in-out") { tran.mode(trigo('sin', "in-out")) } else if (style === "center") { tran.mode(trigo('sin', "center")) } if (relative) { tran.relative() } else { tran.absolute() } tran.time(time) tran.value[v.id + v.name + "x"] = px tran.value[v.id + v.name + "y"] = py } }) } this.setanimate = function ( name, px, py, width, height, allFarme, imageList, soundList ) { const data = { px: px, py: py, width: width, height: height, allFarme: allFarme, imageList: imageList, soundList: soundList, }; core.setFlag("animate_" + name, data); }; this.deleteanimate = function (name) { core.setFlag("animate_" + name); }; let thistime = 0; this.playanimate = function (name, id, x, y, hero, scalex, scaley, loop, reverse, callback) { if (!flags["animate_" + name]) { callback?.() } if (!id) id = setTimeout(null) tran.mode(linear()).time(1).absolute() if (!hero) tran.value[id + name + "x"] = x if (!hero) tran.value[id + name + "y"] = y const data = { id: id, name: name, x: x, y: y, hero: hero, scalex: scalex, scaley: scaley, pause: false, loop: loop, reverse: reverse, farme: reverse ? flags["animate_" + name].allFarme : 0, callback: callback }; core.plugin.playing.add(data); return id }; this.animateloop = function (id, loop) { core.plugin.playing.forEach(v => { if (!id || v.id === id) v.loop = loop }) } this.animatereverse = function (id, reverse) { core.plugin.playing.forEach(v => { if (!id || v.id === id) { const data = flags["animate_" + v.name] v.reverse = reverse } }) } this.animatepause = function (id, pause) { core.plugin.playing.forEach(v => { if (!id || v.id === id) v.pause = pause }) } this.animateclear = function (id) { core.plugin.playing.forEach(v => { if (!id || v.id === id) core.plugin.playing.delete(v) }) } core.animateFrame.animate2Time = 0 core.registerAnimationFrame("animateonmap", true, function (timestamp) { if (timestamp - core.animateFrame.animate2Time < 50 || !core.plugin.playing || core.plugin.playing.length == 0) return; core.animateFrame.animate2Time = timestamp core.clearMap(anctx); core.plugin.playing.forEach((one) => { const data = flags["animate_" + one.name]; if (!data) { core.plugin.playing.delete(one); } else { if (!one.pause) { if (one.reverse) one.farme-- else one.farme++ if (one.reverse) one.farme = data.allFarme - one.farme if ((!one.reverse && one.farme > data.allFarme) || (one.reverse && one.farme < 0)) { if (one.loop) { if (one.reverse) { one.farme = data.allFarme } else { one.start = thistime } } } if ((!one.reverse && one.farme > data.allFarme) || (one.reverse && one.farme < 0)) { one.callback?.() delete tran.value[one.id + one.name + "x"] delete tran.value[one.id + one.name + "y"] core.plugin.playing.delete(one) return } } data.imageList.forEach(function (image) { if ( one.farme >= (image.beforefarme ?? 0) && one.farme <= (image.afterfarme ?? data.allFarme) ) { const img = core.material.images.images?.[image.image]; if (img) { const gla = image.globalAlpha ?? 100; const agla = image.aglobalAlpha ?? gla, beforefarme = image.beforefarme ?? 0; const afterfarme = image.afterfarme ?? data.allFarme; anctx.globalAlpha = (gla + ((agla - gla) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1)) / 100; const cx = (image.cx ?? 0) + (((image.acx ?? 0) - (image.cx ?? 0)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), cy = (image.cy ?? 0) + (((image.acy ?? 0) - (image.cy ?? 0)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), cw = (image.cw ?? img.width) + (((image.acw ?? img.width) - (image.cw ?? img.width)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), ch = (image.ch ?? img.height) + (((image.acw ?? img.height) - (image.cw ?? img.height)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), x = (image.x ?? 0) + (((image.ax ?? 0) - (image.x ?? 0)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), y = (image.y ?? 0) + (((image.ay ?? 0) - (image.y ?? 0)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), w = (image.w ?? img.width) + (((image.aw ?? img.width) - (image.w ?? img.width)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), h = (image.h ?? img.height) + (((image.aw ?? img.height) - (image.w ?? img.height)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1), angle = (Math.PI * ((image.angle ?? 0) + (((image.aangle ?? 0) - (image.angle ?? 0)) * (one.farme - beforefarme)) / (afterfarme - beforefarme || 1))) / 180; if (one.hero) { let sx, sy; if (core.status.heroMoving < 0) { sx = 0; sy = 0; } else { sx = core.utils.scan[core.status.hero.loc.direction].x * 4 * core.status.heroMoving; sy = core.utils.scan[core.status.hero.loc.direction].y * 4 * core.status.heroMoving; } const herox = core.status.hero.loc.x * 32 + 16 + sx; const heroy = core.status.hero.loc.y * 32 + 16 + sy; core.drawImage( anctx, img, cx, cy, cw, ch, herox + (x - data.px) * one.scalex, heroy + (y - data.py) * one.scaley, w * one.scalex, h * one.scaley, angle ); } else { core.drawImage( anctx, img, cx, cy, cw, ch, tran.value[one.id + one.name + "x"] + (x - data.px) * one.scalex, tran.value[one.id + one.name + "y"] + (y - data.py) * one.scaley, w * one.scalex, h * one.scaley, angle ); } } } if (!one.pause) { if (one.reverse) one.farme-- else one.farme++ } }); data.soundList.forEach(function (sound) { const lisen = sound.sound && core.material.sounds[sound.sound] && core.musicStatus.soundStatus; if (one.farme == sound.startfarme && lisen) { if (sound.stopbefore) core.stopSound(); core.playSound(sound.sound); } }); } }); }); }, "移动事件": function () { maps.prototype.moveBlock = function (x, y, steps, time, keep, callback) { if (core.status.replay.speed == 24) time = 1; time = time || 500; var blockArr = this._getAndRemoveBlock(x, y); if (blockArr == null) { if (callback) callback(); return; } var block = blockArr[0], blockInfo = blockArr[1]; // 支持 wait 指令 var moveSteps = (steps || []).map(function (t) { var parts = t.split(':'); return [parts[0], parseInt(parts[1] || "0")]; }).filter(function (t) { var valid = ['up', 'down', 'left', 'right', 'forward', 'backward', 'leftup', 'leftdown', 'rightup', 'rightdown', 'speed', 'wait' ]; return valid.includes(t[0]) && !(t[0] === 'speed' && t[1] < 16) && !(t[0] === 'wait' && t[1] <= 0); }); var canvases = this._initDetachedBlock(blockInfo, x, y, block.event.animate !== false); this._moveDetachedBlock(blockInfo, 32 * x, 32 * y, 1, canvases); var moveInfo = { sx: x, sy: y, x: x, y: y, px: 32 * x, py: 32 * y, opacity: 1, keep: keep, lastDirection: null, offset: 1, moveSteps: moveSteps, step: 0, per_time: time / 16 / (core.status.replay.speed || 1), isWaiting: false, // 等待状态 waitStart: 0 // 等待开始时间戳 }; this._moveBlock_doMove(blockInfo, canvases, moveInfo, callback); } maps.prototype._moveBlock_doMove = function (blockInfo, canvases, moveInfo, callback) { var animateTotal = blockInfo.animate, animateTime = 0; if (!blockInfo.doorInfo && !blockInfo.bigImage && blockInfo.cls == 'npc48') animateTotal = 4; var _run = function () { var cb = function () { core.maps._deleteDetachedBlock(canvases); if (moveInfo.keep) { core.setBlock(blockInfo.number, moveInfo.x, moveInfo.y); core.showBlock(moveInfo.x, moveInfo.y); core.moveEnemyOnPoint(moveInfo.sx, moveInfo.sy, moveInfo.x, moveInfo.y); } if (callback) callback(); }; var animate = window.setInterval(function () { // 关键修改点:无论是否等待都更新动画帧 if (blockInfo.cls != 'tileset') { animateTime += moveInfo.per_time; if (animateTime > core.values.animateSpeed) { animateTime = 0; blockInfo.posX = (blockInfo.posX + 1) % animateTotal; } } // 处理等待状态 if (moveInfo.isWaiting) { if (Date.now() - moveInfo.waitStart >= moveInfo.currentWaitTime) { moveInfo.isWaiting = false; } else { // 保持渲染当前位置(动画持续) core.maps._moveDetachedBlock(blockInfo, moveInfo.px, moveInfo.py, moveInfo.opacity, canvases); return; } } if (moveInfo.moveSteps.length > 0) { // 处理 wait 指令 if (moveInfo.moveSteps[0][0] === 'wait') { moveInfo.isWaiting = true; moveInfo.waitStart = Date.now(); moveInfo.currentWaitTime = moveInfo.moveSteps[0][1]; moveInfo.moveSteps.shift(); return; } if (core.maps._moveBlock_updateSpeed(moveInfo)) { clearInterval(animate); delete core.animateFrame.asyncId[animate]; _run(); } else { core.maps._moveBlock_moving(blockInfo, canvases, moveInfo); } } else { core.maps._moveJumpBlock_finished(blockInfo, canvases, moveInfo, animate, cb); } }, moveInfo.per_time); core.animateFrame.lastAsyncId = animate; core.animateFrame.asyncId[animate] = cb; }; _run(); }; maps.prototype._moveBlock_moving = function (blockInfo, canvases, moveInfo) { if (moveInfo.isWaiting) return; // 等待时只更新动画,不移动 if (moveInfo.step == 0) { if (!this._moveBlock_updateDirection(blockInfo, moveInfo)) return; } var curr = moveInfo.moveSteps[0]; moveInfo.step++; moveInfo.px += core.utils.scan2[curr[0]].x * 2 * moveInfo.offset; moveInfo.py += core.utils.scan2[curr[0]].y * 2 * moveInfo.offset; this._moveDetachedBlock(blockInfo, moveInfo.px, moveInfo.py, moveInfo.opacity, canvases); if (moveInfo.step == 16) { moveInfo.step = 0; moveInfo.moveSteps[0][1]--; if (moveInfo.moveSteps[0][1] <= 0) { moveInfo.moveSteps.shift(); } } }; }, "动画": function () { // 在此增加新插件 const { Transition, linear, bezier, circle, hyper, trigo, power, inverseTrigo, shake, sleep } = core.plugin.animate; //////移动动画////// const tran = new Transition(); maps.prototype.moveAnimate = function (id, px, py, relative, time, style) { if (!id) return core.status.animateObjs.forEach(v => { if (v.id === id) { if (v.hero) return if (!style) tran.mode(linear()) else if (style === "in") { tran.mode(trigo('sin', "in")) } else if (style === "out") { tran.mode(trigo('sin', "out")) } else if (style === "in-out") { tran.mode(trigo('sin', "in-out")) } else if (style === "center") { tran.mode(trigo('sin', "center")) } if (relative) { tran.relative() } else { tran.absolute() } tran.time(time) tran.value[v.id + v.name + "x"] = px tran.value[v.id + v.name + "y"] = py } }) } control.prototype._animationFrame_animate = function (timestamp) { if (timestamp - core.animateFrame.animateTime < 50 || !core.status.animateObjs || core.status.animateObjs.length == 0) return; core.animateFrame.animateTime = timestamp core.clearMap('animate'); // 更新帧 for (var i = 0; i < core.status.animateObjs.length; i++) { var obj = core.status.animateObjs[i]; if (!obj.pause) { if ((!obj.reverse && obj.index >= obj.animate.frames.length) || (obj.reverse && obj.index <= 0)) { if (obj.loop) { if (obj.reverse) { obj.index = obj.animate.frames.length } else { obj.index = 0 } } else (function (callback) { setTimeout(function () { if (callback) callback(); }); })(obj.callback); } } } core.status.animateObjs = core.status.animateObjs.filter(function (obj) { return (!obj.reverse && obj.index < obj.animate.frames.length) || (obj.reverse && obj.index > 0); }); let sx, sy; if (core.status.heroMoving < 0) { sx = 0; sy = 0; } else { sx = core.utils.scan[core.status.hero.loc.direction].x * 4 * core.status.heroMoving; sy = core.utils.scan[core.status.hero.loc.direction].y * 4 * core.status.heroMoving; } const herox = core.status.hero.loc.x * 32 + 16 + sx; const heroy = core.status.hero.loc.y * 32 + 32 + sy - core.material.icons.hero.height / 2; core.status.animateObjs.forEach(function (obj) { if (obj.hero) { core.maps._drawAnimateFrame('animate', obj.animate, herox, heroy, obj.index); } else { core.maps._drawAnimateFrame('animate', obj.animate, tran.value[obj.id + obj.name + "x"], tran.value[obj.id + obj.name + "y"], obj.index); } if (!obj.pause) { if (obj.reverse) obj.index-- else obj.index++ } }); } core.registerAnimationFrame("animate", true, core.control._animationFrame_animate); ////// 绘制动画 ////// maps.prototype.drawAnimate = function (name, x, y, alignWindow, callback) { name = core.getMappedName(name); // 正在播放录像:不显示动画 if (core.isReplaying() || !core.material.animates[name] || x == null || y == null) { if (callback) callback(); return -1; } // 开始绘制 var animate = core.material.animates[name], centerX = 32 * x + 16, centerY = 32 * y + 16; if (alignWindow) { centerX += core.bigmap.offsetX; centerY += core.bigmap.offsetY; } var id = setTimeout(null); animate.se = animate.se || {}; if (typeof animate.se == 'string') animate.se = { 1: animate.se }; tran.mode(linear()).time(1).absolute() tran.value[id + name + "x"] = centerX tran.value[id + name + "y"] = centerY core.status.animateObjs.push({ "name": name, "id": id, "animate": animate, "centerX": centerX, "centerY": centerY, "pause": false, "index": 0, "callback": callback }); return id; } ////// 绘制反转动画 ////// maps.prototype.drawResizeAnimate = function (name, id, centerX, centerY, hero, reverse, loop, callback) { name = core.getMappedName(name); // 正在播放录像:不显示动画 if (core.isReplaying() || !core.material.animates[name]) { if (callback) callback(); return -1; } // 开始绘制 var animate = core.material.animates[name], centerX = core.calValue(centerX) centerY = core.calValue(centerY) animate.se = animate.se || {}; if (typeof animate.se == 'string') animate.se = { 1: animate.se }; var id = id || setTimeout(null); tran.mode(linear()).time(1).absolute() if (!hero) tran.value[id + name + "x"] = centerX if (!hero) tran.value[id + name + "y"] = centerY if (hero) core.status.animateObjs.push({ "name": name, "id": id, "animate": animate, "hero": true, "reverse": reverse, "pause": false, "loop": loop, "index": reverse ? animate.frames.length : 0, "callback": callback }) else core.status.animateObjs.push({ "name": name, "id": id, "animate": animate, "centerX": centerX, "centerY": centerY, "reverse": reverse, "pause": false, "loop": loop, "index": reverse ? animate.frames.length : 0, "callback": callback }); return id; } events.prototype._action_animateResize = function (data, x, y, prefix) { core.events.__action_doAsyncFunc(data.async, core.maps.drawResizeAnimate, data.name, data.id, data.centerX, data.centerY, data.hero, data.reverse, data.loop); }; ////// 绘制一个跟随勇士的动画 ////// maps.prototype.drawHeroAnimate = function (name, callback) { name = core.getMappedName(name); // 正在播放录像或动画不存在:不显示动画 if (core.isReplaying() || !core.material.animates[name]) { if (callback) callback(); return -1; } // 开始绘制 var animate = core.material.animates[name]; animate.se = animate.se || {}; if (typeof animate.se == 'string') animate.se = { 1: animate.se }; var id = setTimeout(null); core.status.animateObjs.push({ "name": name, "id": id, "animate": animate, "hero": true, "start": 0, "pause": false, "pausetime": 0, "index": 0, "callback": callback }); return id; } ////// 获得当前正在播放的所有(指定)动画的id列表 ////// maps.prototype.getPlayingAnimates = function (name) { return (core.status.animateObjs || []).filter(function (one) { return name == null || one.name == name; }).map(function (one) { return one.id }); } ////// 绘制动画的某一帧 ////// maps.prototype._drawAnimateFrame = function (name, animate, centerX, centerY, index) { var ctx = core.getContextByName(name); if (!ctx) return; var frame = animate.frames[index % animate.frame]; core.playSound((animate.se || {})[index % animate.frame + 1], (animate.pitch || {})[index % animate.frame + 1]); var ratio = animate.ratio; frame.forEach(function (t) { var image = animate.images[t.index]; if (!image) return; var realWidth = image.width * ratio * t.zoom / 100; var realHeight = image.height * ratio * t.zoom / 100; core.setAlpha(ctx, t.opacity / 255); var cx = centerX + t.x, cy = centerY + t.y; var ix = cx - realWidth / 2 - core.bigmap.offsetX, iy = cy - realHeight / 2 - core.bigmap.offsetY; var mirror = t.mirror ? 'x' : null; var angle = t.angle ? -t.angle * Math.PI / 180 : null; core.drawImage(ctx, image, ix, iy, realWidth, realHeight, null, null, null, null, angle, mirror); core.setAlpha(ctx, 1); }) } ////// 暂停动画 ////// maps.prototype.pauseAnimate = function (id) { for (var i = 0; i < core.status.animateObjs.length; i++) { var obj = core.status.animateObjs[i]; if (id == null || obj.id == id) { obj.pause = true } } } ////// 继续动画 ////// maps.prototype.remuseAnimate = function (id) { for (var i = 0; i < core.status.animateObjs.length; i++) { var obj = core.status.animateObjs[i]; if (id == null || obj.id == id) { obj.pause = false } } } ////// 停止动画 ////// maps.prototype.stopAnimate = function (id, doCallback) { for (var i = 0; i < core.status.animateObjs.length; i++) { var obj = core.status.animateObjs[i]; if (id == null || obj.id == id) { if (doCallback) { (function (callback) { setTimeout(function () { if (callback) callback(); }); })(obj.callback); } } } core.status.animateObjs = core.status.animateObjs.filter(function (x) { return id != null && x.id != id }); if (core.status.animateObjs.length == 0) core.clearMap('animate'); } }, "Battletokenroll": function () { // 专门用来调整在商店加点时触发效果的封装 this.shopbattletoken = function () { if (core.hasItem("PiggyBank")) hero.money += flags.star.level * flags.ratio if (core.hasItem("VitaminPills")) { flags.health.count += 10 } } // 筹码列表 this.chip = [ "BoxingGlovesB", "BoxingGlovesG", "BoxingGlovesE", "MotorcycleHelmetB", "MotorcycleHelmetG", "MotorcycleHelmetE", "SandwichCookieR", "SandwichCookieT", "SandwichCookieD", "CursedSword", "AdrenalineR", "AdrenalineH", "RevengeHalberd", "PremiumSwordShield", "Trident", "RollerSkates", "PiercingGun", "BankcardL", "BankcardH", "BankcardU", "PiggyBank", "FriendshipBadge", "Dice8", "FlashlightB", "FlashlightS", "StarCoinHammer", "MedicalKitE", "MedicalKitC", "GourmetStew", "Candy", "VitaminPills", "UtilityKnifeB", "UtilityKnifeS", "BufferShieldR", "BufferShieldP", "SprayCanR", "SprayCanL", "SprayCanN", "TargetBoard", "MagicQuiver", "NinjaThrowingDarts", "NormalSight", "EagleEyeSight", "HandheldFanS", "HandheldFanL", "Smartwatch", "AncientStaff", "FoxMask", "MembershipLetterB", "MembershipLetterV", "ExtraBatteryR", "ExtraBatteryC", "Magnifier", "DreamlinerModel", "TeaCake", "Backpack", "MagicTome", "ATM" ] //依据各个筹码的稀有度单独存放列表 this.bluetoken = ["BoxingGlovesB", "MotorcycleHelmetB", "BankcardL", "PiggyBank", "MedicalKitE", "FriendshipBadge", "Dice8", "SandwichCookieR", "TargetBoard", "SprayCanR", "HandheldFanS", "Magnifier" ] this.purpletoken = ["BoxingGlovesG", "MotorcycleHelmetG", "BankcardH", "AdrenalineR", "MembershipLetterB", "UtilityKnifeB", "VitaminPills", "CursedSword", "SandwichCookieT", "Backpack", "FlashlightS", "BufferShieldR", "SprayCanL", "MagicTome", "ExtraBatteryR", "TeaCake", "Candy", "HandheldFanL", "MagicQuiver", "Smartwatch", "NormalSight", "DreamlinerModel", "FoxMask", "GourmetStew" ] this.goldtoken = ["BoxingGlovesE", "MotorcycleHelmetE", "BankcardU", "BufferShieldP", "StarCoinHammer", "AdrenalineH", "MedicalKitC", "FlashlightB", "MembershipLetterV", "RollerSkates", "UtilityKnifeS", "SandwichCookieD", "EagleEyeSight", "PiercingGun", "SprayCanN", "ATM", "ExtraBatteryC", "NinjaThrowingDarts", "RevengeHalberd", "AncientStaff", "PremiumSwordShield", "Trident" ] //抽取筹码,并转发抽取结果到绘制界面 function tokenRandom() { const x = core.randtoken() const y = core.randtoken() const z = core.randtoken() const xyz = [x, y, z] let tokenselect = [] let haschip = [...core.getFlag("hasChip")] const 筹码概率平衡 = Math.min(8, flags.ratio) xyz.forEach(c => { let tokenselect1, b if (c >= 99000 - 筹码概率平衡 * 3000) { tokenselect1 = [...core.plugin.goldtoken].filter(v => !haschip.includes(v)) if (tokenselect1.length === 0) tokenselect1 = [...core.plugin.chip].filter(v => !haschip.includes(v)) b = (c + 1000 + 筹码概率平衡 * 3000) % tokenselect1.length } else if (c < 65000 - 筹码概率平衡 * 5000) { tokenselect1 = [...core.plugin.bluetoken].filter(v => !haschip.includes(v)) if (tokenselect1.length === 0) tokenselect1 = [...core.plugin.chip].filter(v => !haschip.includes(v)) b = (c + 100000) % tokenselect1.length } else { tokenselect1 = [...core.plugin.purpletoken].filter(v => !haschip.includes(v)) if (tokenselect1.length === 0) tokenselect1 = [...core.plugin.chip].filter(v => !haschip.includes(v)) b = (c + 34000 + 筹码概率平衡 * 2000) % tokenselect1.length } tokenselect1 = core.isset(b) ? tokenselect1[b] : "SandwichCookieR"; tokenselect.push(tokenselect1) haschip.push(tokenselect1) }) return tokenselect } // 选择筹码后关闭界面,并将选择的筹码从抽取池中移除 this.choose = function (tokenList, cho) { if (cho === 3) { if (!flags.rollchip) { throw "无意义的选项" return } flags.rollchip-- flags.tokenselect = tokenRandom() core.plugin.drawToken() core.status.route.push("token:" + cho) return } delete flags.tokenselect core.status.route.push("token:" + cho) core.setFlag("tokenList") core.unlockControl() core.status.event.id = "" const tokenctx = core.dymCanvas.token core.clearMap(tokenctx) core.closePanel() if (cho === 7) { if (!flags.lv6) { throw "非法选择" return } delete flags.lv6 if (tokenList.indexOf("ATM") > -1) { tokenList.splice(tokenList.indexOf("ATM"), 1) tokenList.push("ATM") } flags.hasChip = flags.hasChip.concat(tokenList) tokenList.forEach(v => core.getItem(v)) return } const itemId = tokenList[cho] flags.hasChip.push(itemId) core.getItem(itemId) } this.drawToken = function () { flags.tokenselect = tokenRandom() this.tokendraw() } //绘制筹码选择界面 this.tokendraw = function () { core.lockControl() if (!core.isReplaying() && !main.replayChecking) core.status.event.id = "token" let tokenctx = core.createCanvas("token", 0, 0, 416, 416, 140) tokenctx.imageSmoothingEnabled = false core.clearMap(tokenctx) const tokenList = flags.tokenselect core.fillRect(tokenctx, 0, 26, 416, 40, "#000000") core.setTextAlign(tokenctx, "center") core.fillText(tokenctx, "请选择一枚战斗筹码", 196, 53, "#FFFFFF", "bold 20px cjk") core.fillText(tokenctx, "筹码获取概率:", 358, 43, "#FFFFFF", "bold 14px cjk") core.fillText(tokenctx, (65 - Math.min(flags.ratio, 8) * 5) + '%', 320, 60, "#66CCFF", "bold 14px cjk") core.fillText(tokenctx, '/', 337, 60, "#FFFFFF", "bold 14px cjk") core.fillText(tokenctx, (34 + Math.min(flags.ratio, 8) * 2) + '%', 353, 60, "#CB00FF", "bold 14px cjk") core.fillText(tokenctx, '/', 370, 60, "#FFFFFF", "bold 14px cjk") core.fillText(tokenctx, (1 + Math.min(flags.ratio, 8) * 3) + '%', 387, 60, "gold", "bold 14px cjk") core.drawImage(tokenctx, tokenList[0] + ".webp", 14, 78, 120, 260) core.drawImage(tokenctx, tokenList[1] + ".webp", 148, 78, 120, 260) core.drawImage(tokenctx, tokenList[2] + ".webp", 282, 78, 120, 260) if (flags.rollchip) { core.fillRoundRect(tokenctx, 282, 348, 110, 33, 7, "#FFC90F") core.fillText(tokenctx, "刷 新", 322, 370, "#000000", "bold 16px cjk") core.fillText(tokenctx, `(${flags.rollchip})`, 363, 370, "#000000", "16px cjk") } if (flags.lv6) { core.fillRoundRect(tokenctx, 24, 348, 110, 33, 7, "#FFC90F") core.fillText(tokenctx, "手 快 全 拿", 78, 370, "#000000", "bold 16px cjk") } core.setFlag("tokenList", tokenList) } this.drawToken2 = function (Array) { //进阶教程筹码绘制 core.lockControl() if (!core.isReplaying() && !main.replayChecking) core.status.event.id = "token" let tokenctx = core.createCanvas("token", 0, 0, 416, 416, 140) tokenctx.imageSmoothingEnabled = false core.clearMap(tokenctx) const tokenList = Array core.fillRect(tokenctx, 0, 26, 416, 40, "#000000") core.setTextAlign(tokenctx, "center") core.fillText(tokenctx, "请选择一枚战斗筹码", 196, 53, "#FFFFFF", "bold 20px cjk") core.fillText(tokenctx, "筹码获取概率:", 358, 43, "#FFFFFF", "bold 14px cjk") core.fillText(tokenctx, (65 - Math.min(flags.ratio, 8) * 5) + '%', 320, 60, "#66CCFF", "bold 14px cjk") core.fillText(tokenctx, '/', 337, 60, "#FFFFFF", "bold 14px cjk") core.fillText(tokenctx, (34 + Math.min(flags.ratio, 8) * 2) + '%', 353, 60, "#CB00FF", "bold 14px cjk") core.fillText(tokenctx, '/', 370, 60, "#FFFFFF", "bold 14px cjk") core.fillText(tokenctx, (1 + Math.min(flags.ratio, 8) * 3) + '%', 387, 60, "gold", "bold 14px cjk") core.drawImage(tokenctx, tokenList[0] + ".webp", 14, 78, 120, 260) core.drawImage(tokenctx, tokenList[1] + ".webp", 148, 78, 120, 260) core.drawImage(tokenctx, tokenList[2] + ".webp", 282, 78, 120, 260) if (flags.rollchip) { core.fillRoundRect(tokenctx, 282, 348, 110, 33, 7, "#FFC90F") core.fillText(tokenctx, "刷 新", 322, 368, "#000000", "bold 16px cjk") core.fillText(tokenctx, `(${flags.rollchip})`, 363, 368, "#000000", "16px cjk") } core.setFlag("tokenList", tokenList) } this.clickToken = function (x, y, px, py) { const makeBox = ([x, y], [w, h]) => { return [ [x, y], [x + w, y + h], ]; }; const inRect = ([x, y], [ [sx, sy], [dx, dy] ]) => { return sx <= x && x <= dx && sy <= y && y <= dy; }; const pos = [px, py]; const token0 = makeBox([14, 78], [120, 260]) const token1 = makeBox([148, 78], [120, 260]) const token2 = makeBox([282, 78], [120, 260]) const roll = makeBox([282, 348], [110, 33]) const ALL = makeBox([24, 348], [110, 33]) const tokenList = core.getFlag("tokenList") if (inRect(pos, token0)) { core.plugin.choose(tokenList, 0) return } if (inRect(pos, token1)) { core.plugin.choose(tokenList, 1) return } if (inRect(pos, token2)) { core.plugin.choose(tokenList, 2) return } if (inRect(pos, roll) && flags.rollchip) { core.plugin.choose(tokenList, 3) return } if (inRect(pos, ALL) && flags.lv6) { core.plugin.choose(tokenList, 7) return } } //为选择筹码注册录像操作 function replayAction_token(action) { if (action.indexOf("token:") != 0) return false; const cho = Number(action.substring(6)); if (!(cho === 0 || cho === 1 || cho === 2 || cho === 3 || cho === 7)) { core.control._replay_error(action) return false } const tokenList = core.getFlag("tokenList") if (!tokenList[2]) { core.control._replay_error(action) return false } if (core.status.replay.speed == 24) { core.plugin.choose(tokenList, cho) core.replay() return true; } flags.replayToken = true setTimeout(function () { core.plugin.choose(tokenList, cho) delete flags.replayToken core.replay() }, core.control.__replay_getTimeout()); return true; } core.control.registerReplayAction("token", replayAction_token); /** * 绘制筹码图片 * @param {string} token */ this.drawTokenone = function drawToken_one(token) { const canvas1 = document.createElement("canvas"); const ctx1 = canvas1.getContext("2d"); canvas1.width = 120 * 2 canvas1.height = 260 * 2 core.clearMap(ctx1) const tokenColor1 = core.plugin.bluetoken.includes(token) ? "blue" : core.plugin.purpletoken.includes(token) ? "purple" : "gold" core.strokeRoundRect(ctx1, 4 * 2, 4 * 2, 112 * 2, 252 * 2, 20 * 2, "#FFFFFF", 3 * 2) core.fillRoundRect(ctx1, 5 * 2, 5 * 2, 110 * 2, 250 * 2, 20 * 2, "rgba(0,0,0,0.6)") let img = token if (img === "BoxingGlovesB" || img === "BoxingGlovesG" || img === "BoxingGlovesE") img = "BoxingGloves" if (img === "MotorcycleHelmetB" || img === "MotorcycleHelmetG" || img === "MotorcycleHelmetE") img = "MotorcycleHelmet" if (img === "BankcardL" || img === "BankcardH" || img === "BankcardU") img = "BankCard" if (img === "MembershipLetterB" || img === "MembershipLetterV") img = "MembershipLetter" if (img === "UtilityKnifeB" || img === "UtilityKnifeS") img = "UtilityKnife" if (img === "MedicalKitE" || img === "MedicalKitC") img = "MedicalKit" if (img === "AdrenalineR" || img === "AdrenalineH") img = "Adrenaline" if (img === "FlashlightB" || img === "FlashlightS") img = "Flashlight" if (img === "SandwichCookieR" || img === "SandwichCookieT" || img === "SandwichCookieD") img = "SandwichCookie" if (img === "SprayCanR" || img === "SprayCanL" || img === "SprayCanN") img = "SprayCan" if (img === "BufferShieldR" || img === "BufferShieldP") img = "BufferShield" if (img === "ExtraBatteryR" || img === "ExtraBatteryC") img = "ExtraBattery" if (img === "HandheldFanS" || img === "HandheldFanL") img = "HandheldFan" if (img === "NormalSight" || img === "EagleEyeSight") img = "EagleEyeSight" img += ".png" core.drawImage(ctx1, "Relicbg" + tokenColor1 + ".png", 20 * 2, 20 * 2, 80 * 2, 80 * 2) core.drawImage(ctx1, img, 25 * 2, 25 * 2, 70 * 2, 70 * 2) core.setTextAlign(ctx1, "center") core.fillBoldText(ctx1, core.material.items[token].name, 60 * 2, 120 * 2, "#FFFFFF", "#000000", 2, "Bold 28px cjk") let text = core.material.items[token].text core.drawTextContent(ctx1, text, { left: 5 * 2, top: 135 * 2, color: "#FFFFFF", fontSize: 24, align: "center", maxWidth: 220, font: "cjk" }) core.CanvastoWebp(token, canvas1) } }, "CharacterUI": function () { let EF0D1D45DB2D0552DCD963E8B744008B = false if (!main.replayChecking && !core.isReplaying()) { if (core.getCookie("id") === "61652" || core.getCookie("id") === "53410") EF0D1D45DB2D0552DCD963E8B744008B = true const LogBtnStyle = document.createElement('style'); LogBtnStyle.textContent = ` #LogBtn { position: absolute; top: 14%; width: 15%; height: 10%; background: white; border: 4px solid black; border-radius: 27.5px; color: black; font-size: ${parseFloat(core.dom.gameGroup.style.width)*0.04}px; font-weight: bold; cursor: pointer; z-index: 261; transition: all 0.3s ease; display: none; align-items: center; justify-content: center; box-shadow: 0 0 0 3px white, 0 2px 8px rgba(0, 0, 0, 0.2); outline: none; margin: 3px; } #LogBtn:hover { background: #f5f5f5; transform: translateY(-1px); box-shadow: 0 0 0 3px white, 0 4px 12px rgba(0, 0, 0, 0.3); } #LogBtn:active { transform: translateY(0); } #LogBtn.hidden { opacity: 0; transform: translateY(-10px); pointer-events: none; transition: all 0.5s ease; } #LogBtn:not(.hidden) { animation: buttonAppear 0.6s ease-out; } .isVertical #LogBtn { top: 12px; right: 12px; left: auto; width: 110px; height: 50px; font-size: 30px; border-radius: 25px; } /* 弹出窗口样式 */ .code-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 70%; max-width: 400px; background: white; border: 4px solid black; border-radius: 20px; padding: 20px; z-index: 300; box-shadow: 0 0 0 3px white, 0 4px 20px rgba(0, 0, 0, 0.3); display: none; flex-direction: column; align-items: center; } .code-modal-btn { width: 80%; height: 50px; margin: 10px 0; background: pink; border: 3px solid black; border-radius: 25px; color: black; font-size: 16px; font-weight: bold; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; } .code-modal-btn:hover { background: #ffb6c1; transform: translateY(-2px); } .code-modal-btn:active { transform: translateY(0); } .modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); z-index: 299; display: none; } .code-modal-btn { height: 40px !important; /* 固定高度 */ margin: 8px 0 !important; /* 调整间距 */ white-space: nowrap; /* 防止文字换行 */ overflow: hidden; /* 隐藏溢出内容 */ text-overflow: ellipsis; /* 文字过长显示省略号 */ } `; // 将样式添加到head document.head.appendChild(LogBtnStyle); // 创建主按钮 const LogBtn = document.createElement('button'); LogBtn.id = 'LogBtn'; LogBtn.innerHTML = '验证码'; core.dom.startPanel.appendChild(LogBtn); // 创建弹出窗口和遮罩层 const modalOverlay = document.createElement('div'); modalOverlay.className = 'modal-overlay'; document.body.appendChild(modalOverlay); const codeModal = document.createElement('div'); codeModal.className = 'code-modal'; document.body.appendChild(codeModal); const anchorBtn = document.createElement('button'); anchorBtn.className = 'code-modal-btn'; anchorBtn.textContent = '主播通道'; anchorBtn.style.height = '40px'; // 固定高度 anchorBtn.addEventListener('click', function () { checkLogBtn(); core.myprompt("请输入校验码", null, function (value) { if (value && typeof window[value] === 'function') { window[value]() } else if (value) { alert("校验码错误!") } }); closeModal(); }); const getExpBtn = document.createElement('button'); getExpBtn.className = 'code-modal-btn'; getExpBtn.textContent = '获取经验码'; getExpBtn.style.height = '40px'; // 固定高度 getExpBtn.addEventListener('click', async function () { showCopyableTextModal(await core.encrypt(), "经验码") closeModal(); }); const redeemExpBtn = document.createElement('button'); redeemExpBtn.className = 'code-modal-btn'; redeemExpBtn.textContent = '兑现经验码'; redeemExpBtn.style.height = '40px'; // 固定高度 redeemExpBtn.addEventListener('click', function () { core.myprompt("请输入经验码", null, async function (value) { if (value) { if (!await core.decrypt(value)) alert("经验码错误!") } }); closeModal(); }); // 将按钮添加到模态框 codeModal.appendChild(anchorBtn); codeModal.appendChild(getExpBtn); codeModal.appendChild(redeemExpBtn); // 打开模态框函数 function openModal() { codeModal.style.display = 'flex'; modalOverlay.style.display = 'block'; } // 关闭模态框函数 function closeModal() { codeModal.style.display = 'none'; modalOverlay.style.display = 'none'; } // 点击主按钮打开模态框 LogBtn.addEventListener('click', openModal); // 点击遮罩层关闭模态框 modalOverlay.addEventListener('click', closeModal); if (core.getCookie("id")) window[b64_md5(core.getCookie("id") + 'EF0D1D45DB2D0552')] = function () { EF0D1D45DB2D0552DCD963E8B744008B = true } function checkLogBtn() { let LogBtn = document.getElementById('LogBtn') LogBtn.style.fontSize = `${parseFloat(core.dom.gameGroup.style.width)*0.03}px` if (typeof core !== 'undefined' && core.domStyle && core.domStyle.isVertical !== undefined) { if (core.domStyle.isVertical) { document.body.classList.add('isVertical'); LogBtn.style.top = `10%`; LogBtn.style.left = `72%`; LogBtn.style.width = `20%`; LogBtn.style.height = `6%`; LogBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.03}px` LogBtn.style.border = `${4*core.domStyle.scale}px solid black` } else { document.body.classList.remove('isVertical'); LogBtn.style.top = `14%`; LogBtn.style.left = `83%`; LogBtn.style.width = `15%`; LogBtn.style.height = `10%`; LogBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.05}px` LogBtn.style.border = `${4*core.domStyle.scale}px solid black` } } } /** * 显示可复制文本的模态窗口 * @param {string} text - 要显示并可复制的文本内容 * @param {string} title - 窗口标题(可选) */ function showCopyableTextModal(text, title = "") { // 创建模态窗口容器 const copyModal = document.createElement('div'); copyModal.className = 'copy-modal'; copyModal.style.position = 'fixed'; copyModal.style.top = '50%'; copyModal.style.left = '50%'; copyModal.style.transform = 'translate(-50%, -50%)'; copyModal.style.width = '70%'; copyModal.style.maxWidth = '400px'; copyModal.style.background = 'white'; copyModal.style.border = '4px solid black'; copyModal.style.borderRadius = '20px'; copyModal.style.padding = '20px'; copyModal.style.zIndex = '301'; copyModal.style.boxShadow = '0 0 0 3px white, 0 4px 20px rgba(0, 0, 0, 0.3)'; copyModal.style.display = 'flex'; copyModal.style.flexDirection = 'column'; copyModal.style.alignItems = 'center'; // 如果有标题,添加标题 if (title) { const titleEl = document.createElement('h3'); titleEl.textContent = title; titleEl.style.margin = '0 0 15px 0'; titleEl.style.fontSize = '18px'; titleEl.style.color = 'black'; copyModal.appendChild(titleEl); } // 创建文本内容容器 const textContainer = document.createElement('div'); textContainer.style.width = '90%'; textContainer.style.height = '150px'; // 固定高度 textContainer.style.position = 'relative'; textContainer.style.margin = '10px 0'; // 创建文本内容区域(可选中、可滚动) const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.width = '100%'; textArea.style.height = '100%'; textArea.style.padding = '10px'; textArea.style.background = '#f5f5f5'; textArea.style.border = '2px solid #ddd'; textArea.style.borderRadius = '10px'; textArea.style.fontSize = '16px'; textArea.style.fontFamily = 'monospace'; textArea.style.resize = 'none'; // 禁止手动调整大小 textArea.style.overflow = 'auto'; // 自动显示滚动条 textArea.style.userSelect = 'text'; // 确保文本可选中 textArea.readOnly = true; // 只读模式 // 添加到容器 textContainer.appendChild(textArea); copyModal.appendChild(textContainer); // 创建复制按钮 const copyBtn = document.createElement('button'); copyBtn.textContent = '复制'; copyBtn.style.width = '80%'; copyBtn.style.height = '40px'; copyBtn.style.margin = '20px 0 10px 0'; copyBtn.style.background = 'pink'; copyBtn.style.border = '3px solid black'; copyBtn.style.borderRadius = '20px'; copyBtn.style.color = 'black'; copyBtn.style.fontSize = '16px'; copyBtn.style.fontWeight = 'bold'; copyBtn.style.cursor = 'pointer'; copyBtn.style.transition = 'all 0.2s ease'; // 复制按钮悬停效果 copyBtn.addEventListener('mouseover', () => { copyBtn.style.background = '#ffb6c1'; copyBtn.style.transform = 'translateY(-2px)'; }); copyBtn.addEventListener('mouseout', () => { copyBtn.style.background = 'pink'; copyBtn.style.transform = 'translateY(0)'; }); // 复制功能 copyBtn.addEventListener('click', () => { textArea.select(); document.execCommand('copy'); // 显示复制成功提示 const originalText = copyBtn.textContent; copyBtn.textContent = '已复制!'; copyBtn.style.background = '#a0e6a0'; setTimeout(() => { copyBtn.textContent = originalText; copyBtn.style.background = 'pink'; }, 1500); }); copyModal.appendChild(copyBtn); // 创建关闭按钮 const closeBtn = document.createElement('button'); closeBtn.textContent = '关闭'; closeBtn.style.width = '80%'; closeBtn.style.height = '40px'; closeBtn.style.margin = '10px 0 0 0'; closeBtn.style.background = '#f5f5f5'; closeBtn.style.border = '3px solid black'; closeBtn.style.borderRadius = '20px'; closeBtn.style.color = 'black'; closeBtn.style.fontSize = '16px'; closeBtn.style.fontWeight = 'bold'; closeBtn.style.cursor = 'pointer'; closeBtn.style.transition = 'all 0.2s ease'; // 关闭按钮悬停效果 closeBtn.addEventListener('mouseover', () => { closeBtn.style.background = '#e0e0e0'; closeBtn.style.transform = 'translateY(-2px)'; }); closeBtn.addEventListener('mouseout', () => { closeBtn.style.background = '#f5f5f5'; closeBtn.style.transform = 'translateY(0)'; }); // 关闭功能 closeBtn.addEventListener('click', () => { document.body.removeChild(copyModal); document.body.removeChild(overlay); }); copyModal.appendChild(closeBtn); // 创建遮罩层 const overlay = document.createElement('div'); overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.right = '0'; overlay.style.bottom = '0'; overlay.style.background = 'rgba(0, 0, 0, 0.5)'; overlay.style.zIndex = '300'; // 点击遮罩层关闭 overlay.addEventListener('click', () => { document.body.removeChild(copyModal); document.body.removeChild(overlay); }); // 添加到DOM document.body.appendChild(overlay); document.body.appendChild(copyModal); // 自动聚焦并选中所有文本 textArea.focus(); textArea.select(); } } this.D290605F2869068624508C972B6C6AE0 = function D290605F2869068624508C972B6C6AE0(obj, obj2) { if (EF0D1D45DB2D0552DCD963E8B744008B) { const b = {} Object.keys(this.Character).forEach(v => b[v] = { a: 6, b: 0 }) flags.EF0D1D45DB2D0552DCD963E8B744008B = true return b } const a = JSON.parse(core.decodeBase64(obj)) const b = {} Object.keys(this.Character).forEach(v => b[v] = { a: 0, b: 0 }) const c = JSON.parse(core.decodeBase64(obj2)) for (let item in b) { let B0AB0254BD58EB87EAEE3172BA49FEFB = 0 if (a[item]) { for (let v in a[item]) { B0AB0254BD58EB87EAEE3172BA49FEFB += a[item][v] } } if (c.includes("4A258155F46ADB5D4599713358316EE1")) B0AB0254BD58EB87EAEE3172BA49FEFB += 1000 //不是哥们?! if (c.includes("1541C821FF1B39191BF73A8D9BF05805")) B0AB0254BD58EB87EAEE3172BA49FEFB += 1000 //星引擎缔造者 if (c.includes("B41A02E0553FF9EF3942ABCAF60D624D")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //快!再快一点 if (c.includes("A2756198031328A012B30245D493761E")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //筹码,不需要了! if (c.includes("1FC895A2B243EFF1F693E6D926C4CCDC")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //苍鲤 if (c.includes("4BABDF2950A811AF8B27C75D1AA5D168")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //真梦梓 if (c.includes("4E65144DF41B89C8DC10426C2545D20F")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //只有牌佬知道的世界 if (c.includes("F049C350DC05D59CE41DB5FBD8E3B4B9")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //手札抹❌ if (c.includes("0E26F846C3824D5A1AF7202B65A54559")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //你疯了还是我疯了? if (c.includes("AE9E97AE902688891090D1273C16357B")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //好茶,爱喝 if (c.includes("6CE1F3E982BB0E37CB5660DA7C033B98")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //艺术就是派大星 if (c.includes("E55FE7C62ABB0CD0CC29E433CAFFD39B")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //海盗王 if (c.includes("3A7E94AE93F0947DDC8142FC8797AFFC")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //舞狮 if (c.includes("516607A2E777E09E9954E0A14FC40A27")) B0AB0254BD58EB87EAEE3172BA49FEFB += 1000 //学院新星 if (c.includes("5CFCFF9401B7B06C3CF971D3DD1647C0")) B0AB0254BD58EB87EAEE3172BA49FEFB += 500 //雅央 if (B0AB0254BD58EB87EAEE3172BA49FEFB >= 43500) b[item].a = 6 else if (B0AB0254BD58EB87EAEE3172BA49FEFB >= 13500) { b[item].a = 5 b[item].b = B0AB0254BD58EB87EAEE3172BA49FEFB - 13500 } else if (B0AB0254BD58EB87EAEE3172BA49FEFB >= 7500) { b[item].a = 4 b[item].b = B0AB0254BD58EB87EAEE3172BA49FEFB - 7500 } else if (B0AB0254BD58EB87EAEE3172BA49FEFB >= 4500) { b[item].a = 3 b[item].b = B0AB0254BD58EB87EAEE3172BA49FEFB - 4500 } else if (B0AB0254BD58EB87EAEE3172BA49FEFB >= 2500) { b[item].a = 2 b[item].b = B0AB0254BD58EB87EAEE3172BA49FEFB - 2500 } else if (B0AB0254BD58EB87EAEE3172BA49FEFB >= 1000) { b[item].a = 1 b[item].b = B0AB0254BD58EB87EAEE3172BA49FEFB - 1000 } else { b[item].a = 0 b[item].b = B0AB0254BD58EB87EAEE3172BA49FEFB } } return b } // 角色技能发动后其他效果处理 this.afterSkill = function () { if (core.hasItem("HandheldFanS")) hero.cost = Math.min(hero.costmax, hero.cost + 3) if (core.hasItem("HandheldFanL")) { core.drawFolds([core.plugin.card.cardList[core.randcard()], core.plugin.card.cardList[core.randcard()], core.plugin.card.cardList[core.randcard()]]) hero.cost = hero.costmax } if (core.hasItem("Trident")) flags.Trident++ core.closePanel() core.updateStatusBar() } // 在此增加新插件 this.Character = {} const Character = core.plugin.Character Character.勇者 = { name: "勇者", //角色名 title: "异世英雄", //角色头衔 file: "一位不愿意透露姓名并来自异世界的勇者,真实身份与踏上旅途的原因则完全是一个谜团。据他所说他曾经与异世界的魔王芝诺大战44个轮回,随后因为智慧权杖导致时空乱流,意外穿越至此。\n在米米的帮助下,勇者现已在联合城定居", //角色档案 Main: "yongshi.png", //角色头像 startsound: "", //选中语音 startText: "(该角色没有语音)", //语音翻译 Avatar: "Hero_Full.png", //立绘 HeroIcon: "hero.png", //行走图 Sprite: "Hero_Sprite.png", //纸片人 hp: 1800, //生命值 atk: 20, //攻击力 def: 15, //防御力 PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+10`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+10 \\i[card]+1`, `\\i[hp]+800 \\i[atk]+25 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+25 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+25 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 10; case 3: flags.lv3 = true; hero.atk += 10; case 2: hero.atk += 5; hero.def += 10; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: "CD30,无效果", //主动技能描述 ActiveSkillCD: 30, //主动技能冷却,战后-1 Active: function () { core.lockControl() hero.ActiveCD = hero.ActiveSkillCD core.drawTip("你都没有技能了还按呢") core.afterSkill() }, //主动技能效果 PassiveSkill: "没有被动技能捏", //被动技能描述 Passive: function () {}, //被动技能效果 PassiveTime: [] //触发时机 } Character.米米 = { name: "米米", title: "看板娘", file: "米米\n生日:8月2日\n喜欢的食物:精致的快餐便当\n游戏《星引擎派对》的全能看板娘,超级打工战士。便利店、酒吧、超市都有米米打工的身影,恋第一次见到米米就是在便利店。至于米米为什么会成为《星引擎派对》游戏的看板娘就不得而知了。\n米米神出鬼没,可能会出现在各种打工场合,除了某些直播工作的时候形象没有任何变化。", Main: "Mimi.webp", startsound: "Bond_Mimi.mp3", startText: "您好,我是米米。虽然我算是在做看板娘,但其实也只是打工而已~", Avatar: "Mimi_Full.png", HeroIcon: "Mimi_HeroIcon.png", Sprite: "Mimi_Sprite.png", hp: 1400, atk: 15, def: 15, PVELevelsText: ["不应用等级加成", `\\i[money]+300`, `\\i[hp]+600 \\i[money]+300`, `\\i[hp]+600 \\i[atk]+5 \\i[money]+300 \\i[card]+1`, `\\i[hp]+600 \\i[atk]+5 \\i[money]+300 \\i[card]+1 \\i[cost]上限+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+600 \\i[atk]+5 \\i[money]+300 \\i[card]+1 \\i[cost]上限+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+600 \\i[atk]+5 \\i[money]+300 \\i[card]+1 \\i[cost]上限+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.cost++; hero.costmax++; case 3: flags.lv3 = true; hero.atk += 5; case 2: hero.hp += 600; case 1: hero.money += 300; } }, ActiveSkill: `CD40,丢弃全部手牌并抽取相同数量的牌,\\i[cost]+丢弃手牌数量`, ActiveSkillCD: 40, Active: function () { core.lockControl() const a = hero.myCard.length hero.myCard = [] for (let i = 0; i < a; i++) { core.drawFolds(core.plugin.card.cardList[core.randcard()]) } if (hero.cost + a >= hero.costmax) hero.cost = hero.costmax else hero.cost += a hero.ActiveCD = hero.ActiveSkillCD core.drawTip("这样如何") core.playSound("Mimi.mp3") core.afterSkill() }, PassiveSkill: `消耗最后的\\i[cost]时+1,每触发4次此效果,获得一次星光奖励的\\i[money]`, Passive: function () { if (hero.cost === 0) { hero.cost++ flags.Mimi = (flags.Mimi ?? 0) + 1 if (flags.Mimi === 4) { flags.Mimi = 0 const StarlightPrice = flags.star.level * flags.ratio hero.money += StarlightPrice; hero.statistics.money += StarlightPrice; core.drawTip("被动技能生效,星币+" + StarlightPrice); } } }, PassiveTime: ["cardAfter"] } Character.帕露南 = { name: "帕露南", title: "商业之主", file: "帕露南·克里西亚\n生日:6月14日\n喜欢的食物:看起来贼贵的\n联合城的商业传奇,帝国集团的所有者。克里西亚家族是久负盛名的传统家族财团,帕露南让这个帝国更加繁荣,使联合城的各种产业都遍布了克利西亚家族的身影。\n帕露南很少出现在公共场合,只有很多年前报纸留下的采访照片。", Main: "Parunan.webp", startsound: "Bond_Parunan.mp3", startText: "我就是商业达人帕露南~钱就是力量呀~", Avatar: "Parunan_Full.png", HeroIcon: "Parunan_HeroIcon.png", Sprite: "Parunan_Sprite.png", hp: 1600, atk: 12, def: 12, PVELevelsText: ["不应用等级加成", `\\i[money]+300`, `\\i[hp]+600 \\i[money]+300`, `\\i[hp]+600 \\i[money]+300 \\i[card]+1 \\i[BuffStarlight]+5`, `\\i[hp]+600 \\i[atk]+10 \\i[def]+10 \\i[money]+300 \\i[card]+1 \\i[BuffStarlight]+5 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+600 \\i[atk]+10 \\i[def]+10 \\i[money]+300 \\i[card]+1 \\i[BuffStarlight]+5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+600 \\i[atk]+10 \\i[def]+10 \\i[money]+300 \\i[card]+1 \\i[BuffStarlight]+5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 10; case 3: flags.lv3 = true; flags.star.level += 5 case 2: hero.hp += 600; case 1: hero.money += 300; } }, ActiveSkill: `限定1次,当前\\i[money]+30%`, ActiveSkillCD: Infinity, Active: function () { core.lockControl() flags.次数 = 1 flags.筹码购买次数 = 0 hero.money = Math.floor(hero.money * 13 / 10) hero.ActiveCD = hero.ActiveSkillCD core.drawTip("传说中的商人,归来!") core.playSound("Parunan.mp3") core.afterSkill() }, PassiveSkill: `激活全部商店(事件卡商店除外)。初始\\i[stars]为3,无法购买\\i[stars]`, Passive: function () { core.setShopVisited("shop5", true) core.setShopVisited("battletokenshop", true) core.setShopVisited("Cardshop", true) flags.ratio = 3 flags.buyratio = 0 if (flags.MapStatus?.Map === "水乡古镇" || flags.MapStatus?.Map === "龙宫游乐园") core.setShopVisited("WTshop", true) else if (flags.MapStatus?.Map === "魔法学院") { flags.shopvisited = 4 core.setShopVisited("MAshop4", true) } else { flags.shopvisited = 4 core.setShopVisited("shop4", true) } }, PassiveTime: [] } Character.芬妮 = { name: "芬妮", title: "古怪神探", file: "芬妮・克雷格\n生日:2月17日\n喜欢的食物:草莓蛋糕\n格雷森侦探事务所是整个联合城数一数二的私人侦探事务所,芬妮则是事务所的核心。她办理过无数非常级棘手的案件,而这些案件无一例外都得到了完美的解决。不光如此,她还是一位资深的法学博士,拥有联合城颁发的双证。\n在“天王落”事件后失踪,录入的形象是很久之前的。", Main: "Fenny.webp", startsound: "Bond_Fenny.mp3", startText: "嗯?有事件?交给我吧!", Avatar: "Fenny_Full.png", HeroIcon: "Fenny_HeroIcon.png", Sprite: "Fenny_Sprite.png", hp: 1600, atk: 10, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+10`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+10 \\i[card]+1 技能冷却-10`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+10 \\i[card]+1 技能冷却-10 \\i[BuffStarlight]+4 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+10 \\i[card]+1 技能冷却-10 \\i[BuffStarlight]+4 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+10 \\i[card]+1 技能冷却-10 \\i[BuffStarlight]+4 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; flags.star.level += 4 case 3: flags.lv3 = true; hero.ActiveSkillCD -= 10; case 2: hero.atk += 10; hero.def += 10; case 1: hero.hp += 400; } }, ActiveSkill: "CD75,翻开一张事件卡", ActiveSkillCD: 75, Active: function () { core.lockControl() const rand = core.plugin.event.eventList[core.randevent()] core.drawTip("搜查,分析,解明!") core.playSound("Fenny.mp3") core.plugin.event.doEvent(rand, () => { hero.ActiveCD = hero.ActiveSkillCD core.afterSkill() }) }, PassiveSkill: `每当事件卡被翻开时,\\i[BuffStarlight]+1,每30~100场战斗后会翻开1张事件卡`, Passive: function () { flags.star.level++ }, PassiveTime: ["Event"] } Character.阿兰娜 = { name: "阿兰娜", title: "社恐修女", file: "阿兰娜·格瑞斯塔\n生日:2月21\n喜欢的食物:芋头\n联合城星辉院的修女长。星辉院是联合城政府资助具有孤儿院性质的机构,里面收养了星辉位面各地区的孤儿,阿兰娜也是孤儿的一员。\n“退魔之夜”事件后加入战斗修女团。她一直帮助维护修道院的日常运营,很少在外露面。", Main: "Alana.webp", startsound: "Bond_Alana.mp3", startText: "不要这样看着我……我会害羞的……", Avatar: "Alana_Full.png", HeroIcon: "Alana_HeroIcon.png", Sprite: "Alana_Sprite.png", hp: 1400, atk: 10, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5 \\i[card]+1 技能冷却-10`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+15 \\i[card]+1 技能冷却-10 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+15 \\i[card]+1 技能冷却-10 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+15 \\i[card]+1 技能冷却-10 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 10; hero.hp += 400; case 3: flags.lv3 = true; hero.ActiveSkillCD -= 10; case 2: hero.atk += 5; hero.def += 5; case 1: hero.hp += 400; } }, ActiveSkill: "CD50,隐身。期间可穿越怪物且不触发捕捉效果,离开当前楼层隐身结束", ActiveSkillCD: 50, Active: function () { core.lockControl() core.stealthOn() hero.ActiveCD = hero.ActiveSkillCD core.drawTip("看不见我看不见我") core.playSound("Alana.mp3") core.afterSkill() }, PassiveSkill: `战后若未受到伤害,\\i[def]+\\i[stars],若受到伤害则失去伤害值的\\i[def]\n通过此技能获得的\\i[def]最高为80倍\\i[stars],最低为0`, Passive: function () {}, PassiveTime: [] } Character.小町 = { name: "小町", title: "暗影忍者", file: "青山小町\n生日:1月8日\n喜欢的食物:米饭\n本苍院的研究员,致力于星辉古文献的研究。她也是密宗的忍者传人,脖子上会说话的围巾是密宗的魔道具“绞毙”。\n“退魔之夜”事件发生后本苍院被毁,她本人则被政府保护了起来,很少人能见到她。", Main: "Komachi.webp", startsound: "Bond_Komachi.mp3", startText: "你好,在下忍者小町", Avatar: "Komachi_Full.png", HeroIcon: "Komachi_HeroIcon.png", Sprite: "Komachi_Sprite.png", hp: 1400, atk: 10, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+10`, `\\i[hp]+400 \\i[atk]+20 \\i[def]+20 \\i[card]+1`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.hp += 400; case 3: flags.lv3 = true; hero.atk += 10; hero.def += 10; case 2: hero.atk += 5; hero.def += 5; case 1: hero.hp += 400; } }, ActiveSkill: "CD40,获得10点临时cost,临时cost在离开当前楼层后消失", ActiveSkillCD: 40, Active: function () { core.lockControl() flags.komachiskill = 10 hero.ActiveCD = hero.ActiveSkillCD core.drawTip("忍者可不会放跑猎物!") core.playSound("Komachi.mp3") core.afterSkill() }, PassiveSkill: `每使用3张手牌,\\i[card]\\i[cost]+1`, Passive: function () { flags.komachi += 1 if (flags.komachi >= 3) { flags.komachi = 0 core.drawFolds(core.plugin.card.cardList[core.randcard()]) if (hero.cost < hero.costmax) hero.cost++ } }, PassiveTime: ["cardAfter"] } Character.派德曼 = { name: "派德曼", title: "社员叔叔", file: "派德曼\n生日:5月20日(发现日)\n喜欢的食物:未知\n超自然生物,虽然不会对周围的人产生直接的肉体伤害,但是会不断蚕食周围人的精神,使他们陷入神经质,癫狂,自残的精神状态。表达感情和发声都是通过面部的平板电脑,但有传闻说那些声音只是在模仿某人说话。\n在“天王落”事件后被收容。", Main: "Padman.webp", startsound: "Bond_Padman.mp3", startText: "即使是水深火热的状况,也尽管吩咐给我去做吧", Avatar: "Padman_Full.png", HeroIcon: "Padman_HeroIcon.png", Sprite: "Padman_Sprite.png", hp: 1400, atk: 10, def: 10, PVELevelsText: ["不应用等级加成", ` \\i[hp]+400`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+10`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+10 \\i[card]+1 技能冷却-5`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+20 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+20 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+20 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 10; case 3: flags.lv3 = true; hero.ActiveSkillCD -= 5; case 2: hero.atk += 10; hero.def += 10; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: "CD45,你的额外攻防变成最大值", ActiveSkillCD: 45, Active: function () { core.lockControl() flags.CharacterATK = 20 * flags.ratio flags.CharacterDEF = 20 * flags.ratio hero.ActiveCD = hero.ActiveSkillCD core.drawTip("多有冒犯实在抱歉") core.playSound("Padman.mp3") core.afterSkill() }, PassiveSkill: "你初始拥有额外的攻防值(随机数0~20倍\\i[stars]),每场战斗后会改变你的额外攻防", Passive: function () { flags.CharacterATK = core.rand(20 * flags.ratio + 1) flags.CharacterDEF = core.rand(20 * flags.ratio + 1) }, PassiveTime: ["afterBattle"] } Character.帕帕拉 = { name: "帕帕拉", title: "猩红辣妹", file: "帕帕拉\n生日:10月31日\n喜欢的食物:番茄酱为辅料的任何东西\n“猩红行者”乐队的主唱兼吉他手,也是联合城地下飙车集团“地窖”的主理人。乐队吉他主唱是她童年的兴趣爱好,飙车才是现在的心头好。她喜欢把车子涂成红色并且贴上各种可爱的贴纸。\n现在依然活跃于地下赛车场和各Livehouse。", Main: "Papara.webp", startsound: "Bond_Papara.mp3", startText: "Honey Honey,在这里相遇一定是某种缘分,缘分!命中注定show time☆!", Avatar: "Papara_Full.png", HeroIcon: "Papara_HeroIcon.png", Sprite: "Papara_Sprite.png", hp: 1600, atk: 15, def: 10, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+5`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+10 \\i[card]+1`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+20 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.def += 10; case 3: flags.lv3 = true; hero.def += 5; case 2: hero.atk += 5; hero.def += 5; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: `CD70,吸收一只可被炸的怪物,回复其5倍攻防和的\\i[hp]并将其40%攻防用于下一场战斗`, ActiveSkillCD: 70, Active: function () { core.lockControl() core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") { core.drawTip("目标对象不为怪物,无法使用") return core.unlockControl() } const tx = flags.touchenemy[0] const ty = flags.touchenemy[1] const id = core.getBlockId(tx, ty) const enemy = core.material.enemys[id] if (enemy.notBomb) { core.drawTip("目标对象不可炸,无法使用") return core.unlockControl() } let todo = [] let money = core.getEnemyInfo(enemy, void 0, tx, ty).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; flags.cardATK += Math.floor(core.getEnemyInfo(enemy, void 0, tx, ty).atk * 2 / 5) flags.cardDEF += Math.floor(core.getEnemyInfo(enemy, void 0, tx, ty).def * 2 / 5) core.status.hero.hp += (core.getEnemyInfo(enemy, void 0, tx, ty).atk + core.getEnemyInfo(enemy, void 0, tx, ty).def) * 5 core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[tx + "," + ty]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[tx + "," + ty]; core.removeBlock(tx, ty); core.drawTip("给你的礼物!") core.playSound("Papara.mp3") hero.ActiveCD = hero.ActiveSkillCD if (todo.length > 0) core.insertAction(todo, void 0, void 0, core.afterSkill); else core.afterSkill() }) }, PassiveSkill: `伤害计算时\\i[atk]+\\i[hp]÷300`, Passive: function () {}, PassiveTime: ["getDamageInfo"] } Character.恋 = { name: "恋", title: "游戏大师", file: "恋\n生日:4月23日\n喜欢的食物:汉堡\n游戏《星引擎派对》的监制,超级游戏玩家/制作人。游戏杂志《pamagames》把她评选为联合城有史以来最强的游戏玩家。她喜欢收集一些有年代感的电子产品,还有一只灰色的鼠鼠宠物。\n自称永远18岁的谜之美少女,虽然游戏内的形象是她小的时候。", Main: "Koi.webp", startsound: "Bond_Koi.mp3", startText: "Hi~我是恋~姑且算是专业的游戏玩家吧~这个孩子呢,是我的宠物鼠鼠", Avatar: "Koi_Full.png", HeroIcon: "Koi_HeroIcon.png", Sprite: "Koi_Sprite.png", hp: 1400, atk: 15, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+5 \\i[card]+1`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+15 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+15 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+15 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10 hero.def += 10; case 3: flags.lv3 = true; hero.hp += 400; case 2: hero.atk += 5; hero.def += 5; case 1: hero.hp += 400; } }, ActiveSkill: "CD45,获得4层鼠鼠护盾", ActiveSkillCD: 45, Active: function () { core.lockControl() flags.mouseShield += 4 hero.ActiveCD = hero.ActiveSkillCD core.drawTip("这可是小孩子才有的特权~") core.playSound("Koi.mp3") core.afterSkill() }, PassiveSkill: `进行连续5场无伤的战斗后获得1层\n鼠鼠护盾:受到的伤害减少75倍\\i[stars](不同步地图显伤),在进行一场本应受到战斗伤害的战斗后失去1层`, Passive: function () { flags.mouseShield = 0 flags.mouseCD = 0 }, PassiveTime: [] } Character.Z3000 = { name: "Z3000", title: "垃圾箱", file: "Z3000\n生日:未知\n喜欢的食物:任何\n一切都是未知。有传言是疯狂科学家制造的助手机器人,也有人猜测是废旧工破产遗留下来的有意念的幽灵。可以确定的是它会把乱丢垃圾的人吸入自己的身体里好好的教训一番!\n目击到它的人很少,基本相当于都市传说。", Main: "Z3000.webp", startsound: "Bond_Z3000.mp3", startText: "(无法翻译Z3000的信息流)", Avatar: "Z3000_Full.png", HeroIcon: "Z3000_HeroIcon.png", Sprite: "Z3000_Sprite.png", hp: 1600, atk: 10, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+10`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+10 \\i[card]+1`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+10 \\i[card]+1 技能冷却-10 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+10 \\i[card]+1 技能冷却-10 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+10 \\i[card]+1 技能冷却-10 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.ActiveSkillCD -= 10; case 3: flags.lv3 = true; hero.atk += 5; case 2: hero.atk += 10; hero.def += 10; case 1: hero.hp += 400; } }, ActiveSkill: "CD60,选择一只可被炸的怪物,将其击杀并瞬移至目标点位", ActiveSkillCD: 60, Active: function () { core.lockControl() core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") { core.drawTip("目标对象不为怪物,无法使用") return core.unlockControl() } const tx = flags.touchenemy[0] const ty = flags.touchenemy[1] const id = core.getBlockId(tx, ty) const enemy = core.material.enemys[id] if (enemy.notBomb) { core.drawTip("目标对象不可炸,无法使用") return core.unlockControl() } let todo = [] let money = core.getEnemyInfo(enemy, void 0, tx, ty).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[tx + "," + ty]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[tx + "," + ty]; core.removeBlock(tx, ty); core.drawTip("技能使用成功") core.playSound("Z3000.mp3") core.setHeroLoc("x", tx) core.setHeroLoc("y", ty) core.clearMap("hero") core.drawHero() hero.ActiveCD = hero.ActiveSkillCD if (todo.length > 0) core.insertAction(todo, void 0, void 0, core.afterSkill); else core.afterSkill() }) }, PassiveSkill: `出牌后依据消耗的\\i[cost]减少技能冷却`, Passive: function () {}, PassiveTime: [] } Character.潘大猛 = { name: "潘大猛", title: "肉弹战车", file: "潘大猛\n生日:10月1日\n喜欢的食物:绿叶蔬菜\n土生土长的联合城人。街舞、摇滚、二次元文化都是其最爱。平时会在街区做街舞表演和义工,偶尔也去枫的酒店打工,毕竟他的爱好都挺花钱的。\n你可以在周末下午去联合城凰区的三神广场看他的表演。", Main: "Pandaman.webp", startsound: "Bond_Pandaman.mp3", startText: "我是圆滚滚胖墩墩的熊猫潘大猛!但是心灵上也很细腻很多情~", Avatar: "Pandaman_Full.png", HeroIcon: "Pandaman_HeroIcon.png", Sprite: "Pandaman_Sprite.png", hp: 2000, atk: 20, def: 0, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+5`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+5 \\i[card]+1`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.def += 5; case 3: flags.lv3 = true; hero.atk += 5; case 2: hero.atk += 5; hero.def += 5; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: "CD40,将一张无消耗的巧克力蛋糕或者汉堡添加到你的手牌", ActiveSkillCD: 40, Active: function () { core.lockControl() const R = ["ChocolateCake2", "Hamburger2"] core.drawFolds(R[core.rand(2)]) hero.ActiveCD = hero.ActiveSkillCD core.drawTip("慢慢享受吧~") core.playSound("Pandaman.mp3") core.afterSkill() }, PassiveSkill: `\\i[redPotion]\\i[bluePotion]\\i[yellowPotion]\\i[greenPotion]\\i[BuffHealth]回复翻倍,受到的战斗伤害+40%(不增加爆竹呱呱)\n所有百分比减伤效果加算,上限75%`, Passive: function () {}, PassiveTime: [] } Character.璐璐 = { name: "璐璐", title: "史莱姆", file: "璐璐\n生日:12月12日\n喜欢的食物:比自己小的都喜欢\n拉姆齐生物科技的技术顾问。她是实验室的产物,拥有非常高的智慧。一般情况下都会待在实验室里,偶尔溜出去外面进行她的“狩猎”活动,但是根本没人知道她到底“狩猎”了什么。\n璐璐的外形非常容易迷惑他人,千万不要上当!", Main: "Lulu.webp", startsound: "Bond_Lulu.mp3", startText: "你希望我怎么做呢?\n欸?好像没有呼吸了?", Avatar: "Lulu_Full.png", HeroIcon: "Lulu_HeroIcon.png", Sprite: "Lulu_Sprite.png", hp: 1400, atk: 15, def: 10, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5 \\i[card]+1 每场战斗后\\i[BuffHealth]+3`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+10 \\i[card]+1 每场战斗后\\i[BuffHealth]+3 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+10 \\i[card]+1 每场战斗后\\i[BuffHealth]+3 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+10 \\i[card]+1 每场战斗后\\i[BuffHealth]+3 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 5; hero.hp += 400; case 3: flags.lv3 = true; flags.health.upbuff += 3 case 2: hero.atk += 5; hero.def += 5; case 1: hero.hp += 400; } }, ActiveSkill: `CD60,在接下来的战斗中,每场战斗结束后\\i[BuffHealth]+3`, ActiveSkillCD: 60, Active: function () { core.lockControl() flags.health.upbuff += 3 hero.ActiveCD = hero.ActiveSkillCD core.drawTip("这样可以吗?可以的吧?") core.playSound("Lulu.mp3") core.afterSkill() }, PassiveSkill: `受到的伤害>3×\\i[stars]×\\i[BuffHealth]时,技能冷却-1`, Passive: function () {}, PassiveTime: ["afterBattle"] } Character.枫 = { name: "枫", title: "旗袍娘", file: "妪梦枫\n生日:11月7日\n喜欢的食物:牛肉丸\n联合城最好吃饭店【隆兴饭店】的老板娘。其真实身份是【凰天会】的大当家,道上的人都称她为“公主”。和帕露南有密切的商业往来,恋的家主和青梅竹马,主星之一。\n在隆兴饭店可以偶尔看见她和妹妹在指导工作。", Main: "Feng.webp", startsound: "Bond_Feng.mp3", startText: "我就是隆兴饭店的继承人,往后可就承蒙您关照啦~", Avatar: "Feng_Full.png", HeroIcon: "Feng_HeroIcon.png", Sprite: "Feng_Sprite.png", hp: 1600, atk: 15, def: 10, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5 \\i[card]+1 \\i[cost]上限+1`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5 \\i[card]+1 \\i[cost]上限+1 技能冷却-10 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5 \\i[card]+1 \\i[cost]上限+1 技能冷却-10 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5 \\i[card]+1 \\i[cost]上限+1 技能冷却-10 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.ActiveSkillCD -= 10; case 3: flags.lv3 = true; hero.cost++; hero.costmax++; case 2: hero.atk += 5; hero.def += 5; case 1: hero.hp += 400; } }, ActiveSkill: `CD50,\\i[cost]+max,\\i[hp]+400倍\\i[stars],若被动获得的攻防和>=20倍\\i[stars]则获得一张蓄力`, ActiveSkillCD: 50, Active: function () { core.lockControl() hero.cost = hero.costmax hero.hp += 400 * flags.ratio if (flags.CharacterATK + flags.CharacterDEF >= flags.ratio * 20) core.drawFolds("Charge") hero.ActiveCD = hero.ActiveSkillCD core.drawTip("这可是历史的集大成之作") core.playSound("Feng.mp3") core.afterSkill() }, PassiveSkill: `出牌时随机\\i[atk]/\\i[def]+5倍\\i[stars],满足以下其中一条规则时此法获得的属性变为2倍临时属性:\n出牌后\\i[cost]耗尽/此法获得的攻防和>=80倍\\i[stars]\n(此效果在BOSS战不生效)`, Passive: function () { const a = ["CharacterATK", "CharacterDEF"] flags[a[core.rand(2)]] += 5 * flags.ratio if (hero.cost === 0 || flags.CharacterATK + flags.CharacterDEF >= 80 * flags.ratio) { flags.cardATK += flags.CharacterATK * 2 flags.cardDEF += flags.CharacterDEF * 2 flags.CharacterATK = 0 flags.CharacterDEF = 0 } }, PassiveTime: ["cardAfter"] } Character.蓝海晴 = { name: "蓝海晴", title: "命运少女", file: "蓝海晴\n生日:3月22日\n喜欢的食物:草莓\n占卜店“日月”的老板。看起来比较成熟,其实才从大学毕业几年。因为性格和外表的缘故很受顾客欢迎。传言在大学时期加入的社团是一个情报组织,为了收集有关“星引擎”的消息。周末会去一个叫“虹之镜”的酒吧消遣,和那里的酒保库拉拉是多年的好友。\n和大学时代比样貌有明显变化。", Main: "LanHaiQing.webp", startsound: "Bond_LanHaiQing.mp3", startText: "我来占卜下你的命运……当然也可以改变你的命运,如何?", Avatar: "LanHaiQing_Full.png", HeroIcon: "LanHaiQing_HeroIcon.png", Sprite: "LanHaiQing_Sprite.png", hp: 1400, atk: 15, def: 10, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5`, `\\i[hp]+400 \\i[atk]+5 \\i[def]+5 \\i[card]+1 技能冷却-5`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+15 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+15 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+15 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 10; case 3: flags.lv3 = true; hero.ActiveSkillCD -= 5; case 2: hero.atk += 5; hero.def += 5; case 1: hero.hp += 400; } }, ActiveSkill: `CD45,指定一个怪物对其附加30层\\i[BuffMark]`, ActiveSkillCD: 45, Active: function () { core.lockControl() core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy !== "failed") { core.setEnemyOnPoint(flags.touchenemy[0], flags.touchenemy[1], void 0, "mark", 30, "+=") hero.ActiveCD = hero.ActiveSkillCD core.drawTip("开辟命运吧") core.playSound("LanHaiQing.mp3") core.afterSkill() } else { core.drawTip("为非怪物的物品占卜,是不被天命所允许的") core.unlockControl() } }) delete flags.touchenemy }, PassiveSkill: `击败有\\i[BuffMark]的怪物后获得\\i[BuffMark]层数的\\i[money]。受到的战斗伤害为6的正整数倍时\\i[money]+6`, Passive: function () {}, PassiveTime: ["afterBattle"] } Character.美咲 = { name: "美咲", title: "太刀使", file: "龙崎美咲\n生日:7月20日\n喜欢的食物:抹茶冰淇淋\n三神教教主的护法。三神教是和星辉院齐名的又一宗教机构。它不光承担着福利院的工作,还有和本苍院一样的文献研究能力。美联的龙崎家与天王寺家,米泽家同是三神教三大家族。\n和雅央以及教主是青梅竹马,与青山小町是好友。一直在追查“天王落”事件的真相。", Main: "Misaki.webp", startsound: "Bond_Misaki.mp3", startText: "若你欣赏我的剑术,我随时可以动身", Avatar: "Misaki_Full.png", HeroIcon: "Misaki_HeroIcon.png", Sprite: "Misaki_Sprite.png", hp: 1400, atk: 10, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+800 \\i[atk]+10`, `\\i[hp]+800 \\i[atk]+20 \\i[card]+1 技能冷却-5`, `\\i[hp]+800 \\i[atk]+25 \\i[def]+5 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+25 \\i[def]+5 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+25 \\i[def]+5 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 5; hero.def += 5; case 3: flags.lv3 = true; hero.ActiveSkillCD -= 5; hero.atk += 10; case 2: hero.atk += 10; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: "CD50,对前方3*3区域内的所有怪物造成自身攻击力的伤害(不计算绿字属性)(可触发诅咒之剑)", ActiveSkillCD: 50, Active: async function () { core.lockControl() core.drawTip("接招吧,吾之奥义!") core.playSound("Misaki.mp3") let dir = hero.loc.direction let rect = [ [core.nextX(2), core.nextY(2)] ] let todo = [] for (let i in core.utils.scan2) { rect.push([core.nextX(2) + core.utils.scan2[i].x, core.nextY(2) + core.utils.scan2[i].y]) } for (let i of rect) { if (core.getBlockCls(i[0], i[1]) === "enemys") { core.setEnemyOnPoint(i[0], i[1], core.status.floorId, "hp", hero.atk + flags.CharacterATK, "-=") var id = core.getBlockId(i[0], i[1]), enemy = core.material.enemys[id]; if (core.getEnemyInfo(enemy, void 0, i[0], i[1]).hp <= 0) { let money = core.getEnemyInfo(enemy, void 0, i[0], i[1]).money if (core.hasItem("CursedSword")) { flags.Cursed++ if (flags.Cursed === 10) { flags.Cursed = 0 hero.atk += flags.ratio hero.def-- core.drawTip("激活诅咒之剑效果,攻击+" + flags.ratio + ",防御-1", "CursedSword") } } if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[i[0] + "," + i[1]]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[i[0] + "," + i[1]]; core.removeBlock(i[0], i[1]); } } } hero.ActiveCD = hero.ActiveSkillCD core.drawAnimate("jianqi", core.nextX(2), core.nextY(2), false, () => { if (todo.length > 0) core.insertAction(todo, void 0, void 0, core.afterSkill); else core.afterSkill(); }) }, PassiveSkill: `受到伤害<=50时,\\i[atk]+3(MAX:30倍\\i[stars])\n受到伤害>=200倍\\i[stars]时,清除此被动的全部攻击力加成`, Passive: function () {}, PassiveTime: ["afterBattle"] } Character.娜蒂斯 = { name: "娜蒂斯", title: "绿洲女王", file: "娜蒂斯·恩菲尔德\n生日:9月19日\n喜欢的食物:炭烤鹰嘴豆\n绿洲市的权贵,也是联合城领导者家族之一。作为联合城的工业命脉,绿洲市的能源产业对于联合城至关重要。恩菲尔德家族不但是绿洲市实质的掌权者,还是联合城掌权议会中的一员,家族的产业遍布整个绿洲市。\n娜蒂斯长期居住在联合城,偶尔被家族叫回绿洲市。", Main: "Nardis.webp", startsound: "Bond_Nardis.mp3", startText: "新来的下人?我会审视你的工作态度。为自己受命于我而倍感光荣吧", Avatar: "Nardis_Full.png", HeroIcon: "Nardis_HeroIcon.png", Sprite: "Nardis_Sprite.png", hp: 1600, atk: 10, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+5`, `\\i[hp]+400 \\i[atk]+5 \\i[card]+1 技能冷却-5`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+10 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+10 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+400 \\i[atk]+15 \\i[def]+10 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 10; case 3: flags.lv3 = true; hero.ActiveSkillCD -= 5; case 2: hero.atk += 5; case 1: hero.hp += 400; } }, ActiveSkill: "CD40,抽8张牌。\\i[atk]\\i[def]提升本次丢弃手牌数×\\i[stars]的算术平方根", ActiveSkillCD: 40, Active: function () { core.lockControl() const f = Math.floor(hero.myCard.length * Math.sqrt(flags.ratio)) hero.myCard = [] for (let i = 0; i < 8; i++) { core.drawFolds(core.plugin.card.cardList[core.randcard()]) } hero.atk += f hero.def += f hero.ActiveCD = hero.ActiveSkillCD core.drawTip("这是你逼我的!") core.playSound("Nardis.mp3") core.afterSkill() }, PassiveSkill: `伤害计算时\\i[atk]提升\\i[card]×\\i[stars]`, Passive: function () {}, PassiveTime: [] } Character.茉莉 = { name: "茉莉", title: "家政机器人", file: "茉莉\n生日:8月12日\n喜欢的食物:无\n蓝海晴在大学时期同好会一同制作的机器人.内部的系统是一个在“暗网”搞到的据说非常高质量的AI。茉莉在她们毕业后被同好会的另一个成员露佳·皮克西带去了拉姆齐生物科技的技术部门,在那里她变成了一个得力的助手。\n茉莉会经常把璐璐当做有害垃圾清理,虽然遭到了璐璐多次抗议,但还是乐此不疲。", Main: "Jasmine.webp", startsound: "Bond_Jasmine.mp3", startText: "我是家政机器人茉莉。不仅外观可爱,工作也很完美哦~", Avatar: "Jasmine_Full.png", HeroIcon: "Jasmine_HeroIcon.png", Sprite: "Jasmine_Sprite.png", hp: 1200, atk: 10, def: 10, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+800 \\i[atk]+10`, `\\i[hp]+800 \\i[atk]+10 \\i[card]+1 技能冷却-5`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+10 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+10 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+10 \\i[card]+1 技能冷却-5 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 10; case 3: flags.lv3 = true; hero.ActiveSkillCD -= 5; case 2: hero.atk += 10; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: `CD50,下一场战斗时获得3连击,\\i[def]变为0`, ActiveSkillCD: 50, Active: function () { core.lockControl() flags.jasmine = true hero.ActiveCD = hero.ActiveSkillCD core.drawTip("开始行动,执行神秘战法") core.playSound("Jasmine.mp3") core.afterSkill() }, PassiveSkill: `到达新楼层时或每击败26只怪物时,获得\\i[stars]的\\i[atk]\\i[def]`, Passive: function () { hero.atk += flags.ratio hero.def += flags.ratio }, PassiveTime: ["firstArrive"] } Character.星魅琉华 = { name: "星魅琉华", title: "午夜闪光", file: "星魅琉华\n生日:2月15日\n喜欢的食物:三文鱼料理库拉拉的好友兼搭档,“虹之镜”酒吧的调酒师。“退魔之夜”和“天王落”事件的亲历者。午夜飙车党的精神领袖,人送称号“午夜闪光”。“天王落”事件引发了公司之间的激烈战斗,“虹之镜”作为之前的万事屋自然不能避免,自从酒吧名义上的老大在“退魔之夜”失踪后,星魅琉华也逐渐的淡出了大众的视野。\n周六偶尔会帮助库拉拉打理酒吧。", Main: "StarLuka.webp", startsound: "Bond_StarLuka.mp3", startText: "你就是我的搭档?拖后腿的话我可不会饶过你的", Avatar: "StarLuka_Full.png", HeroIcon: "StarLuka_HeroIcon.png", Sprite: "StarLuka_Sprite.png", hp: 1400, atk: 15, def: 10, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+10`, `\\i[hp]+800 \\i[atk]+10 \\i[card]+1`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+5 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+5 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+5 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 5; hero.def += 5; case 3: flags.lv3 = true; hero.hp += 400; case 2: hero.atk += 10; case 1: hero.hp += 400; } }, ActiveSkill: `CD60,投掷2枚6面骰子,对你面前方向的敌人造成10%\\i[atk]×骰子点数的伤害\n如果点数>=10,则破坏方向上的墙\n如果点数为12,刷新技能\n\r[#F00]警告:此技能会连带空地上的隐藏事件一块移除\r`, ActiveSkillCD: 60, Active: function () { core.lockControl() core.drawTip("把他们全部撞飞!") core.playSound("StarLuka.mp3") const result = core.rand(6) + core.rand(6) + 2 const AOEdamage = Math.floor(result * hero.atk / 10) let lineenemy = [] let linewall = [] for (let i = 1;; i++) { const a = [core.nextX(i), core.nextY(i)] if (a[1] < 0 || a[1] > 12 || a[0] < 0 || a[0] > 12) break if (core.getBlockCls(a[0], a[1]) === "enemys") lineenemy.push(a) else if (core.getBlockCls(a[0], a[1]) === "items" || core.getBlockCls(a[0], a[1]) === "npcs" || core.getBlockCls(a[0], a[1]) === "npc48") continue else linewall.push(a) } let todo = [] for (let i of lineenemy) { core.setEnemyOnPoint(i[0], i[1], core.status.floorId, "hp", AOEdamage, "-=") var id = core.getBlockId(i[0], i[1]), enemy = core.material.enemys[id]; if (core.getEnemyInfo(enemy, void 0, i[0], i[1]).hp <= 0) { let money = core.getEnemyInfo(enemy, void 0, i[0], i[1]).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[i[0] + "," + i[1]]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[i[0] + "," + i[1]]; core.removeBlock(i[0], i[1]); } } if (result >= 10) linewall.forEach(v => { if (!["upFloor", "downFloor", "upPortal", "downPortal", "leftPortal", "rightPortal"].includes(core.getBlockId(v[0], v[1]))) core.removeBlock(v[0], v[1]); if (flags.MapStatus?.Map === "水乡古镇") core.setBgFgBlock("bg", 141, v[0], v[1]) }) core.drawMap() core.drawTip("骰子结果:" + result) if (result !== 12) hero.ActiveCD = hero.ActiveSkillCD if (todo.length > 0) core.insertAction(todo, void 0, void 0, core.afterSkill); else core.afterSkill(); }, PassiveSkill: "出牌后或抵达新楼层时,技能冷却/魔法秘典/诅咒之剑/cost回复计数器推进0~3格", Passive: function () { const result = core.rand(4) if (hero.ActiveCD > 0) hero.ActiveCD -= result if (hero.ActiveCD < 0) hero.ActiveCD = 0 if (hero.cost < hero.costmax) hero.costCD-- if (hero.costCD <= 0) { hero.costCD = 10 + hero.costCD hero.cost++ } if (hero.cost === hero.costmax) hero.costCD = 10 if (core.hasItem("CursedSword")) { flags.Cursed += result if (flags.Cursed >= 10) { flags.Cursed -= 10 hero.atk += flags.ratio hero.def-- core.drawTip("激活诅咒之剑效果,攻击+" + flags.ratio + ",防御-1", "CursedSword") } } if (core.hasItem("MagicTome")) { flags.Tome = (flags.Tome ?? 0) + 1 if (flags.Tome >= 5) { flags.Tome -= 5 core.drawFolds("Firecrackers2") core.drawTip("魔法秘典触发", "MagicTome") } } core.drawTip("骰子结果:" + result) }, PassiveTime: ["cardAfter", "firstArrive"] } Character.南希露 = { name: "南希露", title: "网络魅影", file: "南希露\n生日:未知\n喜欢的食物:未知\n库拉拉的好友兼搭档,“虹之镜”酒吧的服务生。“退魔之夜”和“天王落”事件的亲历者。都市传说级别的超级网络黑客“网络魅影”。当然这个称号也是网上那些小屁孩起的,南希露从没说过自己是谁。有一个好友因为暗网的问题而丧命后就一直在默默的调查“星引擎”的下落,意图复活好友。\n听说一直住在“虹之镜”酒吧。", Main: "Nancy_Lu.webp", startsound: "Bond_Nancy_Lu.mp3", startText: "哦?新来的?那么首先,希望你能记住黑客的变装和IP掩护", Avatar: "Nancy_Lu_Full.png", HeroIcon: "Nancy_Lu_HeroIcon.png", Sprite: "Nancy_Lu_Sprite.png", hp: 1400, atk: 10, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+5`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+5 \\i[card]+1`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+20 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 5; case 3: flags.lv3 = true; hero.hp += 400; case 2: hero.atk += 10; hero.def += 5; case 1: hero.hp += 400; } }, ActiveSkill: `限定1次。重置当前楼层。那之后本楼层每有一个\\i[battletoken]受到5000(魔法学院10000)伤害`, ActiveSkillCD: Infinity, Active: function () { core.lockControl() const special1 = [1, 2, 3, 4, 5, 7, 8, 10, 11, 15, 22, 29, 27] function filter(block) { return block.event.cls.indexOf("enemy") === 0 } const mapold = core.maps.searchBlockWithFilter(filter) const posold = [] for (let i of mapold) { posold.push(i.x + "-" + i.y) } core.resetMap() const mapnew = core.maps.searchBlockWithFilter(filter) const posnew = [] for (let i of mapnew) { posnew.push(i.x + "-" + i.y) } for (let i of posnew) { if (!posold.includes(i)) { const pos = i.split("-") const id = core.getBlockId(pos[0], pos[1]) const floorId = core.status.floorId if (id === "yellowKnight" || id === "swordEmperor" || id === "magicDragon" || id === "octopus" || id === "blackMagician") continue if (id === "blackKing") { const special = core.getEnemyValue(void 0, "special", pos[0], pos[1], floorId) const rand = core.rand(special1 - 1) const rand1 = special1[core.rand(special1.length)] const rand2 = special1[core.rand(special1.length)] const a = [...special] if (flags.MapStatus?.Hard === "噩梦" || flags.MapStatus?.Hard === "疯狂") a.push(rand1) if (flags.MapStatus?.Hard === "疯狂") a.push(rand2) core.setEnemyOnPoint(pos[0], pos[1], floorId, "special", b, "=", void 0, true) } else { const rand1 = special1[core.rand(special1.length)] const rand2 = special1[core.rand(special1.length)] const special = core.getEnemyValue(void 0, "special", pos[0], pos[1]) const a = [...special] if (flags.MapStatus?.Hard === "噩梦" || flags.MapStatus?.Hard === "疯狂") a.push(rand1) if (flags.MapStatus?.Hard === "疯狂") a.push(rand2) core.setEnemyOnPoint(pos[0], pos[1], floorId, "special", a, "=", void 0, true) } } } switch (flags.MapStatus?.Hard) { case "疯狂": const a = core.maps.searchBlock("cross") if (a.length > 0) core.setBlock(421, a[0].x, a[0].y) case "噩梦": const b = core.maps.searchBlock("bomb") if (b.length > 0) core.setBlock(421, b[0].x, b[0].y) case "困难": const c = core.maps.searchBlock("knife") if (c.length > 0) core.setBlock(421, c[0].x, c[0].y) } core.updateStatusBar() const BT = core.maps.searchBlock("battletoken") hero.hp -= BT.length * (flags.MapStatus?.Map === "魔法学院" ? 10000 : 5000) if (hero.hp <= 0) return core.lose("信息泄露了……!?") hero.ActiveCD = hero.ActiveSkillCD core.drawTip("发现漏洞~") core.playSound("Nancy_Lu.mp3") core.afterSkill() }, PassiveSkill: `若此技能获得的攻防未达到最大值,战斗后丢弃最左边的手牌(如果有)。依据被丢弃卡牌消耗的cost获得攻防(MAX:30倍\\i[stars])`, Passive: function () { if (hero.myCard.length > 0 && flags.CharacterATK < 30 * flags.ratio) { const cost = core.plugin.card.cardsInfo[hero.myCard.shift()].cost if (flags.CharacterATK < 30 * flags.ratio) flags.CharacterATK = flags.CharacterDEF = Math.min(flags.CharacterATK + cost, 30 * flags.ratio) } }, PassiveTime: ["afterBattle"] } Character.梅加斯 = { name: "梅加斯", title: "机械超人", file: "梅加斯·威廉姆斯\n生日:1月23日\n喜欢的食物:菲力牛排\n库拉拉的好友兼搭档,“虹之镜”酒吧的服务生。“退魔之夜”和“天王落”事件的亲历者。格斗游戏大师,因其精湛的操作和机械方面的造诣游戏界称号“机械师”。恋小时候就认识梅加斯,算是恋的对手和老师。“天王落”事件引发了公司之间的激烈战斗,梅加斯也在事件中遭受了重大的精神挫折,与星魅琉华一样淡出了人们的视野。\n和琉华住在一起,偶尔会出现在酒吧。", Main: "Megas.webp", startsound: "Bond_Megas.mp3", startText: "嗯……?你也在玩这个游戏啊,先说好,我可不会手下留情哦", Avatar: "Megas_Full.png", HeroIcon: "Megas_HeroIcon.png", Sprite: "Megas_Sprite.png", hp: 1400, atk: 5, def: 20, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+800 \\i[def]+5`, `\\i[hp]+800 \\i[def]+10 \\i[card]+1`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+15 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+15 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+5 \\i[def]+15 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 5; hero.def += 5; case 3: flags.lv3 = true; hero.def += 5; case 2: hero.def += 5; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: `CD40,丢弃所有\\i[card]并耗尽\\i[cost],对本楼所有可被炸的怪物造成\\i[card]的\\i[cost]总和×消耗的\\i[cost]×(\\i[stars]的算术平方根)伤害`, ActiveSkillCD: 40, Active: function () { core.lockControl() let cardcost = 0, enemys = []; hero.myCard.forEach(k => { cardcost += core.plugin.card.cardsInfo[k].cost }) const AOEdamage = Math.floor(cardcost * hero.cost * Math.sqrt(flags.ratio)) hero.myCard = [] hero.cost = 0 const floorId = core.status.floorId for (let i = 0; i < 13; i++) { for (let j = 0; j < 13; j++) { if (core.getBlockCls(i, j) === "enemys") enemys.push([i, j]) } } let todo = [] enemys.forEach(v => { const id = core.getBlockId(v[0], v[1]) const z = core.material.enemys[id] if (z.notBomb) return core.setEnemyOnPoint(v[0], v[1], void 0, "hp", AOEdamage, "-=") if (core.getEnemyInfo(id, void 0, v[0], v[1]).hp <= 0) { let money = core.getEnemyInfo(id, void 0, v[0], v[1]).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, z.afterBattle); core.push(todo, core.floors[floorId].afterBattle[v[0] + "," + v[1]]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[v[0] + "," + v[1]]; core.removeBlock(v[0], v[1]); } }) hero.ActiveCD = hero.ActiveSkillCD core.drawTip("↑↑↓↓←→BA") core.playSound("Megas.mp3") if (todo.length > 0) core.insertAction(todo, void 0, void 0, core.afterSkill); else core.afterSkill() }, PassiveSkill: `每回合额外造成自身原始\\i[atk]×(\\i[stars]的算术平方根)÷5的伤害。若手牌数<5,战后\\i[card]+1`, Passive: function () { if (hero.myCard.length < 5) { core.drawFolds(core.plugin.card.cardList[core.randcard()]) } }, PassiveTime: ["afterBattle"] } Character.姬梦朝 = { name: "姬梦朝", title: "风水师", file: "姬梦朝\n生日:10月17日\n喜欢的食物:豆腐制品\n“隆兴饭店”的二把手。其真实身份是“凰天会”的二当家,也是会里的风水师,道上的人都称她为“二公主”。与姐姐不同,梦朝主要是负责内部事务,在饭店和自己领地出现的次数比姐姐要多的多。\n每周末饭店的例行检查她都会和食客们攀谈,以改善饭店的服务质量。", Main: "Zhao.webp", startsound: "Bond_Zhao.mp3", startText: "我是姬梦朝,目前在隆兴饭店工作。虽然不如姐姐枫那么优秀,但我会努力工作的!", Avatar: "Zhao_Full.png", HeroIcon: "Zhao_HeroIcon.png", Sprite: "Zhao_Sprite.png", hp: 1600, atk: 15, def: 10, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[def]+5`, `\\i[hp]+400 \\i[def]+5 \\i[card]+1 战后\\i[BuffHealth]+3`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+15 \\i[card]+1 战后\\i[BuffHealth]+3 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+15 \\i[card]+1 战后\\i[BuffHealth]+3 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+15 \\i[card]+1 战后\\i[BuffHealth]+3 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; hero.def += 10; case 3: flags.lv3 = true; flags.health.upbuff += 3 case 2: hero.def += 5; case 1: hero.hp += 400; } }, ActiveSkill: "CD45,将所有手牌变成符卡-福", ActiveSkillCD: 45, Active: function () { core.lockControl() hero.myCard = hero.myCard.map(v => "TalismanFortune") hero.ActiveCD = hero.ActiveSkillCD core.drawTip("急急如律令!!") core.playSound("Zhao.mp3") core.afterSkill() }, PassiveSkill: "抵达新楼层时投掷6面骰子,依据点数获得对应卡牌\n1/2/3:符卡-祸\n4/5:随机卡牌\n6:符卡-福", Passive: function () { const result = core.rand(6) + 1 switch (result) { case 1: case 2: case 3: core.drawFolds("TalismanMisfortune"); break; case 4: case 5: core.drawFolds(core.plugin.card.cardList[core.randcard()]); break; case 6: core.drawFolds("TalismanFortune"); break; } core.drawTip("骰子结果:" + result) }, PassiveTime: ["firstArrive"] } Character.阿尔 = { name: "阿尔", title: "暗区少主", file: "阿尔·威尔逊\n生日:1月2日\n喜欢的食物:奶酪\n“地害”的创始人之一,与“凰天会”和恩菲尔德家族有着密切往来,人称“A.L”。兵器“天王”的持有者。“天王落”事件的主因。因为阿尔不知为何原因准备将“天王”摧毁才引发了后面的公司战争。摧毁天王使得一系列的神秘事件重新开始袭扰联合城。也正因此事,神器才又重回大众视野。\n在“天王落”事件后消失,不知所踪。", Main: "AL.webp", startsound: "Bond_AL.mp3", startText: "嗯?新面孔?别碍事啊。你也不想成为猎犬的食粮,对吧?", Avatar: "AL_Full.png", HeroIcon: "AL_HeroIcon.png", Sprite: "AL_Sprite.png", hp: 1600, atk: 10, def: 10, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+800 \\i[atk]+10`, `\\i[hp]+800 \\i[atk]+10 \\i[card]+1 \\i[BuffStarlight]+3`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+5 \\i[card]+1 \\i[BuffStarlight]+3 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+5 \\i[card]+1 \\i[BuffStarlight]+3 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+15 \\i[def]+5 \\i[card]+1 \\i[BuffStarlight]+3 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 5; hero.def += 5; case 3: flags.lv3 = true; flags.star.level += 3; case 2: hero.atk += 10; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: `CD55,\\i[BuffStarlight]+1~6`, ActiveSkillCD: 55, Active: function () { core.lockControl() const result = core.rand(6) + 1 core.drawTip("亮出底牌吧!") core.playSound("AL.mp3") flags.star.level += result core.drawTip("骰子结果:" + result) hero.ActiveCD = hero.ActiveSkillCD core.afterSkill() }, PassiveSkill: `每拥有1层\\i[BuffStarlight]减少1%受到的战斗伤害(不减免爆竹呱呱)\n所有百分比减伤效果加算,上限75%`, Passive: function () {}, PassiveTime: ["getDamageInfo"] } Character.凛 = { name: "凛", title: "新人调查员", file: "克利西亚·米泽·凛\n生日:5月25日\n喜欢的食物:巧克力饼干\n米泽家的大小姐。按理说应该是三神教的护法之一,但是忍受不了那些反繁文缛节让大哥给安排了一出“离家出走”。现任职于“联合城异常调查处(UAID)”,职位是初级调查员。但因为一直笨手笨脚常常被上司训斥。\n时常因为工作郁闷会去雅央那里过周末,做得一手连雅央和美咲都赞不绝口的料理。", Main: "Rin.webp", startsound: "Bond_Rin.mp3", startText: "初次见面,我叫凛。虽然是新人调查员,但我一定会完成任务……希望能做得来", Avatar: "Rin_Full.png", HeroIcon: "Rin_HeroIcon.png", Sprite: "Rin_Sprite.png", hp: 1300, atk: 15, def: 15, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+800 \\i[atk]+10`, `\\i[hp]+800 \\i[atk]+10 \\i[card]+1 \\i[money]+200`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+10 \\i[card]+1 \\i[money]+200 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+10 \\i[card]+1 \\i[money]+200 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+800 \\i[atk]+10 \\i[def]+10 \\i[card]+1 \\i[money]+200 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.def += 10; case 3: flags.lv3 = true; hero.money += 200; case 2: hero.atk += 10; hero.hp += 400; case 1: hero.hp += 400; } }, ActiveSkill: "CD60,将所有手牌变成活体书页", ActiveSkillCD: 60, Active: function () { core.lockControl() hero.myCard = hero.myCard.map(v => "LivingBook") hero.ActiveCD = hero.ActiveSkillCD core.drawTip("我会竭尽全力") core.playSound("Rin.mp3") core.afterSkill() }, PassiveSkill: "打出活体书页后,活体书页倍率+1", Passive: function () {}, PassiveTime: [] } Character.照 = { name: "照", title: "三神御主", file: "照\n生日:2月19日\n喜欢的食物:烤鳗鱼\n“三神教”现任的教主。三神教作为联合城比较有影响力的组织,人员组成结构复杂。作为“教主”的照一般都很少露面,但是作为一般人的照则是非常喜欢外出游玩。她本人也不拘泥非要穿着传统服饰以彰显自己的“教主”地位,相反她很喜欢穿一些很符合她身型的衣服。\n照有时候会偷偷溜去雅央那里玩,这让美咲和组织内的长老很是头疼。", Main: "Teru.webp", startsound: "Bond_Teru.mp3", startText: "我乃三神教教主——照\n愿与你同行,寻得你心之所向", Avatar: "Teru_Full.png", HeroIcon: "Teru_HeroIcon.png", Sprite: "Teru_Sprite.png", hp: 1600, atk: 20, def: 5, PVELevelsText: ["不应用等级加成", `\\i[hp]+400`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+5`, `\\i[hp]+400 \\i[atk]+10 \\i[def]+10 \\i[card]+1`, `\\i[hp]+400 \\i[atk]+20 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset]`, `\\i[hp]+400 \\i[atk]+20 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken]`, `\\i[hp]+400 \\i[atk]+20 \\i[def]+10 \\i[card]+1 \\i[battletoken]+2\\i[reset] 开局挑选精选\\i[battletoken] \\i[battletoken]1次手快全拿` ], PVELevels: function (a) { switch (a) { case 6: flags.lv6 = true; case 5: flags.lv5 = true; case 4: flags.rollchip += 2; hero.atk += 10; case 3: flags.lv3 = true; hero.def += 5; case 2: hero.def += 5; hero.atk += 10; case 1: hero.hp += 400; } }, ActiveSkill: "CD40,对前方可视距离你最近的怪物造成你原始攻击力的伤害。\n可储存使用,初始次数为1,上限为3。技能完成冷却后次数+1并重新进入冷却", ActiveSkillCD: 40, Active: function () { core.lockControl() for (let i = 1;; i++) { let x = core.nextX(i), y = core.nextY(i) if (x < 0 || x > 12 || y < 0 || y > 12) { core.drawTip("啊嘞~狐火飞远了~要不换个地方再试一次吧") return core.unlockControl() } const Block = core.getBlock(x, y)?.event if (!Block || Block.id === "none" || Block.cls === "item") continue if (Block.cls !== "enemys") { core.drawTip("这里不能施展降神啦!") return core.unlockControl() } flags.Teru.Active-- core.drawTip("以教主之名!") core.playSound("Teru.mp3") const Damage = Math.floor(core.getRealStatus("atk") * (10 + flags.Teru.RankUP) / 10) let todo = [] core.setEnemyOnPoint(x, y, core.status.floorId, "hp", Damage, "-=") const enemy = core.material.enemys[Block.id]; if (core.getEnemyInfo(Block.id, void 0, x, y).hp <= 0) { let money = core.getEnemyInfo(Block.id, void 0, x, y).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[x + "," + y]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[x + "," + y]; core.removeBlock(x, y); } if (todo.length > 0) core.insertAction(todo, void 0, void 0, core.afterSkill) else core.afterSkill() return } }, PassiveSkill: `出牌后消耗的\\i[cost]>=2时,提升10%主动技能造成的伤害`, Passive: function () { flags.Teru = {} flags.Teru.Active = 1 flags.Teru.RankUP = 0 hero.ActiveCD = hero.ActiveSkillCD }, PassiveTime: [] } Character.摩西 = { name: "摩西", title: "", file: "", Main: "Moses.webp", startsound: "", startText: "", Avatar: "Moses_Full.png", HeroIcon: "hero.png", Sprite: "Moses_Sprite.png", hp: 1600, atk: 15, def: 10, PVELevelsText: ["", `\\i[hp]+400`, "", `\\i[card]+1`, `\\i[battletoken]+2\\i[reset]`, `开局挑选精选\\i[battletoken]`, `\\i[battletoken]1次手快全拿`], PVELevels: function (a) {}, ActiveSkill: "", ActiveSkillCD: 50, Active: function () { core.lockControl() core.afterSkill() }, PassiveSkill: "", Passive: function () {}, PassiveTime: [] } core.registerReplayAction("skill", (action) => { if (action !== "skill") return false if (hero.ActiveCD > 0 && hero.name !== "照") return false if (hero.name === "照" && flags.Teru.Active < 1) return false core.plugin.Character[hero.name].Active() core.status.route.push('skill') core.updateStatusBar() core.replay() return true }) }, "Cards": function () { /** * 抽牌,爆牌时丢弃最左侧卡牌 * @param {string|string[]} cards */ this.drawFolds = function (cards) { hero.myCard = hero.myCard.concat(cards) for (;;) { if (hero.myCard.length <= 8) return hero.myCard.shift() } } /** * 绘制卡牌图片,并用下载器输出 * @param {string} name 生成图片的文件名,图片格式为.webp * @param {string} title 卡名 * @param {number} cost 卡牌费用,0~5,type为Event失效 * @param {"Attack"|"Defend"|"Effect"|"Event"} type 卡牌类型 * @param {string} text 卡牌文本,可使用模板字符串 * @param {string} image 卡面图案,需要在全塔属性注册对应图片 */ this.drawCardone = function drawCardone(name, title, cost, type, text, image) { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); canvas.width = 170 * 2 canvas.height = 270 * 2 core.fillRoundRect(ctx, 0, 0, 170 * 2, 270 * 2, 17 * 2, "#000000") core.strokeRoundRect(ctx, 2 * 2, 2 * 2, 166 * 2, 266 * 2, 17 * 2, "#BB9B6E", 2 * 2) core.drawImage(ctx, image, 4 * 2, 4 * 2, 162 * 2, 162 * 2) core.drawImage(ctx, "TypeIcon" + type + ".webp", 4 * 2, 4 * 2, 32 * 2, 32 * 2) ctx.beginPath() ctx.moveTo(33 * 2, 148 * 2) ctx.lineTo(133 * 2, 148 * 2) ctx.arc(133 * 2, 164 * 2, 16 * 2, -Math.PI / 2, Math.PI / 2) ctx.lineTo(33 * 2, 180 * 2) ctx.arc(33 * 2, 164 * 2, 16 * 2, Math.PI / 2, 3 * Math.PI / 2) ctx.closePath() switch (type) { case "Attack": ctx.fillStyle = "#D8220DE5" break; case "Defend": ctx.fillStyle = "#00FFFFE5" break; case "Effect": ctx.fillStyle = "#DDFF00E5" break; case "Event": ctx.fillStyle = "#FFDD00E5" break; } ctx.fill() core.setTextAlign(ctx, "center") core.fillBoldText(ctx, title, 83 * 2, 169 * 2, "#FFFFFF", "#000000", 3 * 2, "36px STHUPO") let dy = 37 * 2 for (let i = 0; i < cost; i++) { core.drawIcon(ctx, type + "Cost", 8 * 2, dy, 21 * 2, 21 * 2) dy += 23 * 2 } core.drawTextContent(ctx, text, { left: 5 * 2, top: 190 * 2, color: "#FFFFFF", fontSize: 14 * 2, align: "left", maxWidth: 160 * 2, font: "cjk" }) core.CanvastoWebp(name, canvas) } class Card { constructor() { this.cardList = ["AttackM", "AttackL", "AttackG", "DefendM", "DefendL", "DefendG", "GaWuCut", "Charge", "Railgun", "Blast", "LaserBeam", "Berserk", "Brick", "Firecrackers", "CardCollecter", "ChocolateCake", "Hamburger", "HurryHurry", "ShadowAttack", "SmartDice", "KingsPower", "Immovable", "FightFirewithFire", "SelfExplosion" ] this.selectcard = "" this.cardsInfo = { AttackM: { image: "AttackM.webp", cost: 1, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(15) + 1 flags.cardATK += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardcostdown(1) core.plugin.card.cardAfter() }) } }, AttackL: { image: "AttackL.webp", cost: 2, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(25) + 6 flags.cardATK += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardcostdown(2) core.plugin.card.cardAfter() }) } }, AttackG: { image: "AttackG.webp", cost: 3, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(36) + 15 flags.cardATK += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardcostdown(3) core.plugin.card.cardAfter() }) } }, GaWuCut: { image: "GaWuCut.webp", cost: 4, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(71) + 30 flags.cardATK += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardcostdown(4) core.plugin.card.cardAfter() }) } }, Charge: { image: "Charge.webp", cost: 5, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { hero.atk += 5 core.drawFolds("PowerfulAttack") core.drawTip("使用成功") core.plugin.card.cardcostdown(5) core.plugin.card.cardAfter() }) } }, AttackM2: { image: "AttackM2.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(15) + 1 flags.cardATK += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardAfter() }) } }, AttackL2: { image: "AttackL2.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(25) + 6 flags.cardATK += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardAfter() }) } }, AttackG2: { image: "AttackG2.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(36) + 15 flags.cardATK += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardAfter() }) } }, GaWuCut2: { image: "GaWuCut2.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(71) + 30 flags.cardATK += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardAfter() }) } }, Charge2: { image: "Charge2.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { hero.atk += 5 core.drawFolds("PowerfulAttack") core.drawTip("使用成功") core.plugin.card.cardAfter() }) } }, PowerfulAttack: { image: "PowerfulAttack.webp", cost: 3, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { hero.atk += 10 flags.FinalAttack = true core.drawTip("使用成功") core.plugin.card.cardcostdown(3) core.plugin.card.cardAfter() }) } }, DefendM: { image: "DefendM.webp", cost: 1, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(30) + 1 flags.cardDEF += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardcostdown(1) core.plugin.card.cardAfter() }) } }, DefendL: { image: "DefendL.webp", cost: 2, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(55) + 6 flags.cardDEF += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardcostdown(2) core.plugin.card.cardAfter() }) } }, DefendG: { image: "DefendG.webp", cost: 3, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const Result = core.rand(86) + 15 flags.cardDEF += Result core.drawTip("卡牌点数结果:" + Result) core.plugin.card.cardcostdown(3) core.plugin.card.cardAfter() }) } }, LivingBook: { image: "LivingBook.webp", cost: 0, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") return false const tx = flags.touchenemy[0] const ty = flags.touchenemy[1] const id = core.getBlockId(tx, ty) const enemy = core.material.enemys[id] const mark = core.getEnemyInfo(enemy, void 0, tx, ty).mark const LB = flags.LivingBook ?? 2 const card = hero.myCard[index] if (Math.floor(mark / 10) + hero.cost < 2) { core.plugin.card.close() return core.drawTip("活体书页使用失败,cost不足") } core.plugin.card.cardcostdown(2 - Math.min(2, Math.floor(mark / 10))) hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { if (core.hasItem("MagicQuiver") && core.getEnemyInfo(id, void 0, tx, ty).mark > 7) core.drawFolds(core.plugin.card.cardList[core.randcard()]) let todo = [] core.setEnemyOnPoint(tx, ty, void 0, "hp", LB * mark, "-=") if (core.getEnemyInfo(id, void 0, tx, ty).hp <= 0) { let money = core.getEnemyInfo(enemy, void 0, tx, ty).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[tx + "," + ty]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[tx + "," + ty]; core.removeBlock(tx, ty); core.drawTip("目标已击破"); } else { const result = core.rand(10) + 1 core.setEnemyOnPoint(tx, ty, void 0, "mark", result, "+=") core.drawTip("骰子结果:" + result) } flags.LivingBook = (flags.LivingBook ?? 2) + 1 if (todo.length > 0) core.insertAction(todo, void 0, void 0, () => core.plugin.card.cardAfter()); else core.plugin.card.cardAfter() }) }) } }, TalismanFortune: { image: "TalismanFortune.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { hero.hp += 2 * flags.health.count * flags.ratio flags.health.count += 20 core.drawTip("使用成功") core.plugin.card.cardAfter() }) } }, TalismanMisfortune: { image: "TalismanMisfortune.webp", cost: 1, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") return false const tx = flags.touchenemy[0] const ty = flags.touchenemy[1] const id = core.getBlockId(tx, ty) const enemy = core.material.enemys[id] if (enemy.notBomb) return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { if (core.hasItem("MagicQuiver") && core.getEnemyInfo(id, void 0, tx, ty).mark > 7) core.drawFolds(core.plugin.card.cardList[core.randcard()]) let todo = [] core.setEnemyOnPoint(tx, ty, void 0, "hp", flags.health.count, "-=") if (core.getEnemyInfo(id, void 0, tx, ty).hp <= 0) { let money = core.getEnemyInfo(enemy, void 0, tx, ty).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[tx + "," + ty]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[tx + "," + ty]; core.removeBlock(tx, ty); } core.drawTip("使用成功") core.plugin.card.cardcostdown(1) if (todo.length > 0) core.insertAction(todo, void 0, void 0, () => core.plugin.card.cardAfter()); else core.plugin.card.cardAfter() }) }) } }, Railgun: { image: "Railgun.webp", cost: 2, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") return false const tx = flags.touchenemy[0] const ty = flags.touchenemy[1] const id = core.getBlockId(tx, ty) const enemy = core.material.enemys[id] if (enemy.notBomb) return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { if (core.hasItem("MagicQuiver") && core.getEnemyInfo(id, void 0, tx, ty).mark > 7) core.drawFolds(core.plugin.card.cardList[core.randcard()]) let todo = [] const mark = core.getEnemyInfo(id, void 0, tx, ty).mark core.setEnemyOnPoint(tx, ty, void 0, "hp", Math.floor((2 * flags.ratio) ** 0.5 * 75 + mark), "-=") if (core.getEnemyInfo(id, void 0, tx, ty).hp <= 0) { let money = core.getEnemyInfo(enemy, void 0, tx, ty).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[tx + "," + ty]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[tx + "," + ty]; core.removeBlock(tx, ty); } else core.setEnemyOnPoint(tx, ty, void 0, "mark", 5, "+=") core.drawTip("使用成功") core.plugin.card.cardcostdown(2) if (todo.length > 0) core.insertAction(todo, void 0, void 0, () => core.plugin.card.cardAfter()); else core.plugin.card.cardAfter() }) }) } }, Blast: { image: "Blast.webp", cost: 3, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") return false const id = core.getBlockId(flags.touchenemy[0], flags.touchenemy[1]) const enemy = core.material.enemys[id] if (enemy.notBomb) return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) let bool = false core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const block = core.searchBlock(id) let todo = [] block.forEach(v => { const mark = core.getEnemyInfo(id, void 0, v.x, v.y).mark if (core.hasItem("MagicQuiver") && mark > 7) bool = true core.setEnemyOnPoint(v.x, v.y, void 0, "hp", Math.floor((2 * flags.ratio) ** 0.5 * 50 + mark), "-=") if (core.getEnemyInfo(id, void 0, v.x, v.y).hp <= 0) { let money = core.getEnemyInfo(id, void 0, v.x, v.y).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[v.x + "," + v.y]) core.removeBlock(v.x, v.y); } }) if (bool) core.drawFolds(core.plugin.card.cardList[core.randcard()]) core.drawTip("使用成功") core.plugin.card.cardcostdown(3) if (todo.length > 0) core.insertAction(todo, void 0, void 0, () => core.plugin.card.cardAfter()); else core.plugin.card.cardAfter() }) }) } }, LaserBeam: { image: "LaserBeam.webp", cost: 1, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") return false const tx = flags.touchenemy[0] const ty = flags.touchenemy[1] const id = core.getBlockId(tx, ty) const enemy = core.material.enemys[id] if (enemy.notBomb) return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { if (core.hasItem("MagicQuiver") && core.getEnemyInfo(id, void 0, tx, ty).mark > 7) core.drawFolds(core.plugin.card.cardList[core.randcard()]) const mark = core.getEnemyInfo(id, void 0, tx, ty).mark core.setEnemyOnPoint(tx, ty, void 0, "hp", Math.floor((2 * flags.ratio) ** 0.5 * 35 + mark), "-=") let todo = [] if (core.getEnemyInfo(id, void 0, tx, ty).hp <= 0) { let money = core.getEnemyInfo(enemy, void 0, tx, ty).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[tx + "," + ty]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[tx + "," + ty]; core.removeBlock(tx, ty); } else core.setEnemyOnPoint(tx, ty, void 0, "mark", 2, "+=") core.drawTip("使用成功") core.plugin.card.cardcostdown(1) if (todo.length > 0) core.insertAction(todo, void 0, void 0, () => core.plugin.card.cardAfter()); else core.plugin.card.cardAfter() }) }) } }, Berserk: { image: "Berserk.webp", cost: 1, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { flags.cardATK += 3 * flags.ratio flags.cardDEF -= 6 * flags.ratio core.drawTip("使用成功") core.plugin.card.cardcostdown(1) core.plugin.card.cardAfter() }) } }, SelfExplosion: { image: "SelfExplosion.webp", cost: 5, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) if (hero.hp <= 500 * flags.ratio) return core.lose("烟花表演") core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { hero.hp = 1 for (let i = Math.max(hero.loc.x - 3, 0); i <= Math.min(hero.loc.x + 3, 12); i++) { for (let j = Math.max(hero.loc.y - 3, 0); j <= Math.min(hero.loc.y + 3, 12); j++) { core.removeBlock(i, j) if (flags.MapStatus?.Map === "水乡古镇") core.setBgFgBlock("bg", 141, i, j) } } core.drawMap() core.drawTip("使用成功") core.plugin.card.cardcostdown(5) core.plugin.card.cardAfter() }) } }, Brick: { image: "Brick.webp", cost: 4, effect: function (index) { const tx = core.nextX() const ty = core.nextY() if (core.getBlockCls(tx, ty) !== "enemys") return core.unlockControl() const id = core.getBlockId(tx, ty) const enemy = core.material.enemys[id] if (enemy.notBomb) return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { if (core.hasItem("MagicQuiver") && core.getEnemyInfo(id, void 0, tx, ty).mark > 7) core.drawFolds(core.plugin.card.cardList[core.randcard()]) let todo = [] let money = core.getEnemyInfo(enemy, void 0, tx, ty).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.push(todo, enemy.afterBattle); core.push(todo, core.floors[core.status.floorId].afterBattle[tx + "," + ty]) delete((flags.enemyOnPoint || {})[core.status.floorId] || {})[tx + "," + ty]; core.removeBlock(tx, ty); core.drawTip("使用成功") core.plugin.card.cardcostdown(4) if (todo.length > 0) core.insertAction(todo, void 0, void 0, () => core.plugin.card.cardAfter()); else core.plugin.card.cardAfter() }) } }, Firecrackers: { image: "Firecrackers.webp", cost: 1, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const tx = flags.touchenemy[0] const ty = flags.touchenemy[1] if (core.hasItem("MagicQuiver") && core.getEnemyInfo(core.getBlockId(tx, ty), void 0, tx, ty).mark > 7) core.drawFolds(core.plugin.card.cardList[core.randcard()]) core.setEnemyOnPoint(tx, ty, void 0, "special", [], "=") core.drawTip("使用成功") core.plugin.card.cardcostdown(1) core.plugin.card.cardAfter() }) }) } }, Firecrackers2: { image: "Firecrackers2.webp", cost: 0, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchenemy === "failed") return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const tx = flags.touchenemy[0] const ty = flags.touchenemy[1] if (core.hasItem("MagicQuiver") && core.getEnemyInfo(core.getBlockId(tx, ty), void 0, tx, ty).mark > 7) core.drawFolds(core.plugin.card.cardList[core.randcard()]) core.setEnemyOnPoint(tx, ty, void 0, "special", [], "=") core.drawTip("使用成功") core.plugin.card.cardAfter() }) }) } }, CardCollecter: { image: "CardCollecter.webp", cost: 1, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const R1 = core.plugin.card.cardList[core.randcard()] const R2 = core.plugin.card.cardList[core.randcard()] core.drawFolds([R1, R2]) core.drawTip("使用成功") core.plugin.card.cardcostdown(1) core.plugin.card.cardAfter() }) } }, ChocolateCake: { image: "ChocolateCake.webp", cost: 1, effect: function (index) { core.status.event.id = null core.insertAction([{ "type": "choices", "choices": [{ "text": "获得10层治愈", "action": [ { "type": "function", "function": "function(){\nflags.health.count += 10}" }, ] }, { "text": "生命+${50*flag:ratio}", "action": [ { "type": "setValue", "name": "status:hp", "operator": "+=", "value": "50*flag:ratio" }, ] }, ] }], void 0, void 0, () => { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { core.drawTip("使用成功") core.plugin.card.cardcostdown(1) core.plugin.card.cardAfter() }) }) }, }, ChocolateCake2: { image: "ChocolateCake2.webp", cost: 0, effect: function (index) { core.status.event.id = null core.insertAction([{ "type": "choices", "choices": [{ "text": "获得10层治愈", "action": [ { "type": "function", "function": "function(){\nflags.health.count += 10}" }, ] }, { "text": "生命+${50*flag:ratio}", "action": [ { "type": "setValue", "name": "status:hp", "operator": "+=", "value": "50*flag:ratio" }, ] }, ] }], void 0, void 0, () => { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { core.drawTip("使用成功") core.plugin.card.cardAfter() }) }) }, }, Hamburger: { image: "Hamburger.webp", cost: 2, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { core.drawTip("使用成功") core.plugin.card.cardcostdown(2) hero.hp += 200 * flags.ratio flags.health.count += 15 core.plugin.card.cardAfter() }) }, }, Hamburger2: { image: "Hamburger2.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { core.drawTip("使用成功") hero.hp += 200 * flags.ratio flags.health.count += 15 core.plugin.card.cardAfter() }) }, }, FightFirewithFire: { image: "FightFirewithFire.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { hero.hp -= 75 * (flags.FFWF + 1) flags.FFWF++ if (hero.hp <= 0) return core.lose("急火攻心") core.drawTip("使用成功") flags.health.count += 20 core.plugin.card.cardAfter() }) }, }, HurryHurry: { image: "HurryHurry.webp", cost: 5, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchnullblock === "failed") return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { core.setHeroLoc("x", flags.touchnullblock[0]) core.setHeroLoc("y", flags.touchnullblock[1]) core.clearMap("hero") core.drawHero() core.drawTip("使用成功") core.plugin.card.cardcostdown(5) core.plugin.card.cardAfter() }) }) }, }, ShadowAttack: { image: "ShadowAttack.webp", cost: 2, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { flags.ShadowAttack = true core.drawTip("使用成功") core.plugin.card.cardcostdown(2) core.plugin.card.cardAfter() }) }, }, ShadowAttack2: { image: "ShadowAttack2.webp", cost: 0, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { flags.ShadowAttack = true core.drawTip("使用成功") core.plugin.card.cardAfter() }) }, }, SmartDice: { image: "SmartDice.webp", cost: 3, effect: function (index) { core.insertCommonEvent("通用角色点击", void 0, void 0, void 0, function () { if (flags.touchdoor === "failed") return false const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { const loc = [flags.touchdoor[0], flags.touchdoor[1]] core.insertAction({ "type": "openDoor", "loc": loc }, void 0, void 0, core.plugin.card.cardAfter) core.drawTip("使用成功") core.plugin.card.cardcostdown(3) }) }) } }, KingsPower: { image: "KingsPower.webp", cost: 3, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { hero.hp -= 200 * flags.ratio if (hero.hp <= 0) return core.lose("没血就不要开王之力了") flags.KingsPower = 10 core.drawTip("使用成功") core.plugin.card.cardcostdown(3) core.plugin.card.cardAfter() }) }, }, Immovable: { image: "Immovable.webp", cost: 2, effect: function (index) { const card = hero.myCard[index] hero.myCard.splice(Number(index), 1) core.plugin.turncard(core.plugin.card.cardsInfo[card].image, () => { flags.Immovable = 7 core.drawTip("使用成功") core.plugin.card.cardcostdown(2) core.plugin.card.cardAfter() }) }, }, } } close() { //关闭手牌界面 const ctx = core.getContextByName("card") core.clearMap(ctx) core.closePanel() core.plugin.card.selectcard = "" } cardcostdown(cost) { //出牌费用计算 if (flags.MapStatus?.Choose === "Souri") return const k = flags.komachiskill ?? 0 let c = core.hasItem("AncientStaff2") ? Math.max(0, cost - 1) : cost if (k > c) flags.komachiskill = (flags.komachiskill ?? 0) - c else { delete flags.komachiskill c -= k hero.cost -= c } if (hero.name === "Z3000") { hero.ActiveCD -= c if (hero.ActiveCD < 0) hero.ActiveCD = 0 } if (hero.name === "照" && c >= 2) flags.Teru.RankUP++ if (core.hasItem("FoxMask") && c >= 2) { hero.atk += Math.floor(flags.ratio) hero.def += Math.floor(flags.ratio) } } drawcard() { //绘制手牌界面 core.lockControl() let ctx = core.createCanvas("card", 0, 0, 416, 416, 71) core.status.event.id = "card" core.clearMap(ctx) core.fillRect(ctx, 0, 0, 416, 416, "#00000066") core.setTextAlign(ctx, "left") core.fillText(ctx, "这里是手牌界面", 10, 20, "#FFF", "14px cjk") core.fillText(ctx, "手牌上限为8张", 10, 40, "#FFF", "14px cjk") if (core.domStyle.isVertical) core.fillText(ctx, "点击上方卡背关闭", 10, 60, "#FFF", "14px cjk") else core.fillText(ctx, "点击右侧卡背关闭", 10, 60, "#FFF", "14px cjk") core.fillText(ctx, "点击卡牌查看大图", 10, 100, "#FFF", "14px cjk") core.fillText(ctx, "再次点击卡牌使用", 10, 120, "#FFF", "14px cjk") core.fillText(ctx, "或点击卡牌大图使用", 10, 140, "#FFF", "14px cjk") if (flags.komachiskill ?? 0 > 0) core.fillText(ctx, "临时cost剩余:" + flags.komachiskill, 10, 160, "#66FFCC", "14px cjk") if (hero.name === "枫" || hero.name === "美咲" || hero.name === "阿兰娜") core.fillText(ctx, "攻防和:" + (flags.CharacterATK + flags.CharacterDEF), 10, 160, "#66FFCC", "14px cjk") if (hero.name === "南希露") core.fillText(ctx, "累计攻防:" + flags.CharacterATK + "/" + (30 * flags.ratio), 10, 160, "#66FFCC", "14px cjk") if (hero.name === "凛") core.fillText(ctx, "活体书页倍率:" + (flags.LivingBook ?? 2), 10, 160, "#66FFCC", "14px cjk") if (hero.name === "米米") core.fillText(ctx, "被动触发次数:" + (flags.Mimi ?? 0), 10, 160, "#66FFCC", "14px cjk") if (hero.name === "照") core.fillText(ctx, "主动技能伤害:" + Math.floor(core.getRealStatus("atk") * (10 + flags.Teru.RankUP) / 10), 10, 160, "#66FFCC", "14px cjk") if (hero.myCard.length <= 4) { let postX = 208 - 51 * hero.myCard.length for (let i = 0; i < hero.myCard.length; i++) { core.drawImage(ctx, this.cardsInfo[hero.myCard[i]].image, postX, 275, 102, 162) if (i.toString() === this.selectcard) { core.strokeRoundRect(ctx, postX, 275, 102, 162, 15, "#FFFF00", 2) core.drawImage(ctx, this.cardsInfo[hero.myCard[i]].image, 145, 15, 146, 216) } postX += 102 } } else { let postX = 0, dx = 314 / (hero.myCard.length - 1) for (let i = 0; i < hero.myCard.length; i++) { core.drawImage(ctx, this.cardsInfo[hero.myCard[i]].image, postX, 275, 102, 162) if (i.toString() === this.selectcard) { core.strokeRoundRect(ctx, postX, 275, 102, 162, 15, "#FFFF00", 2) core.drawImage(ctx, this.cardsInfo[hero.myCard[i]].image, 145, 15, 146, 216) } postX += dx } } } usecard(index) { // 使用卡牌费用判定 const card = core.plugin.card.cardsInfo[hero.myCard[index]] const a = hero.cost + (flags.komachiskill ?? 0) if (flags.MapStatus?.Choose !== "Souri" && a < (core.hasItem("AncientStaff2") ? Math.max(0, card.cost - 1) : card.cost)) { core.drawTip("出牌费用不足") return } const ctx = core.getContextByName("card") core.clearMap(ctx) core.status.event.id = null core.plugin.card.selectcard = "" card.effect(index) core.status.route.push("card:" + index) } cardAfter() { //卡牌效果执行完毕后操作 //忍术飞镖 if (core.hasItem("NinjaThrowingDarts")) { ["greenSlime", "redSlime", "blackSlime", "slimelord", "slimeMan", "bat", "bigBat", "redBat", "vampire", "zombie", "zombieKnight", "skeleton", "skeletonSoilder", "skeletonCaptain", "ghostSkeleton", "bluePriest", "redPriest", "brownWizard", "redWizard", "blackKing", "whiteKing", "blackMagician", "swordsman", "soldier", "yellowKnight", "redKnight", "darkKnight", "rock", "yellowGuard", "blueGuard", "redGuard", "redKing", "swordEmperor", "Thief", "SharkPirate", "KaniBro", "Loudmouth", "Trainee", "GoldenFish", "EbiBro"].forEach(v => flags.race[v] = 1 + (flags.race[v] ?? 0)) } //可口糖果 if (core.hasItem("Candy")) { flags.health.count += 10 if (flags.health.count >= 100) { flags.health.count -= 100 if (hero.cost < hero.costmax) hero.cost++ } } //角色技能处理 const Character = core.plugin.Character[hero.name] if (Character && Character.PassiveTime.includes("cardAfter")) { Character.Passive() } core.updateStatusBar() core.plugin.card.close() if (flags.MapStatus?.Choose === "Souri") { // 帮助鲤鱼不消耗费用,改为前置计算并扣除血量 hero.hp -= flags.出牌计数器 * 10 * flags.ratio core.drawTip("因地图效果,失去" + flags.出牌计数器 * 10 * flags.ratio + "生命") flags.出牌计数器++ if (hero.hp <= 0) return core.lose("长眠的牌者") } } drawcardShop() { //绘制卡牌商店,刷新卡牌商店内卡牌操作在此 core.lockControl() let ctx = core.getContextByName("cardShop") if (!ctx) { ctx = core.createCanvas("cardShop", 0, 0, 416, 416, 140) ctx.imageSmoothingEnabled = false } if (!main.replayChecking && !core.isReplaying()) core.status.event.id = "cardShop" core.playBgm("AstralShop.mp3") core.clearMap(ctx) core.fillRect(ctx, 0, 32, 416, 48, "#000") core.setTextAlign(ctx, "center") if (flags.MapStatus?.Map !== "魔法学院") core.fillText(ctx, "选择你要购买的卡牌,每张售价60", 208, 63, "#FFF", "20px STHUPO") else { const price = 40 * (flags.ratio ** 2) core.fillText(ctx, "选择你要购买的卡牌,每张售价" + price, 208, 63, "#FFF", "20px STHUPO") } if (!flags.cardShop) { const C1 = core.plugin.card.cardList[core.randcard()], C2 = core.plugin.card.cardList[core.randcard()], C3 = core.plugin.card.cardList[core.randcard()]; flags.cardShop = [C1, C2, C3] } for (let i = 0; i < 3; i++) { const a = flags.cardShop[i] if (!a) core.drawImage(ctx, "card.png", 35 + 122 * i, 135, 102, 162) else core.drawImage(ctx, a + ".webp", 35 + 122 * i, 135, 102, 162) } core.fillRoundRect(ctx, 44, 320, 120, 40, 22, "#EEEE00") core.fillRoundRect(ctx, 252, 320, 120, 40, 22, "#777777") if ((flags.cardShop[0] !== null || flags.cardShop[1] !== null || flags.cardShop[2] !== null) && flags.MapStatus?.Map !== "魔法学院") core.fillText(ctx, "刷新(" + (10 + flags.rollcard) + "星币)", 104, 345, "#000000", "14px STHUPO") else if (flags.cardShop[0] !== null || flags.cardShop[1] !== null || flags.cardShop[2] !== null) { const price = (5 + flags.rollcard) * flags.ratio core.fillText(ctx, "刷新(" + price + "星币)", 104, 345, "#000000", "14px STHUPO") } else core.fillText(ctx, "免费刷新", 104, 345, "#000000", "14px STHUPO") core.fillText(ctx, "关 闭", 312, 345, "#FFFFFF", "14px STHUPO") } closeCardshop() { //关闭卡牌商店 const ctx = core.getContextByName("cardShop") core.clearMap(ctx) core.closePanel() core.playBgm(core.floors[core.status.floorId].bgm) } buyCard(Num) { //购买卡牌,同步记录至录像 const price = flags.MapStatus?.Map === "魔法学院" ? (40 * (flags.ratio ** 2)) : 60 switch (Num) { case 1: hero.money -= price core.drawFolds(flags.cardShop[0]) flags.cardShop[0] = null core.plugin.card.drawcardShop() break; case 2: hero.money -= price core.drawFolds(flags.cardShop[1]) flags.cardShop[1] = null core.plugin.card.drawcardShop() break; case 3: hero.money -= price core.drawFolds(flags.cardShop[2]) flags.cardShop[2] = null core.plugin.card.drawcardShop() break; case 0: if (flags.cardShop[0] !== null || flags.cardShop[1] !== null || flags.cardShop[2] !== null) { if (flags.MapStatus?.Map !== "魔法学院") hero.money -= 10 + flags.rollcard else hero.money -= (5 + flags.rollcard) * flags.ratio flags.rollcard++ } else flags.rollcard = 0 delete flags.cardShop core.plugin.card.drawcardShop() break; case 7: core.plugin.card.closeCardshop() core.unlockControl() break; } core.updateStatusBar() core.status.route.push("cardShop:" + Num) } clickCardshop(x, y, px, py) { //卡牌商店点击 if (core.status.event.id !== "cardShop") return const makeBox = ([x, y], [w, h]) => { return [ [x, y], [x + w, y + h], ]; }; const inRect = ([x, y], [ [sx, sy], [dx, dy] ]) => { return sx <= x && x <= dx && sy <= y && y <= dy; }; const pos = [px, py]; const box1 = makeBox([35, 135], [102, 162]), box2 = makeBox([157, 135], [102, 162]), box3 = makeBox([279, 135], [102, 162]), flush = makeBox([44, 320], [120, 40]), close = makeBox([252, 320], [120, 40]), price = flags.MapStatus?.Map === "魔法学院" ? (40 * (flags.ratio ** 2)) : 60; if (inRect(pos, box1) && flags.cardShop[0]) { if (hero.money < price) core.drawTip("无法购买卡牌") else core.plugin.card.buyCard(1) } if (inRect(pos, box2) && flags.cardShop[1]) { if (hero.money < price) core.drawTip("无法购买卡牌") else core.plugin.card.buyCard(2) } if (inRect(pos, box3) && flags.cardShop[2]) { if (hero.money < price) core.drawTip("无法购买卡牌") else core.plugin.card.buyCard(3) } if (inRect(pos, flush)) { const p2 = flags.MapStatus?.Map === "魔法学院" ? ((5 + flags.rollcard) * flags.ratio) : (10 + flags.rollcard); if (hero.money < p2 && !(flags.cardShop[0] === null && flags.cardShop[1] === null && flags.cardShop[2] === null)) core.drawTip("无法刷新卡牌") else core.plugin.card.buyCard(0) } if (inRect(pos, close)) { core.plugin.card.buyCard(7) } return true } click(x, y, px, py) { //手牌界面点击 if (core.status.event.id !== "card") return if (px >= 145 && px <= 291 && py >= 15 && py <= 231 && core.plugin.card.selectcard != "") { core.plugin.card.usecard(core.plugin.card.selectcard) return true } if (py >= 275) { if (hero.myCard.length <= 4) { if (px <= 208 + 51 * hero.myCard.length && px >= 208 - 51 * hero.myCard.length) if (core.plugin.card.selectcard === Math.floor((px - 208 + 51 * hero.myCard.length) / 102).toString()) { core.plugin.card.usecard(core.plugin.card.selectcard) } else { core.plugin.card.selectcard = Math.floor((px - 208 + 51 * hero.myCard.length) / 102).toString() core.plugin.card.drawcard() } } else { if (px < 314) { if (core.plugin.card.selectcard === Math.floor(px / (314 / (hero.myCard.length - 1))).toString()) { core.plugin.card.usecard(core.plugin.card.selectcard) } else { core.plugin.card.selectcard = Math.floor(px / (314 / (hero.myCard.length - 1))).toString() core.plugin.card.drawcard() } } else { if (core.plugin.card.selectcard === (hero.myCard.length - 1).toString()) { core.plugin.card.usecard(core.plugin.card.selectcard) } else { core.plugin.card.selectcard = (hero.myCard.length - 1).toString() core.plugin.card.drawcard() } } } } return true } } core.plugin.card = new Card() //录像注册(使用手牌/卡牌商店) //使用手牌操作:记录使用的第几张手牌 //卡牌商店购买/刷新操作:记录所购买对应的卡牌以及刷新 core.registerAction("ondown", "card", core.plugin.card.click, 50) core.registerReplayAction("card", (action) => { if (action.indexOf("card:") !== 0) return false let a = action.substring(5) if (!hero.myCard[a]) return false core.plugin.card.usecard(a) core.updateStatusBar() core.replay() return true }) core.registerAction("ondown", "cardShop", core.plugin.card.clickCardshop, 50) core.registerReplayAction("cardShop", (action) => { if (action.indexOf("cardShop:") !== 0) return false let a = Number(action.substring(9)) if (a !== 0 && a !== 7 && !flags.cardShop[a - 1]) return false core.plugin.card.buyCard(a) core.updateStatusBar() core.replay() return true }) }, "canvas转webp": function () { // 在此增加新插件 this.CanvastoWebp = function (name, canvas) { const webpDataURL = canvas.toDataURL("image/webp", 1); //第二个参数为画面质量,范围为0-1,1为无损 const link = document.createElement("a"); link.href = webpDataURL; link.download = name + ".webp"; link.click(); }; }, "隐身穿怪": function () { core.stealthOn = function () { core.setFlag('no_ambush', true) if (core.status.thisMap) { var floorCache = core.getFloorCache() var bs = core.status.thisMap.blocks for (var i in bs) { if (bs[i].event && bs[i].event.cls.indexOf('enemy') == 0) { bs[i].event.noPass = false if (floorCache.enemys[bs[i].x + ',' + bs[i].y] && floorCache.enemys[bs[i].x + ',' + bs[i].y].specialName) { bs[i].event.noPass = true } } } } core.setFlag('__heroOpacity__', 0.5) core.setStatus('isStealth', true) core.drawHero() core.updateStatusBar() } core.stealthOff = function () { core.removeFlag('no_ambush') if (core.status.thisMap) { var bs = core.status.thisMap.blocks for (var i in bs) { if (bs[i].event && bs[i].event.cls.indexOf('enemy') == 0) { bs[i].event.noPass = true } } } core.setStatus('isStealth', false) core.setFlag('__heroOpacity__', 1) core.drawHero() core.updateStatusBar() } ///////楼层缓存/////////// this.getFloorCache = function () { if (core.getStatus('floorCache') == null) { var cache = { enemys: {} } var floorId = core.status.floorId cache.floorId = floorId core.status.maps[floorId].blocks.forEach(function (block) { if (core.isset(block.event) && !block.disable) { // 获得该图块的ID var id = block.event.id, __enemy = core.material.enemys[id] // 检查是不是怪物,且是否拥有该特殊属性 if (core.isset(__enemy)) { var temp = { id: id } temp.special = __enemy.special temp.specialName = __enemy.specialName cache.enemys[block.x + ',' + block.y] = temp } } }) core.setStatus('floorCache', cache) } return core.getStatus('floorCache') } }, "翻牌动画": function () { // 在此增加新插件 const { Animation, Ticker, Transition, sleep, circle, bezierPath, linear, bezier, trigo, power, hyper, inverseTrigo, shake } = core.plugin.animate this.turncard = async function (image, callback) { core.lockControl() let ctx = core.createCanvas("filpCard", 0, 0, 416, 416, 71) if (!main.replayChecking && !core.isReplaying()) { flags.imgcard = "card.png" const ani = new Animation() const fn = () => { core.clearMap(ctx) core.drawImage(ctx, flags.imgcard, 123 + ani.x, 73, 170 - 2 * ani.x, 270) } if (!core.plugin.event.eventList.includes(image.substring(image.length - 5, 0)) && core.getLocalStorage("出牌动画开关")) return callback?.() flags.cardlock = true let timex = 1000 if (core.plugin.event.eventList.includes(image.substring(image.length - 5, 0))) timex = core.getLocalStorage("事件等待", 30) * 100 else timex = core.getLocalStorage("出牌等待", 10) * 100 ani.ticker.add(fn) ani.time(250).absolute().mode(linear()).move(85, 0) await sleep(250) flags.imgcard = image ani.time(250).absolute().mode(linear()).move(0, 0) await sleep(timex) ani.ticker.destroy() } core.clearMap(ctx) callback?.() delete flags.cardlock } class Battle { constructor() { this.mode = "attack" const batt = document.createElement("canvas"); //背景画布设置 batt.style.position = "absolute"; batt.style.zIndex = 200; batt.id = "batt"; main.dom.batt = batt; main.dom.startPanel.insertAdjacentElement("afterend", batt); batt.style.display = "none" batt.style.top = "50%"; batt.style.left = "50%"; batt.style.transform = "translate(-50%,-50%)"; this.heroInfo = {} this.bossInfo = {} this.enemysprite = { vampire: "vampire_sprite.png", redKing: "redKing_sprite.png", octopus: "octopus_sprite.png", magicDragon: "magicDragon_sprite.png", swordEmperor: "redKing_sprite.png", } } start() { if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(main.dom.batt, 676, 416) } else { core.maps._setHDCanvasSize(main.dom.batt, 416, 676) } main.dom.batt.style.display = "block" core.lockControl() for (;;) { this.draw_attack() if (this.bossInfo.hp <= 0) return this.success() if (this.heroInfo.hp <= 0) return this.fail() } } success() { main.dom.batt.style.display = "none" core.unlockControl() } fail() { main.dom.bat.style.display = "none" core.lose("特殊战失败") } draw_attack() { main.dom.batt.style.display = "block" const ctx = main.dom.batt.getContext("2d") if (core.domStyle.isVertical) { ctx.save(); //保存设置 ctx.translate(416, 0); //重新定位右上角为基准 ctx.rotate(Math.PI / 2); //旋转90度 } else { ctx.save() } core.drawImage(ctx, core.plugin.Character[hero.name].sprite, 150, 150, 112, 152) //绘制勇者sprite形象 core.drawImage(ctx, core.plugin.SPbattle.enemysprite[enemy]) //绘制怪物立绘 core.drawImage(ctx, "SPAttack.webp") //绘制攻击边框,坐标为定值 core.drawImage(ctx, "SPDefense.webp") //绘制防御边框,坐标为定值 ctx.restore() } } core.plugin.SPbattle = new Battle() this.tiaozi = function (neirong) { //跳字 if (core.status.replay.replaying) {} else { var shangsheng = 0; var gaodu = 0; var tiaozizhong = 0; var herox = core.status.hero.loc.x, heroy = core.status.hero.loc.y; var name = "tiaozi" + Math.floor(Math.random() * 1000000); //如不需要连续跳字可不加随机数 core.createCanvas(name, herox * 32 - 16, (heroy - 1) * 32 + gaodu, 96, 64, 74); core.setTextAlign(name, "center") core.fillText(name, neirong, 32, 32, "green", "17px cjk"); var fade = setInterval(function () { if (gaodu <= 12) { gaodu = gaodu - 1; if (gaodu == -12) { tiaozizhong = 1; } }; core.relocateCanvas(name, herox * 32 - 16, (heroy - 1) * 32 + gaodu); if (tiaozizhong == 1) { delete core.animateFrame.asyncId[fade]; clearInterval(fade); core.deleteCanvas(name); } }, 16); core.animateFrame.asyncId[fade] = true; } } }, "Events": function () { // 在此增加新插件 class Event { constructor() { this.eventList = ["Haste", "FoodSafety", "MyGoddess", "NoEvent", "CardDestruction", "ReliefArrives", "FallingGifts", "NoEvent", "Thunderstormapproaching", "Hightemperaturewarning", "RacoonAppears", "NoEvent", "BUG", "JACKPOT", "Doubled", "NoEvent", "Half", "Haste", "FoodSafety", "NoEvent", "MyGoddess", "CardDestruction", "ReliefArrives", "ItsWar", "NoEvent", "Thunderstormapproaching", "Hightemperaturewarning", "RacoonAppears", "ItsWar", "NoEvent", "JACKPOT", "Half", "Haste", "NoEvent", "FoodSafety", "Hightemperaturewarning", "CardDestruction", "NoEvent"] this.evspecial1 = [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 27, void 0, 29, 29] this.evspecial2 = { "星趴·梦想号": [1, 2, 3, 4, 5, 7, 8, 10, 11, 15, 22, 27, 17, 29], "御魂庆典": [1, 2, 3, 4, 5, 7, 8, 10, 11, 15, 22, 27, 30, 29], "水乡古镇": [1, 2, 3, 4, 5, 7, 8, 10, 11, 15, 22, 27, 28, 29], "魔法学院": [1, 2, 3, 4, 5, 7, 8, 10, 11, 15, 22, 27, 32, 29], "龙宫游乐园": [1, 2, 3, 4, 5, 7, 8, 10, 11, 15, 22, 27, 29], } this.evspecial3 = { "星趴·梦想号": [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 27, void 0, 17, void 0, 29], "御魂庆典": [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 27, void 0, 30, void 0, 29], "水乡古镇": [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 27, void 0, 28, void 0, 29], "魔法学院": [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 27, void 0, 32, void 0, 29], "龙宫游乐园": [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 27, void 0, 29], } this.evBoss = [] this.eventInfo = { Crowd: function (callback) { core.plugin.turncard("Crowd.webp", () => { let blocks = [] let nullpos = [] const floorId = core.status.floorId, maps = flags.MapStatus?.Map, Hard = flags.MapStatus?.Hard; let special1 let special2 if (Hard === "噩梦") { special1 = core.plugin.event.evspecial1 } else if (Hard === "疯狂") { special1 = core.plugin.event.evspecial2[maps] special2 = core.plugin.event.evspecial3[maps] } for (let i = 0; i < 13; i++) { for (let j = 0; j < 13; j++) { const id = core.maps.getBlockId(i, j) if (!id) nullpos.push([i, j]) } } const mapBlock = core.cloneArray(core.floors[floorId].map) for (let i = 0; i < 13; i++) { for (let j = 0; j < 13; j++) { const num = mapBlock[i][j], block2 = core.getBlockByNumber(num) if (!blocks.includes(num) && ["items", "enemys"].includes(block2.event.cls) && ![208, 211, 226, 245, 247, 249, 257, 258, 63, 421].includes(num)) blocks.push(block2.event.id) } } nullpos.forEach(v => { if (blocks.length === 0) return const id = blocks[core.rand(blocks.length)] core.setBlock(id, v[0], v[1]) if (id.endsWith("Gem2")) flags.gemRandom[v[0] + "-" + v[1] + "-" + floorId] = core.rand(3) + 1 else if (id.endsWith("Gem3")) flags.gemRandom[v[0] + "-" + v[1] + "-" + floorId] = core.rand(6) + 1 else if (id.endsWith("Gem4")) flags.gemRandom[v[0] + "-" + v[1] + "-" + floorId] = core.rand(10) + 1 const cls = core.getBlockById(id).event.cls if (cls === "enemys") { const special = core.getEnemyValue(id, "special", v[0], v[1], floorId) const b = [...special] if (special1) { const rand1 = core.rand(special1.length) const a = special1[rand1] if (a) b.push(special1[rand1]) } if (special2) { const rand2 = core.rand(special2.length) const a = special2[rand2] if (a) b.push(special2[rand2]) } core.setEnemyOnPoint(v[0], v[1], floorId, "special", b, "=") } }) core.plugin.event.afterEvent() if (callback) callback() }) }, MyGoddess: function (callback) { core.plugin.turncard("MyGoddess.webp", () => { hero.hp += 50 * flags.ratio flags.health.count += 10 core.plugin.event.afterEvent() if (callback) callback() }) }, FoodSafety: function (callback) { core.plugin.turncard("FoodSafety.webp", () => { hero.hp -= 50 * flags.ratio if (hero.hp <= 0) return core.lose("吃毒外卖升天") flags.health.count = 0 core.plugin.event.afterEvent() if (callback) callback() }) }, CardDestruction: function (callback) { core.plugin.turncard("CardDestruction.webp", () => { if (hero.myCard.length !== 0) { const length = hero.myCard.length; const halfLength = Math.ceil(length / 2); for (let i = length - 1; i > 0; i--) { const j = core.rand(i + 1); [hero.myCard[i], hero.myCard[j]] = [hero.myCard[j], hero.myCard[i]]; } hero.myCard = hero.myCard.slice(0, halfLength); } core.plugin.event.afterEvent() if (callback) callback() }) }, Hightemperaturewarning: function (callback) { core.plugin.turncard("Hightemperaturewarning.webp", () => { flags.HOT = true core.plugin.event.afterEvent() if (callback) callback() }) }, FallingGifts: function (callback) { core.plugin.turncard("FallingGifts.webp", () => { hero.money += 25 * flags.ratio if (hero.myCard.length < 8) core.drawFolds(core.plugin.card.cardList[core.randcard()]) core.plugin.event.afterEvent() if (callback) callback() }) }, JACKPOT: function (callback) { core.plugin.turncard("JACKPOT.webp", () => { hero.cost = hero.costmax core.drawFolds([core.plugin.card.cardList[core.randcard()], core.plugin.card.cardList[core.randcard()], core.plugin.card.cardList[core.randcard()]]) core.plugin.event.afterEvent() if (callback) callback() }) }, Haste: function (callback) { core.plugin.turncard("Haste.webp", () => { flags.Haste = true core.plugin.event.afterEvent() if (callback) callback() }) }, Thunderstormapproaching: function (callback) { core.plugin.turncard("Thunderstormapproaching.webp", () => { let enemys = [] const floorId = core.status.floorId for (let i = 0; i < 13; i++) { for (let j = 0; j < 13; j++) { if (core.getBlockCls(i, j) === "enemys") enemys.push([i, j]) } } enemys.forEach(v => { const id = core.getBlockId(v[0], v[1]) const special = core.getEnemyValue(id, "special", v[0], v[1], floorId) const b = [...special] const specialList = [1, 2, 3, 4, 5, 7, 8, 10, 11, 15, 22, 27] const a = specialList[core.rand(12)] b.push(a) core.setEnemyOnPoint(v[0], v[1], floorId, "special", b, "=") }) core.plugin.event.afterEvent() if (callback) callback() }) }, ReliefArrives: function (callback) { core.plugin.turncard("ReliefArrives.webp", () => { let enemys = [] const floorId = core.status.floorId for (let i = 0; i < 13; i++) { for (let j = 0; j < 13; j++) { if (core.getBlockCls(i, j) === "enemys") enemys.push([i, j]) } } enemys.forEach(v => { core.setEnemyOnPoint(v[0], v[1], floorId, "special", [], "=") }) core.plugin.event.afterEvent() if (callback) callback() }) }, BUG: function (callback) { core.plugin.turncard("BUG.webp", () => { let nullblock = [] for (let i = 0; i < 13; i++) { for (let j = 0; j < 13; j++) { if (core.getBlockId(i, j, void 0, true) === null) nullblock.push([i, j]) } } const L = core.rand(nullblock.length) const T = nullblock[L] core.setHeroLoc("x", T[0]) core.setHeroLoc("y", T[1]) core.clearMap("hero") core.drawHero() core.drawTip("你还好吗?我们刚刚抢修了一个BUG") core.plugin.event.afterEvent() if (callback) callback() }) }, RacoonAppears: function (callback) { core.plugin.turncard("RacoonAppears.webp", () => { let enemys = [] const floorId = core.status.floorId for (let i = 0; i < 13; i++) { for (let j = 0; j < 13; j++) { if (core.getBlockCls(i, j) === "enemys") enemys.push([i, j]) } } const E1 = enemys[core.rand(enemys.length)] const E2 = enemys[core.rand(enemys.length)] if (E1 !== undefined) { [E1, E2].forEach(v => { const id = core.getBlockId(v[0], v[1]) const special = core.getEnemyValue(id, "special", v[0], v[1], floorId) const b = [...special] b.push(29) core.setEnemyOnPoint(v[0], v[1], floorId, "special", b, "=") }) } core.plugin.event.afterEvent() if (callback) callback() }) }, Doubled: function (callback) { core.plugin.turncard("Doubled.webp", () => { const money = Math.min(Math.floor(hero.money / 5), 200 * flags.ratio) hero.money += money core.plugin.event.afterEvent() if (callback) callback() }) }, Half: function (callback) { core.plugin.turncard("Half.webp", () => { const money = Math.min(Math.floor(hero.money / 5), 200 * flags.ratio) hero.money -= money core.plugin.event.afterEvent() if (callback) callback() }) }, ItsWar: function (callback) { core.plugin.turncard("ItsWar.webp", () => { const deck = ["AttackM2", "AttackM2", "AttackM2", "AttackM2", "AttackM2", "AttackL2", "AttackL2", "AttackL2", "AttackL2", "AttackG2", "AttackG2", "AttackG2", "Charge2", "GaWuCut2", "GaWuCut2", "ShadowAttack2", "ShadowAttack2", "ShadowAttack2"] core.drawFolds([deck[core.rand(deck.length)], deck[core.rand(deck.length)]]) core.plugin.event.afterEvent() if (callback) callback() }) }, NoEvent: function (callback) { core.plugin.turncard("NoEvent.webp", () => { core.plugin.event.afterEvent() if (callback) callback() }) } } } afterEvent() { const Character = core.plugin.Character[hero.name] if (Character && Character.PassiveTime.includes("Event")) { Character.Passive() } core.updateStatusBar() core.closePanel() } doEvent(event, callback) { core.plugin.event.eventInfo[event](callback) } } core.plugin.event = new Event() }, "Gallery": function () { // 在此增加新插件 class Gallery { constructor() { this.mode = "token" this.selectiontoken = 0 this.tokenpage = 0 this.cardpage = 0 this.cardList = ["AttackM", "AttackL", "AttackG", "DefendM", "DefendL", "DefendG", "GaWuCut", "Charge", "PowerfulAttack", "Railgun", "Blast", "LaserBeam", "Berserk", "Brick", "Firecrackers", "CardCollecter", "ChocolateCake", "Hamburger", "HurryHurry", "ShadowAttack", "SmartDice", "KingsPower", "Immovable", "FightFirewithFire", "SelfExplosion", "LivingBook", "TalismanFortune", "TalismanMisfortune", "Haste", "FoodSafety", "MyGoddess", "CardDestruction", "ReliefArrives", "FallingGifts", "Thunderstormapproaching", "Hightemperaturewarning", "RacoonAppears", "BUG", "JACKPOT", "Doubled", "Half", "ItsWar", "NoEvent", "NoEvent", "NoEvent", "NoEvent", "NoEvent", "BattleAttackM", "BattleAttackL", "BattleAttackG", "BattleDefendM", "BattleDefendL", "BattleDefendG", "BattleGaWuCut", "BattleCharge", "BattlePowerfulAttack", ] this.CharacterList = ["勇者", "米米", "帕露南", "芬妮", "阿兰娜", "小町", "派德曼", "帕帕拉", "恋", "Z3000", "潘大猛", "璐璐", "枫", "蓝海晴", "美咲", "娜蒂斯", "茉莉", "星魅琉华", "南希露", "梅加斯", "姬梦朝", "阿尔", "凛", "照" ] this.characterpage = 0 this.selectioncard = "" this.selectioncharacter = 0 this.selectionmode = 0 this.mytoken = false this.mapmode = 0 this.mapstatuspage = 0 this.gametextpage = 0 this.ACVpage = 0 this.MapInfo = { "星趴·梦想号": `体验50层魔塔的传统地图\n\\i[stars]取得方法:抵达11/21/31/41楼\n召唤\\i[SeaGawu]方法:持有\\i[pickaxe]或\\i[earthquake]其中一种和\\i[downFly]的情况下击败50F的魔王`, "御魂庆典": `将50层魔塔进行随机打乱并重组,带来不一样的新体验\n\\i[stars]取得方法:抵达11/21/31/41楼\n召唤\\i[Masao]方法:持有\\i[pickaxe]或\\i[earthquake]其中一种和\\i[downFly]的情况下击败50F的魔王`, "水乡古镇": `我如果不说你能知道这里的原型是50层魔塔吗?将50层魔塔压缩至只有21层大小,并进行平面连通\n也许能发现许多快速过关的技巧\n\\i[stars]取得方法:击败\\i[skeletonCaptain]\\i[vampire]\\i[yellowKnight]\\i[blackMagician]\n召唤\\i[LionGawu]方法:持有\\i[superPotion]但未持有\\i[coin]的情况下击败5-3的魔王(需要消耗\\i[superPotion])`, "魔法学院": `由主楼和图书馆构成的双子塔。受魔法因子影响,图书馆里的部分道具变稀有了\n但击败图书馆的怪物后塔内相同种族的怪物都会被强化(包括击败此怪物后获得的星币)\n\\i[stars]取得方法:主楼:抵达11/21/31/41楼。图书馆:击败\\i[skeletonCaptain]\\i[vampire]\\i[yellowKnight]\\i[blackMagician]\\i[redKing]\n召唤雅央的方法:持有\\i[pickaxe]或\\i[earthquake]其中一种和\\i[downFly]的情况下击败主楼50F的魔王\n\\r[#FFFF00]警告!!!你将同时面对\\i[ADMasao]\\i[SOMasao]没有十足的把握请勿轻易召唤\\r`, "龙宫游乐园": `一张地图两种完全不同的路线\n这次你将在\\i[Souri]\\i[Mamushi]之中选择一位帮助她,同时直接迎战另一位\n\\i[stars]取得方法:抵达特定区域和击败\\i[blackMagician]` } this.HardInfo = { "星趴·梦想号": { "普通": `\r[#00FFFF]商店价格:20\r`, "困难": `\r[#FFFF00]商店价格:25\r \r[#FFFF00]道具置换:\\i[knife]→\\i[cardbag] 怪物增幅:10% 其他:6F以上每层首次到达抽取随机事件卡(21~29F除外)\r`, "噩梦": `\r[#FF00FF]商店价格:30\r \r[#FF00FF]道具置换:\\i[knife]\\i[bomb]→\\i[cardbag] 怪物增幅:30%\r \r[#FFFF00]其他:6F以上每层首次到达抽取随机事件卡(21~29F除外)\r \r[#FF00FF]31F开始怪物有概率获得特殊属性\r`, "疯狂": `\r[#FF0000]商店价格:35\r 道具置换:\\i[knife]\\i[bomb]\\i[cross]→\\i[cardbag] 怪物增幅:50%\r \r[#FFFF00]其他:6F以上每层首次到达抽取随机事件卡(21~29F除外)\r \r[#FF0000]11F开始除BOSS外的怪物都获得0~2个特殊属性 关卡专属特殊属性-亡语:被击杀后同楼层其他怪物增强\r`, }, "御魂庆典": { "普通": `\r[#00FFFF]商店价格:20\r`, "困难": `\r[#FFFF00]商店价格:25\r \r[#FFFF00]道具置换:\\i[knife]→\\i[cardbag] 怪物增幅:10% 其他:10F以上每层首次到达抽取随机事件卡(21~29F除外)\r`, "噩梦": `\r[#FF00FF]商店价格:30\r \r[#FF00FF]道具置换:\\i[knife]\\i[bomb]→\\i[cardbag] 怪物增幅:30%\r \r[#FFFF00]其他:10F以上每层首次到达抽取随机事件卡(21~29F除外)\r \r[#FF00FF]31F开始怪物有概率获得特殊属性\r`, "疯狂": `\r[#FF0000]商店价格:35\r 道具置换:\\i[knife]\\i[bomb]\\i[cross]→\\i[cardbag] 怪物增幅:50%\r \r[#FFFF00]其他:10F以上每层首次到达抽取随机事件卡(21~29F除外)\r \r[#FF0000]11F开始除BOSS外的怪物都获得0~2个特殊属性 关卡专属特殊属性-激怒:无视\\i[BuffMark],同楼层带有\\i[BuffMark]的怪物被击杀后自身属性提升\r`, }, "水乡古镇": { "普通": `\r[#00FFFF]商店价格:20\r`, "困难": `\r[#FFFF00]商店价格:25 道具置换:\\i[knife]→\\i[cardbag] 怪物增幅:10% 其他:除初始区域外每个区域首次到达抽取随机事件卡\r`, "噩梦": `\r[#FF00FF]商店价格:30\r \r[#FF00FF]道具置换:\\i[knife]\\i[bomb]→\\i[cardbag] 怪物增幅:30%\r \r[#FFFF00]其他:除初始区域外每个区域首次到达抽取随机事件卡\r \r[#FF00FF]这些怪物有概率获得特殊属性:\\i[slimeMan]\\i[ghostSkeleton]\\i[blueGuard]\\i[redGuard]\\i[brownWizard]\\i[redWizard]\\i[swordsman]\\i[soldier]\\i[yellowKnight]\\i[redKnight]\\i[darkKnight]\\i[whiteKing]\\i[slimelord]\\i[redBat]\r`, "疯狂": `\r[#FF0000]商店价格:35\r 道具置换:\\i[knife]\\i[bomb]\\i[cross]→\\i[cardbag] 怪物增幅:50%\r \r[#FFFF00]其他:除初始区域外每个区域首次到达抽取随机事件卡\r \r[#FF0000]这些怪物获得0~2个特殊属性:\\i[slimeMan]\\i[ghostSkeleton]\\i[blueGuard]\\i[redGuard]\\i[brownWizard]\\i[redWizard]\\i[swordsman]\\i[soldier]\\i[yellowKnight]\\i[redKnight]\\i[darkKnight]\\i[whiteKing]\\i[slimelord]\\i[redBat]\\i[blackSlime]\\i[bigBat]\\i[zombie]\\i[zombieKnight]\\i[vampire]\\i[rock] 关卡专属特殊属性-爆竹呱呱:同区域其他怪物被击败后,对玩家造成怪物\\i[atk]的10%+玩家\\i[BuffHealth]的伤害\r`, }, "魔法学院": { "普通": `\r[#00FFFF]商店价格:8\r`, "困难": `\r[#FFFF00]商店价格:10 道具置换:\\i[knife]→\\i[cardbag] 怪物增幅:10% 其他:主楼6F以上及图书馆每层首次到达抽取随机事件卡(21~29F和连廊除外)\r`, "噩梦": `\r[#FF00FF]商店价格:12\r \r[#FF00FF]道具置换:\\i[knife]\\i[bomb]→\\i[cardbag] 怪物增幅:30%\r \r[#FFFF00]其他:主楼6F以上及图书馆每层首次到达抽取随机事件卡(21~29F和连廊除外)\r \r[#FF00FF]31F开始怪物有概率获得特殊属性\r`, "疯狂": `\r[#FF0000]商店价格:14 道具置换:\\i[knife]\\i[bomb]\\i[cross]→\\i[cardbag] 怪物增幅:50%\r \r[#FFFF00]其他:主楼6F以上及图书馆每层首次到达抽取随机事件卡(21~29F和连廊除外)\r \r[#FF0000]11F开始怪物获得0~2个特殊属性 关卡专属特殊属性-茶壶:只在图书馆刷新,被击败后玩家随机回复不超过本次受到伤害的生命值 主楼BOSS专属特殊属性-甜点师:首次抵达BOSS楼层时,吸收图书馆对应10层的茶壶。每吸收1只增强自身10%的属性\r`, }, "龙宫游乐园": { "普通": `经验获取率:1倍。商店价格:16`, "困难": `经验获取率:2倍。商店价格:20\n道具置换:\\i[superPotion]→\\i[cardbag]\n怪物增幅:10%\n其他:完成选择后:\n左/右侧地区每个区域首次到达抽取事件卡`, "噩梦": `经验获取率:4倍。商店价格:24\n道具置换:\\i[superPotion]\\i[bomb]→\\i[cardbag]\n怪物增幅:30%\n其他:完成选择后:\n左/右侧地区每个区域首次到达抽取事件卡\n所有怪物有概率获得特殊属性`, "疯狂": `经验获取率:8倍。商店价格:28\n道具置换:\\i[superPotion]\\i[bomb]\\i[cross]→\\i[cardbag]\n怪物增幅:50%\n其他:完成选择后:\n左/右侧地区每个区域首次到达抽取事件卡\n所有怪物获得0~2个特殊属性,根据你的选择会刷新不同的关卡专属特殊属性`, } } this.BossInfo = { "星趴·梦想号": `\\i[SeaGawu]技能组: \r[#6FC]时点回合(3,6)\r:进行一次三连击 \r[#6FC]时点回合(5,6)\r:防御提升10%`, "御魂庆典": `\\i[Masao]技能组: \r[#6FC]时点回合(6,6)\r:回复攻击力的生命,不进行攻击`, "水乡古镇": `\\i[LionGawu]技能组: \r[#F00]攻击无视防御力\r \r[#6FC]每回合\r:攻击+2%`, "魔法学院": `\\i[ADMasao]技能组: \r[#6FC]时点回合(6,6)\r:额外造成攻击的伤害,自身回复最大生命的10% \\i[SOMasao]技能组: \r[#6FC]时点回合(6,6)\r:额外造成玩家生命值10%的伤害,自身攻击提升最大生命的1%`, "龙宫游乐园": `\\i[Souri]技能组: \r[#6FC]时点回合(4,4)\r:攻击提升本回合造成伤害的2% \r[#F00]每回合双方最终伤害提升10%\r \\i[Mamushi]技能组: \r[#6FC]时点回合(3,5)\r:本回合受到伤害减少自身当前生命值2.67%,若本回合受到伤害,不触发弃牌技能,否则攻击+600且本回合造成伤害翻倍 \r[#6FC]每回合\r:丢弃最左侧的手牌 \r[#F00]玩家手牌<5时,每少1张造成伤害提升20%\r ` } } update() { let ctx = core.createCanvas("gallery", 0, 0, 416, 416, 71) core.clearMap(ctx) core.status.event.id = "gallery" core.lockControl() switch (this.mode) { case "token": this.drawtoken() break case "card": this.drawcard() break case "character": this.drawCharacter() break case "map": this.drawmap() break } } close() { let ctx = core.getContextByName("gallery") core.clearMap(ctx) core.status.event.id = null core.closePanel() core.unlockControl() } initdraw() { let ctx = core.getContextByName("gallery") core.setTextAlign(ctx, "center") core.strokeRoundRect(ctx, 1, 1, 94, 35, 10, "#999999", 2) core.fillRoundRect(ctx, 1, 1, 94, 34, 10, this.mode === "token" ? "#000000A0" : "#CCCCCCA0") core.fillText(ctx, "筹 码", 47, 25, this.mode === "token" ? "#DDDDDD" : "#222222", "22px cjk") core.strokeRoundRect(ctx, 95, 1, 94, 35, 10, "#999999", 2) core.fillRoundRect(ctx, 95, 1, 94, 34, 10, this.mode === "card" ? "#000000A0" : "#CCCCCCA0") core.fillText(ctx, "卡 牌", 141, 25, this.mode === "card" ? "#DDDDDD" : "#222222", "22px cjk") core.strokeRoundRect(ctx, 189, 1, 94, 35, 10, "#999999", 2) core.fillRoundRect(ctx, 189, 1, 94, 34, 10, this.mode === "character" ? "#000000A0" : "#CCCCCCA0") core.fillText(ctx, "角 色", 235, 25, this.mode === "character" ? "#DDDDDD" : "#222222", "22px cjk") core.strokeRoundRect(ctx, 283, 1, 94, 35, 10, "#999999", 2) core.fillRoundRect(ctx, 283, 1, 94, 34, 10, this.mode === "map" ? "#000000A0" : "#CCCCCCA0") core.fillText(ctx, "其 他", 329, 25, this.mode === "map" ? "#DDDDDD" : "#222222", "22px cjk") core.strokeRoundRect(ctx, 378, 1, 35, 35, 10, "#999999", 2) core.fillRoundRect(ctx, 378, 1, 35, 34, 10, "#AAAAAA") core.fillText(ctx, "×", 395, 26, "#FF0000", "30px STHUPO") } drawTokenIcon(ctx, token, x, y, w, h, a) { const tokenColor1 = core.plugin.bluetoken.includes(token) ? "blue" : core.plugin.purpletoken.includes(token) ? "purple" : "gold" let img = token if (img === "BoxingGlovesB" || img === "BoxingGlovesG" || img === "BoxingGlovesE") img = "BoxingGloves" if (img === "MotorcycleHelmetB" || img === "MotorcycleHelmetG" || img === "MotorcycleHelmetE") img = "MotorcycleHelmet" if (img === "BankcardL" || img === "BankcardH" || img === "BankcardU") img = "BankCard" if (img === "MembershipLetterB" || img === "MembershipLetterV") img = "MembershipLetter" if (img === "UtilityKnifeB" || img === "UtilityKnifeS") img = "UtilityKnife" if (img === "MedicalKitE" || img === "MedicalKitC") img = "MedicalKit" if (img === "AdrenalineR" || img === "AdrenalineH") img = "Adrenaline" if (img === "FlashlightB" || img === "FlashlightS") img = "Flashlight" if (img === "SandwichCookieR" || img === "SandwichCookieT" || img === "SandwichCookieD") img = "SandwichCookie" if (img === "SprayCanR" || img === "SprayCanL" || img === "SprayCanN") img = "SprayCan" if (img === "BufferShieldR" || img === "BufferShieldP") img = "BufferShield" if (img === "ExtraBatteryR" || img === "ExtraBatteryC") img = "ExtraBattery" if (img === "HandheldFanS" || img === "HandheldFanL") img = "HandheldFan" if (img === "EagleEyeSight" || img === "NormalSight") img = "EagleEyeSight" img += ".png" core.drawImage(ctx, "Relicbg" + tokenColor1 + ".png", x, y, w, h) core.drawImage(ctx, img, x + a, y + a, w - 2 * a, h - 2 * a) } drawtoken() { const tokentext = { "BoxingGlovesB": `\\i[atk]+${flags.MapStatus?.Map === "魔法学院"?20:10}`, "BoxingGlovesG": `\\i[atk]+${flags.MapStatus?.Map === "魔法学院"?40:20}`, "BoxingGlovesE": `\\i[atk]+${flags.MapStatus?.Map === "魔法学院"?100:50}`, "MotorcycleHelmetB": `\\i[def]+${flags.MapStatus?.Map === "魔法学院"?40:20}`, "MotorcycleHelmetG": `\\i[def]+${flags.MapStatus?.Map === "魔法学院"?80:40}`, "MotorcycleHelmetE": `\\i[def]+${flags.MapStatus?.Map === "魔法学院"?160:80}`, "BankcardL": `\\i[BuffStarlight]+15`, "BankcardH": `\\i[BuffStarlight]+25`, "BankcardU": `\\i[BuffStarlight]+8,怪物掉落\\i[money]翻倍(可与\\i[coin]叠加至4倍)`, "Dice8": `到达新楼层时\\i[BuffStarlight]+1。\n受到的战斗伤害为8的正整数倍时,获得等同于8×\\i[stars]数量的\\i[money](${8*flags.ratio})与\\i[stars]算术平方根数量的\\i[def](${Math.floor(Math.sqrt(flags.ratio))})`, "CursedSword": `每进行10次战斗,\\i[atk]+\\i[stars](${flags.ratio}),\\i[def]-1\n如果受到的战斗伤害大于125×\\i[stars](${flags.ratio * 125}),视为进行了10次战斗并重置次数。\n\r[#66FFCC]还需${10 - flags.Cursed}次战斗`, "BufferShieldR": `\\i[def]+15。受到的战斗伤害减少\\i[def]×(\\i[BuffHealth]+1)÷(\\i[stars]×15)\n\n\r[#66FFCC]当前伤害减少:${Math.floor(hero.def * (flags.health.count + 1) / (15 * flags.ratio))}`, "BufferShieldP": `\\i[def]+35。受到的战斗伤害减少\\i[def]×\\i[BuffHealth]÷(\\i[stars]×75)%(百分比减伤加算,上限75%)\n\n\r[#66FFCC]当前伤害减免:${Math.min(Math.floor(hero.def * flags.health.count / (75 * flags.ratio)), 75)}%`, "StarCoinHammer": `\\i[BuffStarlight]+7。若\\i[BuffStarlight]≥35,则每次战斗额外消耗${flags.MapStatus?.Map==="魔法学院"?300:75}×\\i[stars]的\\i[money](${flags.ratio*(flags.MapStatus?.Map==="魔法学院"?300:75)}),伤害计算时获得\\i[money]×\\i[stars]${flags.MapStatus?.Map==="魔法学院"?"‰":"%"}的\\i[atk](上限1000×\\i[stars])`, "AdrenalineR": `若当前\\i[hp]<=125×\\i[stars](${flags.ratio * 125}),伤害计算时\\i[atk]+30倍\\i[stars](${flags.ratio * 30})`, "AdrenalineH": `若当前\\i[hp]<=125×\\i[stars](${flags.ratio * 125}),伤害计算时\\i[atk]+80倍\\i[stars](${flags.ratio * 80})`, "PiggyBank": `在\\i[shop]购物后,获得一次星光奖励的\\i[money]`, "Backpack": `从\\i[woman]\\i[Parunan]获得物品时额外获得一份`, "MedicalKitE": `战后,\\i[BuffHealth]+12 `, "MedicalKitC": `战后,\\i[BuffHealth]+30 `, "FlashlightB": `获得星光奖励时,\\i[BuffStarlight]+1`, "MembershipLetterB": `\\i[shop]价格-20%`, "MembershipLetterV": `\\i[shop]效果翻倍但价格+20%`, "RollerSkates": `获得二连击`, "UtilityKnifeS": `\\i[atk]+35,若\\i[hp]>=800×\\i[stars](${800 * flags.ratio}),伤害计算时获得\\i[BuffHealth]数量的\\i[atk]`, "UtilityKnifeB": `\\i[atk]+15,若\\i[hp]>=1000×\\i[stars](${1000 * flags.ratio}),伤害计算时获得\\i[BuffHealth]数量的\\i[atk]`, "SandwichCookieD": `\\i[hp]×${flags.MapStatus?.Map === "魔法学院"?"4+30000":"2+10000"}`, "SandwichCookieT": `\\i[hp]×${flags.MapStatus?.Map === "魔法学院"?"1.96+7200":"1.4+3000"}`, "SandwichCookieR": `\\i[hp]+${flags.MapStatus?.Map === "魔法学院"?3000:1500}`, "FriendshipBadge": `与\\i[woman]交易前可消耗\\i[BuffStarlight]进行一次额外交易,每个\\i[woman]限一次`, "VitaminPills": `在\\i[shop]购物时,\\i[BuffHealth]+10,\\i[BuffHealth]的保留效果提升至75%`, "FlashlightS": `\\i[BuffStarlight]+5,伤害计算时\\i[atk]提升\\i[BuffStarlight]×\\i[stars]的算术平方根(${Math.floor(flags.star.level*Math.sqrt(flags.ratio))})`, "TargetBoard": `战后楼层内所有怪物附加2\\i[BuffMark]`, "SprayCanR": `除\\i[N397]\\i[N398]\\i[redKing]和\\i[Gawu]家族外,所有怪物附加8\\i[BuffMark]`, "SprayCanL": `除\\i[N397]\\i[N398]\\i[redKing]和\\i[Gawu]家族外,所有怪物附加16\\i[BuffMark]`, "SprayCanN": `除\\i[N397]\\i[N398]\\i[redKing]和\\i[Gawu]家族外,所有怪物附加40\\i[BuffMark]`, "NormalSight": `与怪物战斗时,先附加2\\i[BuffMark],伤害计算时获得2×\\i[BuffMark]的\\i[def]`, "EagleEyeSight": `与怪物战斗时,先附加3\\i[BuffMark],伤害计算时获得2×\\i[stars]×\\i[BuffMark]的\\i[def]`, "PiercingGun": `怪物防御减少${flags.MapStatus?.Map === "魔法学院"?75:100}%`, "ATM": `获得1~3个随机\\i[battletoken]`, "MagicTome": `每5场战斗后,给予一张无消耗高升炮\n还剩${5-flags.Tome}场战斗`, "ExtraBatteryR": `技能冷却-10`, "ExtraBatteryC": `战后技能冷却额外-1`, "TeaCake": `\\i[redPotion]\\i[bluePotion]\\i[yellowPotion]\\i[greenPotion]+50%`, "Candy": `出牌后\\i[BuffHealth]+10。如果\\i[BuffHealth]>=100,失去100\\i[BuffHealth],\\i[cost]+1`, "GourmetStew": `到达新楼层时战后获得的\\i[BuffHealth]+1`, "HandheldFanS": `使用技能后\\i[cost]+3`, "HandheldFanL": `使用技能后\\i[cost]回满,\\i[card]+3`, "MagicQuiver": `对带有\\i[BuffMark]的怪物使用卡牌后\\i[card]+1`, "NinjaThrowingDarts": `出牌后,除\\i[N397]\\i[N398]和\\i[Gawu]家族外,所有怪物附加1层\\i[BuffMark]`, "RevengeHalberd": `本局每累计受到1000战斗伤害,最终\\i[atk]+1%\n可在数据统计查看已受到的战斗伤害`, "Smartwatch": `战后若手牌<5张\\i[card]+1`, "AncientStaff": `\\i[cost]上限+1,出牌\\i[cost]消耗-1(不会为负消耗)`, "PremiumSwordShield": `\\i[atk]\\i[def]+20。每拥有1个\\i[battletoken],最终\\i[atk]+2%,最终\\i[def]+5%\n(\\i[atk]+${2 * flags.hasChip.length}%,\\i[def]+${5 * flags.hasChip.length}%)`, "Trident": `发动技能后,本局游戏最终\\i[atk]+3%,最终\\i[def]+5%\n(\\i[atk]+${3 * flags.Trident}%,\\i[def]+${5 * flags.Trident}%)`, "DreamlinerModel": `每累计战斗30回合,技能冷却-1\n\r[#66FFCC]累计回合数:${flags.DreamlinerModel}`, "Magnifier": `快捷商店激活事件卡商店`, "FoxMask": `若出牌消耗费用>=2,\\i[atk]和\\i[def]提升\\i[stars]的一半(${Math.floor(flags.ratio/2)})`, } let ctx = core.getContextByName("gallery") this.initdraw() core.strokeRoundRect(ctx, 1, 37, 260, 378, 5, "#999999", 2) core.fillRoundRect(ctx, 2, 37, 258, 377, 5, "#000000A0") core.strokeRoundRect(ctx, 261, 37, 154, 346, 5, "#999999", 2) core.fillRoundRect(ctx, 262, 37, 152, 345, 5, "#000000A0") core.strokeRoundRect(ctx, 261, 382, 154, 33, 5, "#999999", 2) core.fillRoundRect(ctx, 262, 383, 152, 31, 5, "#000000A0") core.fillText(ctx, this.mytoken ? "☑只显示已拥有筹码" : "□只显示已拥有筹码", 338, 405, "#FFFFFF", "16px cjk") const tokenList = this.mytoken ? flags.hasChip : core.plugin.chip const haschip = flags.hasChip let px = 7, px2 = 132, py = 44; for (let i = this.tokenpage * 16; i <= this.tokenpage * 16 + 15; i++) { if (!tokenList[i]) break if (i % 2 === 0) { core.strokeRoundRect(ctx, px, py, 120, 37, 5, this.selectiontoken === i - this.tokenpage * 16 ? "#FFFF00" : "#999999", 2) //core.drawIcon(ctx, tokenList[i], px + 2, py + 2, 32, 32) this.drawTokenIcon(ctx, tokenList[i], px + 2, py + 2, 32, 32, 4) core.fillText(ctx, core.material.items[tokenList[i]].name, px + 75, py + 23, haschip && haschip.includes(tokenList[i]) ? "#FFFFFF" : "#777777", "12px cjk") } else { core.strokeRoundRect(ctx, px2, py, 120, 37, 5, this.selectiontoken === i - this.tokenpage * 16 ? "#FFFF00" : "#999999", 2) //core.drawIcon(ctx, tokenList[i], px2 + 2, py + 2, 32, 32) this.drawTokenIcon(ctx, tokenList[i], px2 + 2, py + 2, 32, 32, 4) core.fillText(ctx, core.material.items[tokenList[i]].name, px2 + 75, py + 23, haschip && haschip.includes(tokenList[i]) ? "#FFFFFF" : "#777777", "12px cjk") py += 42 } } if (this.tokenpage > 0) core.drawImage(ctx, "Left_set.png", 3, 375, 34, 34) if (this.tokenpage < Math.ceil((tokenList.length / 16) - 1) && tokenList.length > 0) core.drawImage(ctx, "Right_set.png", 225, 375, 34, 34) if (tokenList.length > 0) core.fillText(ctx, "第 " + (this.tokenpage + 1) + " / " + Math.ceil(tokenList.length / 16) + "页", 132, 400, "#FFFFFF", "24px STHUPO") const tokenselect = this.selectiontoken + this.tokenpage * 16 if (!tokenList[tokenselect]) return this.drawTokenIcon(ctx, tokenList[tokenselect], 298, 51, 80, 80, 5) core.fillText(ctx, core.material.items[tokenList[tokenselect]].name, 338, 152, "#FFFFFF", "20px cjk") core.drawTextContent(ctx, tokentext[tokenList[tokenselect]], { left: 263, top: 162, color: "#FFFFFF", align: "left", maxWidth: 150, fontSize: 13, font: "cjk" }) } drawcard() { let ctx = core.getContextByName("gallery") this.initdraw() core.strokeRoundRect(ctx, 1, 37, 414, 378, 5, "#999999", 2) core.fillRoundRect(ctx, 2, 37, 412, 377, 5, "#000000A0") let px = 3, py = 44; for (let i = this.cardpage * 8; i <= this.cardpage * 8 + 7; i++) { if (!this.cardList[i]) break core.drawImage(ctx, this.cardList[i] + ".webp", px, py, 102, 162) px += 103 if (i === this.cardpage * 8 + 3) { px = 3 py = 208 } } if (this.cardpage > 0) core.drawImage(ctx, "Left_set.png", 3, 375, 34, 34) if (this.cardpage < 6) core.drawImage(ctx, "Right_set.png", 380, 375, 34, 34) core.fillText(ctx, "第 " + (this.cardpage + 1) + " / 7 页", 208, 400, "#FFFFFF", "24px STHUPO") if (this.selectioncard) { core.fillRect(ctx, 0, 0, 416, 416, "#000000A0") core.drawImage(ctx, this.selectioncard + ".webp", 106, 49, 204, 324) } } drawCharacter() { let ctx = core.getContextByName("gallery") this.initdraw() core.strokeRoundRect(ctx, 1, 37, 100, 378, 5, "#999999", 2) core.fillRoundRect(ctx, 2, 37, 99, 377, 5, "#000000A0") core.strokeRoundRect(ctx, 102, 37, 313, 378, 5, "#999999", 2) core.fillRoundRect(ctx, 103, 37, 312, 377, 5, "#000000A0") let px = 7, py = 44; for (let i = this.characterpage * 8; i <= this.characterpage * 8 + 7; i++) { const C = this.CharacterList[i] if (!C) break core.strokeRoundRect(ctx, px, py, 90, 37, 5, this.selectioncharacter === i - this.characterpage * 8 ? "#FFFF00" : "#999999", 2) core.fillText(ctx, C, px + 45, py + 23, "#FFFFFF", "16px cjk") py += 42 } if (this.characterpage > 0) core.drawImage(ctx, "Left_set.png", 3, 375, 34, 34) if (this.characterpage < 2) core.drawImage(ctx, "Right_set.png", 68, 375, 34, 34) core.fillText(ctx, (this.characterpage + 1), 52, 400, "#FFFFFF", "24px STHUPO") const fn = core.plugin.Character[this.CharacterList[this.characterpage * 8 + this.selectioncharacter]] if (this.selectionmode === 0) { core.drawTextContent(ctx, fn.file, { left: 104, top: 39, color: "#FFFFFF", align: "left", maxWidth: 311, fontSize: 13, font: "cjk" }) core.strokeRoundRect(ctx, 270, 227, 139, 68, 5, "#999999", 2) core.drawTextContent(ctx, fn.startText, { left: 271, top: 228, color: "#CCCCCC", align: "left", maxWidth: 138, fontSize: 13, font: "cjk" }) core.drawImage(ctx, "Right_set.png", 382, 382, 30, 30) } else { core.drawTextContent(ctx, "主动技能:" + fn.ActiveSkill + "\n被动技能:" + fn.PassiveSkill, { left: 104, top: 39, color: "#FFFFFF", align: "left", maxWidth: 311, fontSize: 13, font: "cjk" }) core.drawImage(ctx, "Left_set.png", 382, 382, 30, 30) } core.drawImage(ctx, fn.Avatar, 103, 214, 162, 200) } drawmap() { let ctx = core.getContextByName("gallery") this.initdraw() core.fillRoundRect(ctx, 2, 37, 136, 35, 5, (this.mapmode === 0 ? "#000000A0" : "#CCCCCCA0")) core.strokeRoundRect(ctx, 1, 37, 137, 36, 5, "#999999", 2) core.fillText(ctx, "地图信息", 69, 64, this.mapmode === 0 ? "#DDDDDD" : "#222222", "22px cjk") core.fillRoundRect(ctx, 140, 37, 136, 35, 5, (this.mapmode === 1 ? "#000000A0" : "#CCCCCCA0")) core.strokeRoundRect(ctx, 139, 37, 137, 36, 5, "#999999", 2) core.fillText(ctx, "名词解释", 207, 64, this.mapmode === 1 ? "#DDDDDD" : "#222222", "22px cjk") core.fillRoundRect(ctx, 278, 37, 137, 35, 5, (this.mapmode === 2 ? "#000000A0" : "#CCCCCCA0")) core.strokeRoundRect(ctx, 277, 37, 138, 36, 5, "#999999", 2) core.fillText(ctx, "成就/设置", 345, 64, this.mapmode === 2 ? "#DDDDDD" : "#222222", "22px cjk") core.setTextAlign(ctx, "left") if (this.mapmode === 0) { core.fillRoundRect(ctx, 2, 73, 412, 341, 5, "#000000A0") core.strokeRoundRect(ctx, 1, 73, 414, 342, 5, "#999999", 2) if (this.mapstatuspage === 0) core.drawTextContent(ctx, this.MapInfo[flags.MapStatus?.Map], { left: 6, top: 75, color: "#FFFFFF", align: "left", maxWidth: 408, fontSize: 15, font: "cjk" }) else if (this.mapstatuspage === 1) core.drawTextContent(ctx, this.HardInfo[flags.MapStatus?.Map][flags.MapStatus?.Hard], { left: 6, top: 75, color: "#FFFFFF", align: "left", maxWidth: 408, fontSize: 15, font: "cjk" }) else core.drawTextContent(ctx, this.BossInfo[flags.MapStatus?.Map], { left: 6, top: 75, color: "#FFFFFF", align: "left", maxWidth: 408, fontSize: 15, font: "cjk" }) if (this.mapstatuspage > 0) core.drawImage(ctx, "Left_set.png", 320, 380, 34, 34) core.fillBoldText(ctx, this.mapstatuspage + 1, 359, 406, "#FFF", "#000", 3, "26px STHUPO") if (this.mapstatuspage < 2) core.drawImage(ctx, "Right_set.png", 380, 380, 34, 34) } else if (this.mapmode === 1) { core.fillRoundRect(ctx, 2, 73, 412, 341, 5, "#000000A0") core.strokeRoundRect(ctx, 1, 73, 414, 342, 5, "#999999", 2) if (this.gametextpage === 0) core.drawTextContent(ctx, `\\i[stars]\r[#FF0]星星\r:关联塔内大部分物品 血瓶会因\r[#FF0]星星\r而强化:血瓶初始回血量×\\i[stars] 战斗筹码抽取概率会因\r[#FF0]星星\r而变动 部分卡牌/角色技能/战斗筹码也受\r[#FF0]星星\r影响 显示在状态栏的\\i[BuffStarlight]区域:${flags.star.level}×星星数(${flags.star.count}) \\i[BuffStarlight]\r[#FF0]星光\r:每3场战斗后会给予玩家\\i[BuffStarlight]×\\i[stars]的星币奖励(${flags.star.level*flags.ratio}),这个奖励也被称为\r[#FF0]星光奖励\r 从拥有至少1\r[#FF0]星光\r开始,星光计数器才会启动 显示在状态栏的\\i[BuffStarlight]区域:星光数×${flags.ratio}(星光计数器) \\i[BuffHealth]\r[#6FC]治愈\r:战后回复\r[#6FC]治愈\r层数的\\i[hp],随后\r[#6FC]治愈\r层数减少40% 显示在状态栏的\\i[BuffHealth]区域 加号左侧代表当前的\r[#6FC]治愈层数\r,右侧代表战后给予的\r[#6FC]治愈层数\r(通过筹码/角色获得)`, { left: 6, top: 75, color: "#FFFFFF", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) else if (this.gametextpage === 1) core.drawTextContent(ctx, `\\i[BuffMark]\r[#F00]标记\r:附加在怪物身上,每回合受到\r[#F00]标记层数\r的伤害 在你不破防怪物时,标记相当于魔攻 需点开\\i[book]里的单个怪物详情查看\r[#F00]标记层数\r \\i[cost]\r[#fa5fd0]cost\r:打出卡牌时所需的费用,显示在状态栏${core.domStyle.isVertical?"下方":"左下角"} 当\r[#fa5fd0]cost\r不满时,每10场战斗后回复1\r[#fa5fd0]cost\r \\i[card]卡牌:分为普通卡和事件卡,普通卡为一次性道具/增幅,事件卡会在符合条件的首次抵达楼层时执行其效果 点击状态栏${core.domStyle.isVertical?"上方":"右侧"}的卡牌按钮进入卡牌页面 左上角为卡牌类型(攻击/防御/效果/事件),卡牌类型的下方为出牌所需cost,卡图下方为卡牌效果 查看卡牌详情请点击图鉴上方的卡牌按钮`, { left: 6, top: 75, color: "#FFFFFF", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) else if (this.gametextpage === 2) core.drawTextContent(ctx, `\\i[Gawu]家族(\r[#F00]嘎呜家族\r):恋让她的房东——天王寺雅央所扮演的超级BOSS \r[#F00]嘎呜家族\r指的是4张地图的5只嘎呜:\\i[SeaGawu]\\i[LionGawu]\\i[Masao]\\i[ADMasao]\\i[SOMasao] 现在也包括龙宫游乐园的天川家族:\\i[Souri]\\i[Mamushi] 除龙宫游乐园外,需要满足地图的召唤条件(所有前提都是击败真魔王)后才能召唤 嘎呜采用\r[#FF0]特殊回合制战斗\r,玩家无法通过\\i[book]和地图显伤查看嘎呜造成的伤害 \r[#6FC]时点回合(x,y)\r:仅在\r[#FF0]特殊回合制战斗\r才使用的名词 从x回合第一次发动技能开始,每过y回合会发动一次技能 例如:\r[#6FC]时点回合(1,6)\r代表第1/7/13/19……回合发动一次技能`, { left: 6, top: 75, color: "#FFFFFF", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) else if (this.gametextpage === 3) core.drawTextContent(ctx, `怪物的种族:显示在手册里,有6大种族:史莱姆/暗夜/守护/人类/魔法师/神秘 一般情况种族没有任何作用(魔法学院地图除外) \r[#F00]嘎呜家族\r属于神秘族 特殊属性:当怪物有特殊属性时会显示在地图上,可点击\\i[book]查看详情 \r[green]绿字属性\r/\r[red]红字属性\r:当你的攻击/防御受筹码/卡牌等影响时这些属性会在状态栏变色,同时会显示变动了多少 这部分属性不受怪物模仿/坚固的特殊属性影响 最终攻击力/最终防御力:计算完筹码/卡牌等加成后对此攻击力/防御力再进行一次加成。可理解为另一个乘区`, { left: 6, top: 75, color: "#FFFFFF", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) else if (this.gametextpage === 4) core.drawTextContent(ctx, `\\i[cross]十字架:与\\i[zombie]\\i[zombieKnight]\\i[vampire]战斗,伤害计算时\\i[atk]翻倍(在筹码/卡牌计算前) \\i[knife]屠龙匕:与\\i[N398]战斗,伤害计算时\\i[atk]翻倍(在筹码/卡牌计算前) \\i[coin]幸运金币:从怪物获取的\\i[money]翻倍(与\\i[BankcardU]可叠加至4倍)`, { left: 6, top: 75, color: "#FFFFFF", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.gametextpage > 0) core.drawImage(ctx, "Left_set.png", 320, 380, 34, 34) core.fillBoldText(ctx, this.gametextpage + 1, 359, 406, "#FFF", "#000", 3, "26px STHUPO") if (this.gametextpage < 4) core.drawImage(ctx, "Right_set.png", 380, 380, 34, 34) } else { core.fillRoundRect(ctx, 2, 73, 412, 170, 5, "#000000A0") core.strokeRoundRect(ctx, 1, 73, 414, 171, 5, "#999999", 2) core.fillRoundRect(ctx, 2, 244, 412, 170, 5, "#000000A0") core.strokeRoundRect(ctx, 1, 244, 414, 171, 5, "#999999", 2) if (this.ACVpage > 0) core.drawImage(ctx, "Left_set.png", 320, 209, 34, 34) core.fillBoldText(ctx, this.ACVpage + 1, 359, 235, "#FFF", "#000", 3, "26px STHUPO") if (this.ACVpage < 8) core.drawImage(ctx, "Right_set.png", 380, 209, 34, 34) core.fillText(ctx, "只有已完成的成就才能查看详细信息", 6, 92, "#FFF", "15px cjk") const achievement = JSON.parse(core.decodeBase64(core.getLocalStorage("408DA99A231691FD6EAF0117B9991F26", 'W10='))) if (this.ACVpage === 0) core.drawTextContent(ctx, achievement.includes("1541C821FF1B39191BF73A8D9BF05805") ? "星引擎缔造者:通关魔法学院以外的地图时持有202403星币\n奖励:全角色经验值+1000" : "星引擎缔造者:??????\n奖励:全角色经验值+1000", { left: 6, top: 100, color: achievement.includes("1541C821FF1B39191BF73A8D9BF05805") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.ACVpage === 1) core.drawTextContent(ctx, achievement.includes("4A258155F46ADB5D4599713358316EE1") ? "不是哥们?!:0级勇者通关疯狂难度\n奖励:全角色经验值+1000" : "不是哥们?!:??????\n奖励:全角色经验值+1000", { left: 6, top: 100, color: achievement.includes("4A258155F46ADB5D4599713358316EE1") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.ACVpage === 2) core.drawTextContent(ctx, `快!再快一点:${achievement.includes("B41A02E0553FF9EF3942ABCAF60D624D")?"在不超过4星星的情况下通关疯狂难度星趴·梦想号":"??????"} 奖励:全角色经验值+500`, { left: 6, top: 100, color: achievement.includes("B41A02E0553FF9EF3942ABCAF60D624D") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.ACVpage === 3) core.drawTextContent(ctx, `启航!梦想号:${achievement.includes("E55FE7C62ABB0CD0CC29E433CAFFD39B")?"击败疯狂难度的海盗王嘎呜-天王寺雅央":"??????"} 奖励:全角色经验值+500`, { left: 6, top: 100, color: achievement.includes("E55FE7C62ABB0CD0CC29E433CAFFD39B") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.ACVpage === 4) core.drawTextContent(ctx, `百鬼夜行:${achievement.includes("5CFCFF9401B7B06C3CF971D3DD1647C0")?"击败疯狂难度的祭典使者-天王寺雅央":"??????"} 奖励:全角色经验值+500`, { left: 6, top: 100, color: achievement.includes("5CFCFF9401B7B06C3CF971D3DD1647C0") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.ACVpage === 5) core.drawTextContent(ctx, `年兽退散:${achievement.includes("3A7E94AE93F0947DDC8142FC8797AFFC")?"击败疯狂难度的舞狮嘎呜":"??????"} 奖励:全角色经验值+500`, { left: 6, top: 100, color: achievement.includes("3A7E94AE93F0947DDC8142FC8797AFFC") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.ACVpage === 6) core.drawTextContent(ctx, `学院双星:${achievement.includes("516607A2E777E09E9954E0A14FC40A27")?"同时击败疯狂难度的代理院长-天王寺雅央和学院安全官-天王寺雅央":"??????"} 奖励:全角色经验值+1000`, { left: 6, top: 100, color: achievement.includes("516607A2E777E09E9954E0A14FC40A27") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.ACVpage === 7) core.drawTextContent(ctx, `战棋之主-鲤:${achievement.includes("4BABDF2950A811AF8B27C75D1AA5D168")?"帮助天川苍鲤取得胜利":"??????"} 奖励:全角色经验值+500`, { left: 6, top: 100, color: achievement.includes("4BABDF2950A811AF8B27C75D1AA5D168") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) if (this.ACVpage === 8) core.drawTextContent(ctx, `战棋之主-蛟:${achievement.includes("1FC895A2B243EFF1F693E6D926C4CCDC")?"帮助天川真梦梓取得胜利":"??????"} 奖励:全角色经验值+500`, { left: 6, top: 100, color: achievement.includes("1FC895A2B243EFF1F693E6D926C4CCDC") ? "#FFFFFF" : "#999999", align: "left", maxWidth: 410, fontSize: 15, font: "cjk" }) core.fillText(ctx, core.getLocalStorage("出牌动画开关") ? "□出牌动画" : "☑出牌动画", 10, 272, "#6FC", "20px cjk") if (!core.getLocalStorage("出牌动画开关")) core.fillText(ctx, "< 动画时长:" + (core.getLocalStorage("出牌等待", 10) / 10).toFixed(1) + "s >", 160, 272, "#6FC", "20px cjk") core.fillText(ctx, "< 事件卡停留时长:" + (core.getLocalStorage("事件等待", 30) / 10).toFixed(1) + "s >", 125, 300, "#6FC", "20px cjk") core.fillText(ctx, "点此反馈游戏BUG", 10, 406, "#6FC", "20px cjk") } } click(x, y, px, py) { if (core.status.event.id !== "gallery") return /** * 区域计算 * @param {[number,number]} start * @param {[number,number]} end * @returns */ const makeBox = ([x, y], [w, h]) => { return [ [x, y], [x + w, y + h], ]; }; /** * 判定点击是否在落点 * @param {[number,number]} click 点击位置 * @param {function} makeBox 区域,来自 {@link makeBox()} * @returns */ const inRect = ([x, y], [ [sx, sy], [dx, dy] ]) => { return sx <= x && x <= dx && sy <= y && y <= dy; }; const fn = core.plugin.gallery const pos = [px, py]; const tokenbox = makeBox([1, 1], [94, 35]) const cardbox = makeBox([95, 1], [94, 35]) const characterbox = makeBox([189, 1], [94, 35]) const mapbox = makeBox([283, 1], [94, 35]) const closebox = makeBox([378, 1], [35, 35]) const Left = makeBox([3, 375], [34, 34]) switch (fn.mode) { case "token": if (inRect(pos, cardbox)) fn.mode = "card" if (inRect(pos, characterbox)) fn.mode = "character" if (inRect(pos, mapbox)) fn.mode = "map" const tokenRight = makeBox([225, 375], [34, 34]) const tokenListbox = makeBox([7, 44], [245, 328]) const mytoken = makeBox([261, 382], [154, 33]) const tokenList = fn.mytoken ? flags.hasChip : core.plugin.chip if (inRect(pos, Left) && fn.tokenpage > 0) { fn.tokenpage-- fn.selectiontoken = 0 } if (inRect(pos, tokenRight) && fn.tokenpage < Math.ceil((tokenList.length / 16) - 1) && tokenList.length > 0) { fn.tokenpage++ fn.selectiontoken = 0 } if (inRect(pos, closebox)) { fn.close() return true } if (inRect(pos, mytoken)) { fn.tokenpage = 0 fn.selectiontoken = 0 fn.mytoken = !fn.mytoken } if (inRect(pos, tokenListbox)) { const index = Math.floor((py - 39) / 42) * 2 + Math.floor((px - 7) / 120) if (index + fn.tokenpage * 16 < tokenList.length) fn.selectiontoken = index } break case "card": if (fn.selectioncard) { fn.selectioncard = "" fn.update() return true } if (inRect(pos, tokenbox)) fn.mode = "token" if (inRect(pos, characterbox)) fn.mode = "character" if (inRect(pos, mapbox)) fn.mode = "map" const cardRight = makeBox([380, 375], [34, 34]) const cardListbox = makeBox([3, 44], [412, 326]) if (inRect(pos, Left) && fn.cardpage > 0) fn.cardpage-- if (inRect(pos, cardRight) && fn.cardpage < 6) fn.cardpage++ if (inRect(pos, cardListbox)) { const index = fn.cardpage * 8 + Math.floor((py - 44) / 163) * 4 + Math.floor((px - 3) / 103) if (fn.cardList[index]) fn.selectioncard = fn.cardList[index] } if (inRect(pos, closebox)) { fn.close() return true } break case "character": if (inRect(pos, tokenbox)) fn.mode = "token" if (inRect(pos, cardbox)) fn.mode = "card" if (inRect(pos, mapbox)) fn.mode = "map" const characterRight = makeBox([68, 375], [34, 34]) const characterListbox = makeBox([7, 44], [90, 328]) if (inRect(pos, Left) && fn.characterpage > 0) { fn.characterpage-- fn.selectioncharacter = 0 fn.selectionmode = 0 core.stopSound() } if (inRect(pos, characterRight) && fn.characterpage < 2) { fn.characterpage++ fn.selectioncharacter = 0 fn.selectionmode = 0 core.stopSound() } if (inRect(pos, characterListbox)) { const index = fn.characterpage * 8 + Math.floor((py - 44) / 41) if (fn.CharacterList[index]) { fn.selectioncharacter = Math.floor((py - 44) / 41) fn.selectionmode = 0 core.stopSound() } } if (fn.selectionmode === 0) { if (inRect(pos, makeBox([270, 227], [139, 68]))) core.playSound(core.plugin.Character[fn.CharacterList[fn.characterpage * 8 + fn.selectioncharacter]].startsound) if (inRect(pos, makeBox([382, 382], [30, 30]))) fn.selectionmode = 1 } else { if (inRect(pos, makeBox([382, 382], [30, 30]))) fn.selectionmode = 0 } if (inRect(pos, closebox)) { fn.close() return true } break case "map": if (inRect(pos, tokenbox)) fn.mode = "token" if (inRect(pos, cardbox)) fn.mode = "card" if (inRect(pos, characterbox)) fn.mode = "character" if (inRect(pos, closebox)) { fn.close() return true } const 地图信息 = makeBox([1, 37], [137, 36]), 名词解释 = makeBox([139, 37], [137, 36]), 成就列表 = makeBox([277, 37], [138, 36]); if (inRect(pos, 地图信息) && fn.mapmode !== 0) fn.mapmode = 0 if (inRect(pos, 名词解释) && fn.mapmode !== 1) fn.mapmode = 1 if (inRect(pos, 成就列表) && fn.mapmode !== 2) fn.mapmode = 2 if (fn.mapmode === 0) { if (inRect(pos, makeBox([320, 380], [34, 34])) && fn.mapstatuspage > 0) fn.mapstatuspage-- if (inRect(pos, makeBox([380, 380], [34, 34])) && fn.mapstatuspage < 2) fn.mapstatuspage++ } if (fn.mapmode === 1) { if (inRect(pos, makeBox([320, 380], [34, 34])) && fn.gametextpage > 0) fn.gametextpage-- if (inRect(pos, makeBox([380, 380], [34, 34])) && fn.gametextpage < 4) fn.gametextpage++ } if (fn.mapmode === 2) { if (inRect(pos, makeBox([320, 209], [34, 34])) && fn.ACVpage > 0) fn.ACVpage-- if (inRect(pos, makeBox([380, 209], [34, 34])) && fn.ACVpage < 8) fn.ACVpage++ if (inRect(pos, makeBox([8, 252], [105, 25]))) core.setLocalStorage("出牌动画开关", !core.getLocalStorage("出牌动画开关")) let 出牌等待 = core.getLocalStorage("出牌等待", 10) let 事件等待 = core.getLocalStorage("事件等待", 30) if (inRect(pos, makeBox([155, 256], [20, 20])) && 出牌等待 > 10 && !core.getLocalStorage("出牌动画开关")) core.setLocalStorage("出牌等待", 出牌等待 - 5) if (inRect(pos, makeBox([330, 256], [20, 20])) && 出牌等待 < 50 && !core.getLocalStorage("出牌动画开关")) core.setLocalStorage("出牌等待", 出牌等待 + 5) if (inRect(pos, makeBox([122, 283], [20, 20])) && 事件等待 > 10) core.setLocalStorage("事件等待", 事件等待 - 5) if (inRect(pos, makeBox([357, 283], [20, 20])) && 事件等待 < 50) core.setLocalStorage("事件等待", 事件等待 + 5) if (inRect(pos, makeBox([10, 388], [172, 22]))) { fn.close() return core.plugin.showFeedbackSystem() } } break } fn.update() return true } } core.plugin.gallery = new Gallery() core.registerAction("ondown", "gallery", core.plugin.gallery.click, 50) }, "select": function () { // 在此增加新插件 const select = document.createElement("canvas"); //CGui画布设置 select.style.position = "absolute"; select.style.zIndex = 300; select.style.display = "none"; select.id = "select"; main.dom.gameGroup.insertAdjacentElement("afterend", select); select.style.top = "50%"; select.style.left = "50%"; select.style.transform = "translate(-50%,-50%)"; const ctx = select.getContext("2d"); main.dom.select = select; select.onclick = function (e) { try { e.preventDefault() const left = core.dom.gameGroup.offsetLeft; const top = core.dom.gameGroup.offsetTop; const px = Math.floor((e.clientX - left) / core.domStyle.scale), py = Math.floor((e.clientY - top) / core.domStyle.scale); core.ui.select.onclick(px, py); } catch (ee) { main.log(ee); } }; class Select { constructor() { this.page = true this.selectionHero = "勇者" this.selectionLV = 0 this.selectionMap = "星趴·梦想号" this.selectionHard = "普通" this.selectionTeach = 0 this.heroselected = "" this.LVselected = 0 this.Mapselected = "" this.Hardselected = "" this.charmode = false this.char = ["勇者", "米米", "帕露南", "芬妮", "阿兰娜", "小町", "派德曼", "帕帕拉", "恋", "Z3000", "潘大猛", "璐璐", "枫", "蓝海晴", "美咲", "娜蒂斯", "茉莉", "星魅琉华", "南希露", "梅加斯", "姬梦朝", "阿尔", "凛", "照"] this.MapInfo = [ `体验50层魔塔的传统地图\n\\i[stars]取得方法:抵达11/21/31/41楼\n召唤\\i[SeaGawu]方法:持有\\i[pickaxe]或\\i[earthquake]其中一种和\\i[downFly]的情况下击败50F的魔王`, `将50层魔塔进行随机打乱并重组,带来不一样的新体验\n\\i[stars]取得方法:抵达11/21/31/41楼\n召唤\\i[Masao]方法:持有\\i[pickaxe]或\\i[earthquake]其中一种和\\i[downFly]的情况下击败50F的魔王`, `我如果不说你能知道这里的原型是50层魔塔吗?将50层魔塔压缩至只有21层大小,并进行平面连通\n也许能发现许多快速过关的技巧\n\\i[stars]取得方法:击败\\i[skeletonCaptain]\\i[vampire]\\i[yellowKnight]\\i[blackMagician]\n召唤\\i[LionGawu]方法:持有\\i[superPotion]但未持有\\i[coin]的情况下击败5-3的魔王(需要消耗\\i[superPotion])`, `由主楼和图书馆构成的双子塔。受魔法因子影响,图书馆里的部分道具变稀有了\n但击败图书馆的怪物后塔内相同种族的怪物都会被强化(包括击败此怪物后获得的星币)\n\\i[stars]取得方法:主楼:抵达11/21/31/41楼。图书馆:击败\\i[skeletonCaptain]\\i[vampire]\\i[yellowKnight]\\i[blackMagician]\\i[redKing]\n召唤雅央的方法:持有\\i[pickaxe]或\\i[earthquake]其中一种和\\i[downFly]的情况下击败主楼50F的魔王\n\\r[#FFFF00]警告!!!你将同时面对\\i[ADMasao]\\i[SOMasao]没有十足的把握请勿轻易召唤\\r`, `一张地图两种完全不同的路线\n这次你将在\\i[Souri]\\i[Mamushi]之中选择一位帮助她,同时直接迎战另一位\n\\i[stars]取得方法:抵达特定区域和击败\\i[blackMagician]` ] this.HardInfo = { "星趴·梦想号": { "普通": `经验获取率:1倍。商店价格:20`, "困难": `经验获取率:2倍。商店价格:25\n道具置换:\\i[knife]→\\i[cardbag]\n怪物增幅:10%\n其他:6F以上每层首次到达抽取随机事件卡(21~29F除外)`, "噩梦": `经验获取率:4倍。商店价格:30\n道具置换:\\i[knife]\\i[bomb]→\\i[cardbag]\n怪物增幅:30%\n其他:6F以上每层首次到达抽取随机事件卡(21~29F除外)\n31F开始怪物有概率获得特殊属性`, "疯狂": `经验获取率:8倍。商店价格:35\n道具置换:\\i[knife]\\i[bomb]\\i[cross]→\\i[cardbag]\n怪物增幅:50%\n其他:6F以上每层首次到达抽取随机事件卡(21~29F除外)\n11F开始除BOSS外的怪物都获得0~2个特殊属性\n关卡专属特殊属性-亡语:被击杀后同楼层其他怪物增强`, }, "御魂庆典": { "普通": `经验获取率:1倍。商店价格:20`, "困难": `经验获取率:2倍。商店价格:25\n道具置换:\\i[knife]→\\i[cardbag]\n怪物增幅:10%\n其他:10F以上每层首次到达抽取随机事件卡(21~29F除外)`, "噩梦": `经验获取率:4倍。商店价格:30\n道具置换:\\i[knife]\\i[bomb]→\\i[cardbag]\n怪物增幅:30%\n其他:10F以上每层首次到达抽取随机事件卡(21~29F除外)\n31F开始怪物有概率获得特殊属性`, "疯狂": `经验获取率:8倍。商店价格:35\n道具置换:\\i[knife]\\i[bomb]\\i[cross]→\\i[cardbag]\n怪物增幅:50%\n其他:10F以上每层首次到达抽取随机事件卡(21~29F除外)\n11F开始除BOSS外的怪物都获得0~2个特殊属性\n关卡专属特殊属性-激怒:无视\\i[BuffMark],同楼层带有\\i[BuffMark]的怪物被击杀后自身属性提升`, }, "水乡古镇": { "普通": `经验获取率:1倍。商店价格:20`, "困难": `经验获取率:2倍。商店价格:25\n道具置换:\\i[knife]→\\i[cardbag]\n怪物增幅:10%\n其他:除初始区域外每个区域首次到达抽取随机事件卡`, "噩梦": `经验获取率:4倍。商店价格:30\n道具置换:\\i[knife]\\i[bomb]→\\i[cardbag]\n怪物增幅:30%\n其他:除初始区域外每个区域首次到达抽取随机事件卡\n这些怪物有概率获得特殊属性:\\i[slimeMan]\\i[ghostSkeleton]\\i[blueGuard]\\i[redGuard]\\i[brownWizard]\\i[redWizard]\\i[swordsman]\\i[soldier]\\i[yellowKnight]\\i[redKnight]\\i[darkKnight]\\i[whiteKing]\\i[slimelord]\\i[redBat]`, "疯狂": `经验获取率:8倍。商店价格:35\n道具置换:\\i[knife]\\i[bomb]\\i[cross]→\\i[cardbag]\n怪物增幅:50%\n其他:除初始区域外每个区域首次到达抽取随机事件卡\n这些怪物获得0~2个特殊属性:\\i[slimeMan]\\i[ghostSkeleton]\\i[blueGuard]\\i[redGuard]\\i[brownWizard]\\i[redWizard]\\i[swordsman]\\i[soldier]\\i[yellowKnight]\\i[redKnight]\\i[darkKnight]\\i[whiteKing]\\i[slimelord]\\i[redBat]\\i[blackSlime]\\i[bigBat]\\i[zombie]\\i[zombieKnight]\\i[vampire]\\i[rock]\n关卡专属特殊属性-爆竹呱呱:同区域其他怪物被击败后,对玩家造成自身10%\\i[atk]+玩家\\i[BuffHealth]伤害`, }, "魔法学院": { "普通": `经验获取率:1倍。商店价格:8`, "困难": `经验获取率:2倍。商店价格:10\n道具置换:\\i[knife]→\\i[cardbag]\n怪物增幅:10%\n其他:主楼6F以上及图书馆每层首次到达抽取随机事件卡(21~29F和连廊除外)`, "噩梦": `经验获取率:4倍。商店价格:12\n道具置换:\\i[knife]\\i[bomb]→\\i[cardbag]\n怪物增幅:30%\n其他:主楼6F以上及图书馆每层首次到达抽取随机事件卡(21~29F和连廊除外)\n31F开始怪物有概率获得特殊属性`, "疯狂": `经验获取率:8倍。商店价格:14\n道具置换:\\i[knife]\\i[bomb]\\i[cross]→\\i[cardbag]\n怪物增幅:50%\n其他:主楼6F以上及图书馆每层首次到达抽取随机事件卡(21~29F和连廊除外)\n11F开始怪物获得0~2个特殊属性\n关卡专属特殊属性-茶壶:只在图书馆刷新,被击败后玩家随机回复不超过本次受到伤害的生命值\n主楼BOSS专属特殊属性-甜点师:首次抵达BOSS楼层时,吸收图书馆对应10层的茶壶。每吸收1只增强自身10%的属性`, }, "龙宫游乐园": { "普通": `经验获取率:1倍。商店价格:16`, "困难": `经验获取率:2倍。商店价格:20\n道具置换:\\i[superPotion]→\\i[cardbag]\n怪物增幅:10%\n其他:完成选择后:\n左/右侧地区每个区域首次到达抽取事件卡`, "噩梦": `经验获取率:4倍。商店价格:24\n道具置换:\\i[superPotion]\\i[bomb]→\\i[cardbag]\n怪物增幅:30%\n其他:完成选择后:\n左/右侧地区每个区域首次到达抽取事件卡\n所有怪物有概率获得特殊属性`, "疯狂": `经验获取率:8倍。商店价格:28\n道具置换:\\i[superPotion]\\i[bomb]\\i[cross]→\\i[cardbag]\n怪物增幅:50%\n其他:完成选择后:\n左/右侧地区每个区域首次到达抽取事件卡\n所有怪物获得0~2个特殊属性,根据你的选择会刷新不同的关卡专属特殊属性`, }, } this.TipInfo = ["这里是梦想号,紧急呼叫!重复,紧急呼叫!", "百鬼夜行,别迷路了!", "爆竹可是驱邪的好东西!", "学生们为何要攻占食堂?是饥饿的驱使还是另有隐情?", "龙宫里有什么神奇的宝贝呢!?"] } onclick(px, py) { const makeBox = ([x, y], [w, h]) => { return [ [x, y], [x + w, y + h], ]; }; const inRect = ([x, y], [ [sx, sy], [dx, dy] ]) => { return sx <= x && x <= dx && sy <= y && y <= dy; }; const pos = [px, py]; if (core.domStyle.isVertical) { //竖屏 if (this.page) { //第一页 const 普通 = makeBox([23, 144], [75, 30]), 困难 = makeBox([121, 144], [75, 30]), 噩梦 = makeBox([219, 144], [75, 30]), 疯狂 = makeBox([317, 144], [75, 30]), 新手教程 = makeBox([8, 638], [100, 30]), 进阶教程 = makeBox([136, 638], [100, 30]), 切图左 = makeBox([0, 44], [48, 48]), 切图右 = makeBox([368, 44], [48, 48]), 翻页 = makeBox([308, 638], [100, 30]); if (inRect(pos, 普通)) this.selectionHard = "普通" if (inRect(pos, 困难)) this.selectionHard = "困难" if (inRect(pos, 噩梦)) this.selectionHard = "噩梦" if (inRect(pos, 疯狂)) this.selectionHard = "疯狂" if (inRect(pos, 切图左)) this.selectionMap = this.selectionMap === 0 ? (this.MapScene.length - 1) : (this.selectionMap - 1) if (inRect(pos, 切图右)) this.selectionMap = this.selectionMap === this.MapScene.length - 1 ? 0 : (this.selectionMap + 1) if (inRect(pos, 翻页)) { this.page = !this.page this.selectionLV = flags.D290605F2869068624508C972B6C6AE0[this.selectionHero].a } if (inRect(pos, 新手教程)) { this.selectionTeach = 1 return this.start() } if (inRect(pos, 进阶教程)) { this.selectionTeach = 2 return this.start() } } else { //第二页 const heroBox = makeBox([4, 4], [408, 194]), charBox = makeBox([8, 376], [64, 23]), 翻页 = makeBox([10, 399], [120, 30]), LVDown = makeBox([314, 424], [32, 32]), LvUP = makeBox([366, 424], [32, 32]), START = makeBox([10, 438], [120, 30]); if (inRect(pos, heroBox)) { const Ma = Math.floor((py - 4) / 66) * 8 + Math.floor((px - 4) / 51) if (Ma < this.char.length) { this.selectionHero = this.char[Math.floor((py - 8) / 66) * 8 + Math.floor((px - 4) / 51)] this.selectionLV = flags.D290605F2869068624508C972B6C6AE0[this.selectionHero].a core.stopSound() core.playSound(core.plugin.Character[this.selectionHero].startsound) } } if (inRect(pos, charBox) || inRect(pos, makeBox([0, 222], [416, 152]))) this.charmode = !this.charmode if (inRect(pos, LVDown) && this.selectionLV > 0) this.selectionLV-- if (inRect(pos, LvUP) && this.selectionLV < flags.D290605F2869068624508C972B6C6AE0[this.selectionHero].a) this.selectionLV++ if (inRect(pos, 翻页)) this.page = !this.page if (inRect(pos, START)) return this.start() } } else { //横屏 if (this.page) { //第一页 const 普通 = makeBox([30, 129], [132, 39]), 困难 = makeBox([192, 129], [132, 39]), 噩梦 = makeBox([354, 129], [132, 39]), 疯狂 = makeBox([516, 129], [132, 39]), 新手教程 = makeBox([8, 380], [120, 30]), 进阶教程 = makeBox([156, 380], [120, 30]), 切图左 = makeBox([65, 30], [64, 64]), 切图右 = makeBox([547, 30], [64, 64]), 翻页 = makeBox([548, 380], [120, 30]); if (inRect(pos, 普通)) this.selectionHard = "普通" if (inRect(pos, 困难)) this.selectionHard = "困难" if (inRect(pos, 噩梦)) this.selectionHard = "噩梦" if (inRect(pos, 疯狂)) this.selectionHard = "疯狂" if (inRect(pos, 切图左)) this.selectionMap = this.selectionMap === 0 ? (this.MapScene.length - 1) : (this.selectionMap - 1) if (inRect(pos, 切图右)) this.selectionMap = this.selectionMap === this.MapScene.length - 1 ? 0 : (this.selectionMap + 1) if (inRect(pos, 翻页)) { this.page = !this.page this.selectionLV = flags.D290605F2869068624508C972B6C6AE0[this.selectionHero].a } if (inRect(pos, 新手教程)) { this.selectionTeach = 1 return this.start() } if (inRect(pos, 进阶教程)) { this.selectionTeach = 2 return this.start() } } else { //第二页 const heroBox = makeBox([6, 8], [663, 123]), charBox = makeBox([170, 386], [64, 23]), 翻页 = makeBox([10, 139], [120, 30]), LVDown = makeBox([570, 190], [32, 32]), LvUP = makeBox([626, 190], [32, 32]), START = makeBox([10, 178], [120, 30]); if (inRect(pos, heroBox)) { const Ma = Math.floor((py - 8) / 62) * 13 + Math.floor((px - 6) / 51) if (Ma < this.char.length) { this.selectionHero = this.char[Math.floor((py - 8) / 62) * 13 + Math.floor((px - 6) / 51)] this.selectionLV = flags.D290605F2869068624508C972B6C6AE0[this.selectionHero].a core.stopSound() core.playSound(core.plugin.Character[this.selectionHero].startsound) } } if (inRect(pos, LVDown) && this.selectionLV > 0) this.selectionLV-- if (inRect(pos, LvUP) && this.selectionLV < flags.D290605F2869068624508C972B6C6AE0[this.selectionHero].a) this.selectionLV++ if (inRect(pos, charBox) || inRect(pos, makeBox([166, 180], [306, 200]))) this.charmode = !this.charmode if (inRect(pos, 翻页)) this.page = !this.page if (inRect(pos, START)) return this.start() } } this.update() } init() { this.page = true this.selectionHero = "勇者" this.selectionLV = 0 this.MapScene = ["星趴·梦想号", "御魂庆典", "水乡古镇", "魔法学院", "龙宫游乐园"] this.selectionMap = 0 this.selectionHard = "普通" this.selectionTeach = 0 this.heroselected = "" this.LVselected = 0 this.Mapselected = "" this.Hardselected = "" this.update() } update() { select.style.display = "block" if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(ctx, 416, 676) core.clearMap(ctx) core.drawImage(ctx, "VerticalStartUI.png", 0, 0, 416, 676) if (this.page) { //第一页 core.setTextAlign(ctx, "left") core.playBgm("loading.mp3") ctx.save() ctx.beginPath() ctx.moveTo(55, 18) ctx.lineTo(361, 18) ctx.arcTo(368, 18, 368, 126, 7) ctx.lineTo(368, 119) ctx.arcTo(368, 126, 48, 126, 7) ctx.lineTo(55, 126) ctx.arcTo(48, 126, 48, 18, 7) ctx.lineTo(48, 25) ctx.arcTo(48, 18, 368, 18, 7) ctx.closePath() ctx.clip() core.drawImage(ctx, "MapScene" + this.selectionMap + ".png", 48, 18, 320, 88) core.fillBoldText(ctx, this.MapScene[this.selectionMap], 56, 66, "#FFF", "#000", 4, "18px STHUPO") core.fillRect(ctx, 48, 106, 320, 20, "#000") ctx.font = "12px STHUPO" ctx.textBaseline = "top" ctx.textAlign = "center" ctx.fillStyle = "#FFF" ctx.fillText(this.TipInfo[this.selectionMap], 208, 110) ctx.restore() core.drawImage(ctx, "Left_set.png", 0, 44, 48, 48) core.drawImage(ctx, "Right_set.png", 368, 44, 48, 48) core.setTextAlign(ctx, "center") core.fillRoundRect(ctx, 23, 144, 75, 30, 8, "#333") core.fillRoundRect(ctx, 121, 144, 75, 30, 8, "#333") core.fillRoundRect(ctx, 219, 144, 75, 30, 8, "#333") core.fillRoundRect(ctx, 317, 144, 75, 30, 8, "#333") core.strokeRoundRect(ctx, 23, 144, 75, 30, 8, this.selectionHard === "普通" ? "#00FFFF" : "#999999", 2) core.strokeRoundRect(ctx, 121, 144, 75, 30, 8, this.selectionHard === "困难" ? "#FFFF00" : "#999999", 2) core.strokeRoundRect(ctx, 219, 144, 75, 30, 8, this.selectionHard === "噩梦" ? "#FF00FF" : "#999999", 2) core.strokeRoundRect(ctx, 317, 144, 75, 30, 8, this.selectionHard === "疯狂" ? "#FF2222" : "#999999", 2) core.fillText(ctx, "普 通", 60.5, 163, this.selectionHard === "普通" ? "#0FF" : "#CCC", "16px STHUPO") core.fillText(ctx, "困 难", 158.5, 163, this.selectionHard === "困难" ? "#FF0" : "#CCC", "16px STHUPO") core.fillText(ctx, "噩 梦", 256.5, 163, this.selectionHard === "噩梦" ? "#F0F" : "#CCC", "16px STHUPO") core.fillText(ctx, "疯 狂", 354.5, 163, this.selectionHard === "疯狂" ? "#F22" : "#CCC", "16px STHUPO") core.fillRoundRect(ctx, 8, 638, 100, 30, 8, "#333") core.fillRoundRect(ctx, 136, 638, 100, 30, 8, "#333") core.fillRoundRect(ctx, 308, 638, 100, 30, 8, "#333") core.strokeRoundRect(ctx, 8, 638, 100, 30, 8, "#FFF", 2) core.strokeRoundRect(ctx, 136, 638, 100, 30, 8, "#0FF", 2) core.strokeRoundRect(ctx, 308, 638, 100, 30, 8, "#FFF", 2) core.fillText(ctx, "新手教程", 58, 658, "#FFF", "16px STHUPO") core.fillText(ctx, "进阶教程", 186, 658, "#0FF", "16px STHUPO") core.fillBoldText(ctx, "选择角色", 358, 658, "#FFF", "#000", 3, "16px STHUPO") let diff = "#00FFFF" if (this.selectionHard === "困难") diff = "#FFFF00" if (this.selectionHard === "噩梦") diff = "#FF00FF" if (this.selectionHard === "疯狂") diff = "#FF2222" core.fillRoundRect(ctx, 8, 182, 400, 212, 8, "#333") core.fillRoundRect(ctx, 8, 406, 400, 224, 8, "#333") core.strokeRoundRect(ctx, 8, 182, 400, 212, 8, "#FFF", 2) core.strokeRoundRect(ctx, 8, 406, 400, 224, 8, diff, 2) core.drawTextContent(ctx, this.MapInfo[this.selectionMap], { left: 12, top: 186, color: "#FFFFFF", align: "left", maxWidth: 396, fontSize: 13, font: "cjk" }) core.drawTextContent(ctx, this.HardInfo[this.MapScene[this.selectionMap]][this.selectionHard], { left: 12, top: 410, color: "#FFFFFF", align: "left", maxWidth: 396, fontSize: 13, font: "cjk" }) } else { //第二页 core.setTextAlign(ctx, "left") core.playBgm("Character.mp3") const fn = this.selectionHero let px = 8 let py = 8 for (let i = 0; i < this.char.length; i++) { core.fillRoundRect(ctx, px, py, 43, 58, 8, "#333") core.strokeRoundRect(ctx, px, py, 43, 58, 8, this.char[i] === fn ? "#FF0" : "#999", 2) core.drawImage(ctx, core.plugin.Character[this.char[i]].Main, px + 0.5, py + 0.5, 42, 57) const lv = flags.D290605F2869068624508C972B6C6AE0[this.char[i]].a core.fillBoldText(ctx, lv, px + 36, py + 9, "#FFF", "#000", 3, "18px STHUPO") px += 51 if (i % 8 === 7) { px = 8 py += 66 } } core.drawImage(ctx, core.plugin.Character[fn].Avatar, 0, 476, 162, 200) core.fillBoldText(ctx, fn, 8, 217, "#FFF", "#000", 3, "18px STHUPO") core.fillBoldText(ctx, core.plugin.Character[fn].title, 16 + fn.length * (fn === "Z3000" ? 9 : 18), 217, "#FFF", "#000", 3, "18px STHUPO") core.drawTextContent(ctx, `\\i[hp]${core.plugin.Character[fn].hp}\\i[atk]${core.plugin.Character[fn].atk}\\i[def]${core.plugin.Character[fn].def}`, { left: 24 + fn.length * (fn === "Z3000" ? 9 : 18) + core.plugin.Character[fn].title.length * 18, top: 202, color: "#FFF", align: "left", maxWidth: 300, fontSize: 18, font: "STHUPO" }) core.fillRoundRect(ctx, -1, 222, 419, 152, 8, "#333") core.strokeRoundRect(ctx, -1, 222, 419, 152, 8, "#999", 2) if (this.charmode) { core.drawImage(ctx, "Main2_File.png", 8, 376, 64, 23) core.drawTextContent(ctx, core.plugin.Character[fn].file, { left: 8, top: 223, color: "#FFF", align: "left", maxWidth: 408, fontSize: 13, font: "cjk", }) } else { core.drawImage(ctx, "Main2_Skill.png", 8, 376, 64, 23) core.drawTextContent(ctx, "主动技能:" + core.plugin.Character[fn].ActiveSkill + "\n被动技能:" + core.plugin.Character[fn].PassiveSkill, { left: 8, top: 223, color: "#FFF", align: "left", maxWidth: 408, fontSize: 13, font: "cjk" }) } core.fillBoldText(ctx, "level." + flags.D290605F2869068624508C972B6C6AE0[fn].a, 225, 396, "#FFF", "#000", 3, "18px STHUPO") core.setTextAlign(ctx, "right") const LV = [1000, 1500, 2000, 3000, 6000, 30000] if (flags.D290605F2869068624508C972B6C6AE0[fn].a === 6) { core.fillText(ctx, "MAX", 408, 396, "#FFF", "14px STHUPO") core.drawImage(ctx, "exp_full.png", 225, 398, 183, 26) } else { core.fillText(ctx, flags.D290605F2869068624508C972B6C6AE0[fn].b + "/" + LV[flags.D290605F2869068624508C972B6C6AE0[fn].a], 408, 396, "#FFF", "14px STHUPO") core.drawImage(ctx, "exp_empty.png", 225, 398, 183, 26) core.drawImage(ctx, "exp_full.png", 0, 0, 2 + 179 * flags.D290605F2869068624508C972B6C6AE0[fn].b / LV[flags.D290605F2869068624508C972B6C6AE0[fn].a], 26, 225, 398, 2 + 179 * flags.D290605F2869068624508C972B6C6AE0[fn].b / LV[flags.D290605F2869068624508C972B6C6AE0[fn].a], 26) } core.setTextAlign(ctx, "center") core.fillBoldText(ctx, "应用等级:", 266, 445, "#FFF", "#000", 3, "18px STHUPO") core.fillBoldText(ctx, this.selectionLV, 356, 446, "#FFF", "#000", 3, "18px STHUPO") core.drawImage(ctx, "Left_" + (this.selectionLV < 1 ? "on" : "set") + ".png", 314, 424, 32, 32) core.drawImage(ctx, "Right_" + (this.selectionLV >= flags.D290605F2869068624508C972B6C6AE0[fn].a ? "on" : "set") + ".png", 366, 424, 32, 32) core.fillRoundRect(ctx, 226, 456, 180, 140, 8, "#333") core.strokeRoundRect(ctx, 226, 456, 180, 140, 8, "#999", 2) core.drawTextContent(ctx, core.plugin.Character[fn].PVELevelsText[this.selectionLV], { left: 228, top: 458, color: "#FFF", align: "left", maxWidth: 176, fontSize: 12, font: "cjk" }) core.fillRoundRect(ctx, 10, 399, 120, 30, 8, "#333") core.strokeRoundRect(ctx, 10, 399, 120, 30, 8, "#FFF", 2) core.fillBoldText(ctx, "选择地图", 70, 419, "#FFF", "#000", 3, "18px STHUPO") const gradient = ctx.createLinearGradient(10, 438, 130, 446) gradient.addColorStop(0, "red") gradient.addColorStop(0.14, "orange") gradient.addColorStop(0.28, "yellow") gradient.addColorStop(0.43, "green") gradient.addColorStop(0.57, "blue") gradient.addColorStop(0.71, "indigo") gradient.addColorStop(0.86, "violet") gradient.addColorStop(1, "red") core.fillRoundRect(ctx, 10, 438, 120, 30, 8, "#333") core.strokeRoundRect(ctx, 10, 438, 120, 30, 8, gradient, 2) core.fillBoldText(ctx, "开始游戏", 70, 457, "#FFF", "#000", 3, "18px STHUPO") } } else { core.maps._setHDCanvasSize(ctx, 676, 416) core.clearMap(ctx) core.drawImage(ctx, "StartUI.png", 0, 0, 676, 416) if (this.page) { //第一页 core.setTextAlign(ctx, "left") core.playBgm("loading.mp3") ctx.save() ctx.beginPath() ctx.moveTo(146, 7) ctx.lineTo(522, 7) ctx.arcTo(538, 7, 538, 117, 8) ctx.lineTo(538, 109) ctx.arcTo(538, 117, 138, 117, 8) ctx.lineTo(146, 117) ctx.arcTo(138, 117, 138, 7, 8) ctx.lineTo(138, 15) ctx.arcTo(138, 7, 538, 7, 8) ctx.closePath() ctx.clip() core.drawImage(ctx, "MapScene" + this.selectionMap + ".png", 138, 7, 400, 110) core.fillBoldText(ctx, this.MapScene[this.selectionMap], 148, 60, "#FFF", "#000", 4, "22px STHUPO") core.fillRect(ctx, 138, 97, 400, 20, "#000") ctx.font = "14px STHUPO" ctx.textBaseline = "top" ctx.textAlign = "center" ctx.fillStyle = "#FFF" ctx.fillText(this.TipInfo[this.selectionMap], 338, 100) ctx.restore() core.drawImage(ctx, "Left_set.png", 65, 30, 64, 64) core.drawImage(ctx, "Right_set.png", 547, 30, 64, 64) let diff = "#00FFFF" if (this.selectionHard === "困难") diff = "#FFFF00" if (this.selectionHard === "噩梦") diff = "#FF00FF" if (this.selectionHard === "疯狂") diff = "#FF2222" core.setTextAlign(ctx, "center") core.fillRoundRect(ctx, 30, 129, 132, 39, 8, "#333") core.fillRoundRect(ctx, 192, 129, 132, 39, 8, "#333") core.fillRoundRect(ctx, 354, 129, 132, 39, 8, "#333") core.fillRoundRect(ctx, 516, 129, 132, 39, 8, "#333") core.fillRoundRect(ctx, 8, 180, 324, 194, 8, "#333") core.fillRoundRect(ctx, 344, 180, 324, 194, 8, "#333") core.strokeRoundRect(ctx, 30, 129, 132, 39, 8, this.selectionHard === "普通" ? "#00FFFF" : "#999999", 2) core.strokeRoundRect(ctx, 192, 129, 132, 39, 8, this.selectionHard === "困难" ? "#FFFF00" : "#999999", 2) core.strokeRoundRect(ctx, 354, 129, 132, 39, 8, this.selectionHard === "噩梦" ? "#FF00FF" : "#999999", 2) core.strokeRoundRect(ctx, 516, 129, 132, 39, 8, this.selectionHard === "疯狂" ? "#FF2222" : "#999999", 2) core.fillText(ctx, "普 通", 96, 154, this.selectionHard === "普通" ? "#0FF" : "#CCC", "20px STHUPO") core.fillText(ctx, "困 难", 258, 154, this.selectionHard === "困难" ? "#FF0" : "#CCC", "20px STHUPO") core.fillText(ctx, "噩 梦", 420, 154, this.selectionHard === "噩梦" ? "#F0F" : "#CCC", "20px STHUPO") core.fillText(ctx, "疯 狂", 582, 154, this.selectionHard === "疯狂" ? "#F22" : "#CCC", "20px STHUPO") core.strokeRoundRect(ctx, 8, 180, 324, 194, 8, "#FFF", 2) core.strokeRoundRect(ctx, 344, 180, 324, 194, 8, diff, 2) core.drawTextContent(ctx, this.MapInfo[this.selectionMap], { left: 12, top: 184, color: "#FFFFFF", align: "left", maxWidth: 318, fontSize: 13, font: "cjk" }) core.drawTextContent(ctx, this.HardInfo[this.MapScene[this.selectionMap]][this.selectionHard], { left: 348, top: 184, color: "#FFFFFF", align: "left", maxWidth: 318, fontSize: 13, font: "cjk" }) core.fillRoundRect(ctx, 8, 380, 120, 30, 8, "#333") core.fillRoundRect(ctx, 156, 380, 120, 30, 8, "#333") core.fillRoundRect(ctx, 548, 380, 120, 30, 8, "#333") core.strokeRoundRect(ctx, 8, 380, 120, 30, 8, "#FFF", 2) core.strokeRoundRect(ctx, 156, 380, 120, 30, 8, "#0FF", 2) core.strokeRoundRect(ctx, 548, 380, 120, 30, 8, "#FFF", 2) core.fillText(ctx, "新手教程", 68, 400, "#FFF", "18px STHUPO") core.fillText(ctx, "进阶教程", 216, 400, "#0FF", "18px STHUPO") core.fillBoldText(ctx, "选择角色", 608, 400, "#FFF", "#000", 3, "18px STHUPO") } else { //第二页 core.setTextAlign(ctx, "left") core.playBgm("Character.mp3") const fn = this.selectionHero let px = 10 for (let i = 0; i < 13; i++) { core.fillRoundRect(ctx, px, 8, 43, 58, 8, "#333") core.strokeRoundRect(ctx, px, 8, 43, 58, 8, this.char[i] === fn ? "#FF0" : "#999", 2) core.drawImage(ctx, core.plugin.Character[this.char[i]].Main, px + 0.5, 8.5, 42, 57) const lv = flags.D290605F2869068624508C972B6C6AE0[this.char[i]].a core.fillBoldText(ctx, lv, px + 36, 17, "#FFF", "#000", 3, "18px STHUPO") px += 51 } px = 10 for (let i = 13; i < this.char.length; i++) { core.fillRoundRect(ctx, px, 73, 43, 58, 8, "#333") core.strokeRoundRect(ctx, px, 73, 43, 58, 8, this.char[i] === fn ? "#FF0" : "#999", 2) core.drawImage(ctx, core.plugin.Character[this.char[i]].Main, px + 0.5, 73.5, 42, 57) const lv = flags.D290605F2869068624508C972B6C6AE0[this.char[i]].a core.fillBoldText(ctx, lv, px + 36, 82, "#FFF", "#000", 3, "18px STHUPO") px += 51 } core.drawImage(ctx, core.plugin.Character[fn].Avatar, 0, 216, 162, 200) core.fillBoldText(ctx, fn, 170, 150, "#FFF", "#000", 3, "18px STHUPO") core.fillBoldText(ctx, core.plugin.Character[fn].title, 182 + fn.length * (fn === "Z3000" ? 9 : 18), 150, "#FFF", "#000", 3, "18px STHUPO") core.drawTextContent(ctx, `\\i[hp]${core.plugin.Character[fn].hp}\\i[atk]${core.plugin.Character[fn].atk}\\i[def]${core.plugin.Character[fn].def}`, { left: 168, top: 158, color: "#FFF", align: "left", maxWidth: 300, fontSize: 18, font: "STHUPO" }) core.fillRoundRect(ctx, 166, 180, 306, 200, 8, "#333") core.strokeRoundRect(ctx, 166, 180, 306, 200, 8, "#999", 2) if (this.charmode) { core.drawImage(ctx, "Main2_File.png", 170, 386, 64, 23) core.drawTextContent(ctx, core.plugin.Character[fn].file, { left: 170, top: 182, color: "#FFF", align: "left", maxWidth: 298, fontSize: 12, font: "cjk", }) } else { core.drawImage(ctx, "Main2_Skill.png", 170, 386, 64, 23) core.drawTextContent(ctx, "主动技能:" + core.plugin.Character[fn].ActiveSkill + "\n被动技能:" + core.plugin.Character[fn].PassiveSkill, { left: 170, top: 182, color: "#FFF", align: "left", maxWidth: 298, fontSize: 12, font: "cjk" }) } core.fillBoldText(ctx, "level." + flags.D290605F2869068624508C972B6C6AE0[fn].a, 486, 158, "#FFF", "#000", 3, "18px STHUPO") core.setTextAlign(ctx, "right") const LV = [1000, 1500, 2000, 3000, 6000, 30000] if (flags.D290605F2869068624508C972B6C6AE0[fn].a === 6) { core.fillText(ctx, "MAX", 669, 158, "#FFF", "14px STHUPO") core.drawImage(ctx, "exp_full.png", 486, 163, 183, 26) } else { core.fillText(ctx, flags.D290605F2869068624508C972B6C6AE0[fn].b + "/" + LV[flags.D290605F2869068624508C972B6C6AE0[fn].a], 669, 158, "#FFF", "14px STHUPO") core.drawImage(ctx, "exp_empty.png", 486, 163, 183, 26) core.drawImage(ctx, "exp_full.png", 0, 0, 2 + 179 * flags.D290605F2869068624508C972B6C6AE0[fn].b / LV[flags.D290605F2869068624508C972B6C6AE0[fn].a], 26, 486, 163, 2 + 179 * flags.D290605F2869068624508C972B6C6AE0[fn].b / LV[flags.D290605F2869068624508C972B6C6AE0[fn].a], 26) } core.setTextAlign(ctx, "center") core.fillBoldText(ctx, "应用等级:", 525, 211, "#FFF", "#000", 3, "18px STHUPO") core.drawImage(ctx, "Left_" + (this.selectionLV < 1 ? "on" : "set") + ".png", 570, 190, 32, 32) core.drawImage(ctx, "Right_" + (this.selectionLV >= flags.D290605F2869068624508C972B6C6AE0[fn].a ? "on" : "set") + ".png", 626, 190, 32, 32) core.fillBoldText(ctx, this.selectionLV, 614, 213, "#FFF", "#000", 3, "18px STHUPO") core.fillRoundRect(ctx, 486, 222, 180, 140, 8, "#333") core.strokeRoundRect(ctx, 486, 222, 180, 140, 8, "#999", 2) core.drawTextContent(ctx, core.plugin.Character[fn].PVELevelsText[this.selectionLV], { left: 488, top: 224, color: "#FFF", align: "left", maxWidth: 176, fontSize: 12, font: "cjk" }) core.fillRoundRect(ctx, 10, 139, 120, 30, 8, "#333") core.strokeRoundRect(ctx, 10, 139, 120, 30, 8, "#FFF", 2) core.fillBoldText(ctx, "选择地图", 70, 159, "#FFF", "#000", 3, "18px STHUPO") const gradient = ctx.createLinearGradient(10, 178, 130, 186) gradient.addColorStop(0, "red") gradient.addColorStop(0.14, "orange") gradient.addColorStop(0.28, "yellow") gradient.addColorStop(0.43, "green") gradient.addColorStop(0.57, "blue") gradient.addColorStop(0.71, "indigo") gradient.addColorStop(0.86, "violet") gradient.addColorStop(1, "red") core.fillRoundRect(ctx, 10, 178, 120, 30, 8, "#333") core.strokeRoundRect(ctx, 10, 178, 120, 30, 8, gradient, 2) core.fillBoldText(ctx, "开始游戏", 70, 197, "#FFF", "#000", 3, "18px STHUPO") } } } start() { core.status.route.push("input2:" + core.encodeBase64(core.encodeBase64(this.selectionTeach.toString()))) if (!this.selectionTeach) { this.selectHero() this.selectLV() this.selectMap() this.selectHard() } main.dom.outerBackground.style.display = "block" main.dom.outerUI.style.display = "block" select.style.display = "none" core.doAction() } selectHero() { this.heroselected = this.selectionHero core.status.route.push("input2:" + core.encodeBase64(core.encodeBase64(this.heroselected))) } selectLV() { this.LVselected = this.selectionLV core.status.route.push("input2:" + core.encodeBase64(core.encodeBase64(this.LVselected.toString()))) } selectMap() { this.Mapselected = this.MapScene[this.selectionMap] core.status.route.push("input2:" + core.encodeBase64(core.encodeBase64(this.Mapselected))) } selectHard() { this.Hardselected = this.selectionHard core.status.route.push("input2:" + core.encodeBase64(core.encodeBase64(this.Hardselected))) } selectTeach() { this.selectionTeach = 0 this.start() } } core.ui.select = new Select() }, "楼层打乱": function () { // 在此增加新插件 function generate_floor_list() { var floors1 = [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50 ]; function not_zero(list) { var cnt = 0; for (var i of list) { if (i != 0) cnt++; } return cnt; } function fill4(list) { for (var i = 0; i < 5; i++) { if (i == 2) continue; //随机列表中的一个楼层 while (true) { var r = core.rand(4); if (list[r] != 0) { //随机区域内的一个楼层 while (true) { var r2 = core.rand(9) + 1; if (floors1[i * 10 + r2] == 0) { floors1[i * 10 + r2] = list[r]; list[r] = 0; break; } } break; } } } } //填充boss var boss = [10, 20, 40, 49]; var boss2 = [10, 20, 40, 49]; for (var i = 0; i < 4; i++) { while (true) { var r = core.rand(4); if (boss[r] != 0) { floors1[boss2[i]] = boss[r]; boss[r] = 0; break; } } } //填充红钥匙,剑,盾,商店 var redKey = [8, 14, 34, 42]; var sword = [5, 17, 33, 48]; var shield = [9, 11, 38, 43]; var shop = [4, 12, 32, 46]; fill4(redKey); fill4(sword); fill4(shield); fill4(shop); var plain = [1, 2, 3, 6, 7, 13, 15, 16, 18, 19, 31, 35, 36, 37, 39, 41, 44, 45, 47]; //随机填充其他剩余层 for (var i = 1; i <= 19; i++) { while (true) { var r = core.rand(19); if (plain[r] != 0) { //随机全塔的一个楼层 while (true) { var r2 = core.rand(49) + 1; if (floors1[r2] == 0) { floors1[r2] = plain[r]; plain[r] = 0; break; } } break; } } } return floors1; } this.randomFloors = function () { var cross = [9, 19, 39, 46]; var floors1 = generate_floor_list() //不断生成,直到符合规则 while (true) { var pass = false; floors1 = generate_floor_list() //检查十字架 for (var i = 11; i <= 19; i++) { for (var j = 0; j < 4; j++) { if (floors1[i] == cross[j]) { pass = true; } } } //檢查44層 if (!pass || floors1[1] == 44 || floors1[1] == 41 || floors1[11] == 44 || floors1[31] == 44 || floors1[41] == 44) { continue; } else { break; } } // 重写core.floorIds数组 var floors = []; floors.push("SC0"); for (var i = 1; i <= 20; i++) { var floorId = "SC" + floors1[i] + "_" + Math.ceil(i / 10); core.status.maps[floorId].title = "御魂庆典:" + i + "F"; core.status.maps[floorId].name = "第 " + i + " 层" floors.push(floorId); } for (var i = 21; i <= 30; i++) { var floorId = "SC" + floors1[i] floors.push(floorId); } for (var i = 31; i <= 40; i++) { var floorId = "SC" + floors1[i] + "_" + Math.ceil(i / 10) core.status.maps[floorId].title = "御魂庆典:" + i + "F"; core.status.maps[floorId].name = "第 " + i + " 层" floors.push(floorId); } for (var i = 41; i <= 49; i++) { var floorId = "SC" + floors1[i] + "_" + Math.ceil(i / 10) core.status.maps[floorId].title = "御魂庆典:" + i + "F"; core.status.maps[floorId].name = "第 " + i + " 层" floors.push(floorId); } floors.push('SC50') core.setFlag("allfloorIds", floors); core.floorIds = floors const Hard = flags.MapStatus?.Hard let b = {} function filter2(block) { return block.event.id.endsWith("Gem2") } function filter3(block) { return block.event.id.endsWith("Gem3") } function filter4(block) { return block.event.id.endsWith("Gem4") } switch (Hard) { case '普通': for (let i = 0; i < core.rand(10) + 1; i++) core.rand() case "困难": for (let i = 0; i < core.rand(10) + 20; i++) core.rand() case "噩梦": for (let i = 0; i < core.rand(10) + core.rand(10) + 1; i++) core.rand() case "疯狂": for (let i = 0; i < core.rand(10) + core.rand(20) + 20; i++) core.rand() } core.floorIds.forEach(v => { const Gem2 = core.searchBlockWithFilter(filter2, v) const Gem3 = core.searchBlockWithFilter(filter3, v) const Gem4 = core.searchBlockWithFilter(filter4, v) Gem2.forEach(v => b[`${v.x}-${v.y}-${v.floorId}`] = core.rand(3) + 1) Gem3.forEach(v => b[`${v.x}-${v.y}-${v.floorId}`] = core.rand(6) + 1) Gem4.forEach(v => b[`${v.x}-${v.y}-${v.floorId}`] = core.rand(10) + 1) switch (Hard) { case '疯狂': if (['SC9_2', 'SC19_2', 'SC39_2', 'SC46_2'].includes(v)) { const cross = core.searchBlock('cross', v)[0] core.setBlock(421, cross.x, cross.y, v) } case "噩梦": if (['SC37_1', 'SC37_2', 'SC37_4', 'SC37_5'].includes(v)) { core.setBlock(421, 5, 5, v) } case "困难": if (['SC10_5', 'SC20_5', 'SC40_5', 'SC49_5'].includes(v)) { const knife = core.searchBlock('knife', v)[0] core.setBlock(421, knife.x, knife.y, v) } } }) for (let i = 0; i < core.floorIds.length; i++) { if (core.floorIds[i].startsWith("SC37")) core.setFlag("37ratio", Math.ceil(i / 10)); //记录地震卷轴位置并回传给SC47商人 if (core.floorIds[i].startsWith("SC44")) core.setFlag("44floor", i); } core.setFlag("gemRandom", b) } }, "地图初始化": function () { function filter(block) { return block.event.cls.indexOf("enemy") === 0 } this.全地图通用初始化 = function () { let k = 10 switch (flags.MapStatus?.Hard) { case "疯狂": for (let i = 0; i <= core.rand(150) + 150; i++) core.rand() flags.价格 = 35 k = 15; break; case "噩梦": for (let i = 0; i <= core.rand(100) + 100; i++) core.rand() flags.价格 = 30 k = 13; break; case "困难": for (let i = 0; i <= core.rand(50) + 50; i++) core.rand() flags.价格 = 25 k = 11; break; } flags.史莱姆 = flags.暗夜 = flags.守护 = flags.神秘 = flags.魔法师 = flags.人类 = flags.骷髅 = 0 core.addItem("book"); core.addItem("fly"); ["greenSlime", "redSlime", "blackSlime", "slimelord", "slimeMan", "bat", "bigBat", "redBat", "vampire", "zombie", "zombieKnight", "skeleton", "skeletonSoilder", "skeletonCaptain", "ghostSkeleton", "bluePriest", "redPriest", "brownWizard", "redWizard", "blackKing", "whiteKing", "blackMagician", "swordsman", "soldier", "yellowKnight", "redKnight", "darkKnight", "rock", "yellowGuard", "blueGuard", "redGuard", "redKing", "swordEmperor", "octopus", "magicDragon", "SeaGawu", "Masao", "LionGawu", "ADMasao", "SOMasao", "Thief", "SharkPirate", "KaniBro", "Loudmouth", "Trainee", "GoldenFish", "EbiBro", "Souri", "Mamushi"].forEach(v => { const enemy = core.material.enemys[v] let hp = Math.floor(enemy.hp * k / 10), atk = Math.floor(enemy.atk * k / 10), def = Math.floor(enemy.def * k / 10); if (flags.MapStatus?.Map === "御魂庆典" && v === "redKing") { hp = Math.floor(hp / 10) atk = Math.floor(atk / 10) def = Math.floor(def / 10) } core.setEnemy(v, "hp", hp, "=", void 0, true) core.setEnemy(v, "atk", atk, "=", void 0, true) core.setEnemy(v, "def", def, "=", void 0, true) }) } this.梦想号初始化 = function () { let b = {} b["5-1-MT11"] = core.rand(3) + 1 b["6-1-MT12"] = core.rand(3) + 1 b["10-6-MT12"] = core.rand(3) + 1 b["5-1-MT14"] = core.rand(3) + 1 b["1-1-MT15"] = core.rand(3) + 1 b["1-5-MT16"] = core.rand(3) + 1 b["1-7-MT16"] = core.rand(3) + 1 b["9-1-MT17"] = core.rand(3) + 1 b["11-1-MT17"] = core.rand(3) + 1 b["2-11-MT18"] = core.rand(3) + 1 b["10-11-MT18"] = core.rand(3) + 1 b["11-1-MT19"] = core.rand(3) + 1 b["1-4-MT20"] = core.rand(3) + 1 b["2-4-MT20"] = core.rand(3) + 1 b["4-5-MT20"] = core.rand(3) + 1 b["4-6-MT20"] = core.rand(3) + 1 b["4-7-MT20"] = core.rand(3) + 1 b["8-7-MT20"] = core.rand(3) + 1 b["8-6-MT20"] = core.rand(3) + 1 b["8-5-MT20"] = core.rand(3) + 1 b["8-4-MT31"] = core.rand(6) + 1 b["4-8-MT31"] = core.rand(6) + 1 b["1-1-MT32"] = core.rand(6) + 1 b["2-2-MT32"] = core.rand(6) + 1 b["11-1-MT34"] = core.rand(6) + 1 b["11-11-MT34"] = core.rand(6) + 1 b["3-7-MT37"] = core.rand(6) + 1 b["4-7-MT37"] = core.rand(6) + 1 b["7-7-MT37"] = core.rand(6) + 1 b["8-7-MT37"] = core.rand(6) + 1 b["9-7-MT37"] = core.rand(6) + 1 b["7-8-MT37"] = core.rand(6) + 1 b["8-8-MT37"] = core.rand(6) + 1 b["9-8-MT37"] = core.rand(6) + 1 b["5-8-MT38"] = core.rand(6) + 1 b["11-6-MT39"] = core.rand(6) + 1 b["6-9-MT39"] = core.rand(6) + 1 b["8-2-MT40"] = core.rand(6) + 1 b["9-2-MT40"] = core.rand(6) + 1 b["10-2-MT40"] = core.rand(6) + 1 b["7-4-MT40"] = core.rand(6) + 1 b["8-4-MT40"] = core.rand(6) + 1 b["9-4-MT40"] = core.rand(6) + 1 b["1-9-MT40"] = core.rand(6) + 1 b["1-11-MT40"] = core.rand(6) + 1 b["4-11-MT41"] = core.rand(10) + 1 b["8-11-MT41"] = core.rand(10) + 1 b["3-11-MT42"] = core.rand(10) + 1 b["1-3-MT45"] = core.rand(10) + 1 b["2-3-MT45"] = core.rand(10) + 1 b["1-5-MT45"] = core.rand(10) + 1 b["2-5-MT45"] = core.rand(10) + 1 b["1-3-MT46"] = core.rand(10) + 1 b["11-2-MT47"] = core.rand(10) + 1 b["6-10-MT47"] = core.rand(10) + 1 b["6-8-MT47"] = core.rand(10) + 1 b["5-8-MT47"] = core.rand(10) + 1 b["3-8-MT48"] = core.rand(10) + 1 b["3-11-MT48"] = core.rand(10) + 1 b["2-4-MT49"] = core.rand(10) + 1 b["3-4-MT49"] = core.rand(10) + 1 b["4-4-MT49"] = core.rand(10) + 1 b["8-4-MT49"] = core.rand(10) + 1 b["9-4-MT49"] = core.rand(10) + 1 b["10-4-MT49"] = core.rand(10) + 1 core.setFlag("gemRandom", b) switch (flags.MapStatus?.Hard) { case "疯狂": const floorIds = [] for (let i = 11; i <= 49; i++) { floorIds.push("MT" + i) } const special1 = [1, 2, void 0, 3, 4, void 0, 5, 7, void 0, 8, 10, void 0, 11, 15, void 0, 22, 29, void 0, 17, 27] const special2 = [1, void 0, void 0, void 0, void 0, 2, void 0, void 0, void 0, void 0, 3, void 0, void 0, void 0, void 0, 4, void 0, void 0, void 0, void 0, 5, void 0, void 0, void 0, void 0, 7, void 0, void 0, void 0, void 0, 8, void 0, void 0, void 0, void 0, 10, void 0, void 0, void 0, void 0, 11, void 0, void 0, void 0, void 0, 15, void 0, void 0, void 0, void 0, 22, void 0, void 0, void 0, void 0, 29, void 0, void 0, void 0, void 0, 17, void 0, void 0, void 0, void 0, 27, void 0, void 0, void 0, void 0] const enemys = core.maps.searchBlockWithFilter(filter, floorIds) enemys.forEach(v => { if (v.block.event.id === "yellowKnight" || v.block.event.id === "swordEmperor" || v.block.event.id === "magicDragon" || v.block.event.id === "octopus" || v.block.event.id === "blackMagician") return if (v.block.event.id === "blackKing") { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length - 1) const rand2 = core.rand(special2.length - 5) const a = special1[rand] const c = special2[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) return } const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length) const rand2 = core.rand(special2.length) const a = special1[rand] const c = special2[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) core.setBlock(421, 5, 5, "MT37") core.setBlock(421, 6, 3, "MT19") break case "噩梦": const floorIds2 = [] for (let i = 31; i <= 49; i++) { floorIds2.push("MT" + i) } const special12 = [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 29, void 0, 27, void 0] const enemys2 = core.maps.searchBlockWithFilter(filter, floorIds2) enemys2.forEach(v => { if (v.block.event.id === "yellowKnight" || v.block.event.id === "swordEmperor" || v.block.event.id === "magicDragon") return if (v.block.event.id === "blackKing") { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special12.length - 2) const a = special12[rand] const b = [...special] if (a) b.push(a) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) return } const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special12.length) const a = special12[rand] const b = [...special] if (a) b.push(a) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) core.setBlock(421, 5, 5, "MT37") break } } this.水乡初始化 = function () { let b = {} b["0-4-SX2_1"] = core.rand(3) + 1 b["12-4-SX2_1"] = core.rand(3) + 1 b["12-6-SX2_1"] = core.rand(3) + 1 b["4-0-SX2_2"] = core.rand(3) + 1 b["5-12-SX2_2"] = core.rand(3) + 1 b["7-12-SX2_2"] = core.rand(3) + 1 b["11-10-SX2_2"] = core.rand(3) + 1 b["12-11-SX2_2"] = core.rand(3) + 1 b["8-10-SX2_3"] = core.rand(6) + 1 b["10-10-SX2_3"] = core.rand(6) + 1 b["12-10-SX2_3"] = core.rand(6) + 1 b["8-12-SX2_3"] = core.rand(6) + 1 b["10-12-SX2_3"] = core.rand(6) + 1 b["12-12-SX2_3"] = core.rand(6) + 1 b["9-11-SX2_3"] = core.rand(6) + 1 b["11-11-SX2_3"] = core.rand(6) + 1 b["1-3-SX2_4"] = core.rand(3) + 1 b["3-3-SX2_4"] = core.rand(3) + 1 b["8-0-SX2_4"] = core.rand(3) + 1 b["9-0-SX2_4"] = core.rand(3) + 1 b["11-0-SX2_4"] = core.rand(3) + 1 b["12-0-SX2_4"] = core.rand(3) + 1 b["8-1-SX2_4"] = core.rand(3) + 1 b["12-1-SX2_4"] = core.rand(3) + 1 b["1-8-SX2_5"] = core.rand(3) + 1 b["3-10-SX2_5"] = core.rand(3) + 1 b["8-2-SX2_5"] = core.rand(3) + 1 b["12-2-SX2_5"] = core.rand(3) + 1 b["5-0-SX3_2"] = core.rand(6) + 1 b["12-0-SX3_2"] = core.rand(6) + 1 b["6-1-SX3_2"] = core.rand(6) + 1 b["0-6-SX3_2"] = core.rand(6) + 1 b["12-6-SX3_2"] = core.rand(6) + 1 b["3-0-SX3_3"] = core.rand(6) + 1 b["4-0-SX3_3"] = core.rand(6) + 1 b["8-0-SX3_3"] = core.rand(6) + 1 b["12-0-SX3_3"] = core.rand(6) + 1 b["1-7-SX3_3"] = core.rand(6) + 1 b["2-7-SX3_3"] = core.rand(6) + 1 b["3-7-SX3_3"] = core.rand(6) + 1 b["9-7-SX3_3"] = core.rand(6) + 1 b["10-7-SX3_3"] = core.rand(6) + 1 b["11-7-SX3_3"] = core.rand(6) + 1 b["1-6-SX3_4"] = core.rand(6) + 1 b["1-10-SX3_4"] = core.rand(6) + 1 b["6-1-SX3_4"] = core.rand(10) + 1 b["0-12-SX3_5"] = core.rand(10) + 1 b["0-0-SX3_5"] = core.rand(10) + 1 b["11-1-SX4_1"] = core.rand(10) + 1 b["11-1-SX4_1"] = core.rand(10) + 1 b["1-0-SX4_2"] = core.rand(10) + 1 b["9-5-SX4_2"] = core.rand(10) + 1 b["4-12-SX4_2"] = core.rand(10) + 1 b["12-12-SX4_2"] = core.rand(10) + 1 b["1-4-SX4_3"] = core.rand(10) + 1 b["2-4-SX4_3"] = core.rand(10) + 1 b["3-4-SX4_3"] = core.rand(10) + 1 b["9-4-SX4_3"] = core.rand(10) + 1 b["10-4-SX4_3"] = core.rand(10) + 1 b["11-4-SX4_3"] = core.rand(10) + 1 b["0-0-SX4_4"] = core.rand(10) + 1 b["1-0-SX4_4"] = core.rand(10) + 1 b["0-6-SX4_4"] = core.rand(10) + 1 b["3-6-SX4_4"] = core.rand(10) + 1 b["0-4-SX4_5"] = core.rand(10) + 1 b["10-2-SX4_5"] = core.rand(10) + 1 b["1-6-SX4_5"] = core.rand(10) + 1 b["1-8-SX4_5"] = core.rand(10) + 1 b["4-6-SX4_5"] = core.rand(10) + 1 b["4-8-SX4_5"] = core.rand(10) + 1 core.setFlag("gemRandom", b) core.getItem("yellowKey", 4) core.getItem("blueKey") switch (flags.MapStatus?.Hard) { case "噩梦": const floorIds = ["SX1_1", "SX1_2", "SX1_3", "SX1_4", "SX1_5", "SX2_1", "SX2_2", "SX2_3", "SX2_4", "SX2_5", "SX3_1", "SX3_2", "SX3_3", "SX3_4", "SX3_5", "SX4_1", "SX4_2", "SX4_3", "SX4_4", "SX4_5"] function filter224(block) { const enemyList = [ "slimeMan", "ghostSkeleton", "blueGuard", "redGuard", "brownWizard", "redWizard", "swordsman", "soldier", "yellowKnight", "redKnight", "darkKnight", "whiteKing", "slimelord", "redBat", ] return enemyList.includes(block.event.id) } const special1 = [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 27, void 0, 29, void 0] const enemys = core.maps.searchBlockWithFilter(filter224, floorIds) enemys.forEach(v => { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length) const a = special1[rand] const b = [...special] if (a) b.push(special1[rand]) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) core.setBlock(421, 10, 11, "SX2_3") break case "疯狂": const floorIds2 = ["SX1_1", "SX1_2", "SX1_3", "SX1_4", "SX1_5", "SX2_1", "SX2_2", "SX2_3", "SX2_4", "SX2_5", "SX3_1", "SX3_2", "SX3_3", "SX3_4", "SX3_5", "SX4_1", "SX4_2", "SX4_3", "SX4_4", "SX4_5"] function filter112(block) { const enemyList2 = [ "slimeMan", "ghostSkeleton", "blueGuard", "redGuard", "brownWizard", "redWizard", "swordsman", "soldier", "yellowKnight", "redKnight", "darkKnight", "whiteKing", "slimelord", "redBat", "blackSlime", "bigBat", "zombie", "zombieKnight", "rock", "vampire", ] return enemyList2.includes(block.event.id) } const special12 = [1, 2, void 0, 3, 4, void 0, 5, 7, void 0, 8, 10, void 0, 11, 15, void 0, 22, 29, void 0, 28, 27] const special22 = [1, void 0, void 0, void 0, void 0, 2, void 0, void 0, void 0, void 0, 3, void 0, void 0, void 0, void 0, 4, void 0, void 0, void 0, void 0, 5, void 0, void 0, void 0, void 0, 7, void 0, void 0, void 0, void 0, 8, void 0, void 0, void 0, void 0, 10, void 0, void 0, void 0, void 0, 11, void 0, void 0, void 0, void 0, 15, void 0, void 0, void 0, void 0, 22, void 0, void 0, void 0, void 0, 29, void 0, void 0, void 0, void 0, 28, void 0, void 0, void 0, void 0, 27, void 0, void 0, void 0, void 0] const enemys2 = core.maps.searchBlockWithFilter(filter112, floorIds2) enemys2.forEach(v => { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special12.length) const rand2 = core.rand(special22.length) const a = special12[rand] const c = special22[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) core.setBlock(421, 10, 11, "SX2_3") core.setBlock(421, 10, 0, "SX2_2") break } } this.学院初始化 = function () { flags.价格 = (flags.价格 * 2 / 5) let b = {} b["5-1-MAL11"] = core.rand(3) + 1 b["6-1-MAL12"] = core.rand(3) + 1 b["10-6-MAL12"] = core.rand(3) + 1 b["5-1-MAL14"] = core.rand(3) + 1 b["1-1-MAL15"] = core.rand(3) + 1 b["1-5-MAL16"] = core.rand(3) + 1 b["1-7-MAL16"] = core.rand(3) + 1 b["9-1-MAL17"] = core.rand(3) + 1 b["11-1-MAL17"] = core.rand(3) + 1 b["2-11-MAL18"] = core.rand(3) + 1 b["10-11-MAL18"] = core.rand(3) + 1 b["11-1-MAL19"] = core.rand(3) + 1 b["1-4-MAL20"] = core.rand(3) + 1 b["2-4-MAL20"] = core.rand(3) + 1 b["4-5-MAL20"] = core.rand(3) + 1 b["4-6-MAL20"] = core.rand(3) + 1 b["4-7-MAL20"] = core.rand(3) + 1 b["8-7-MAL20"] = core.rand(3) + 1 b["8-6-MAL20"] = core.rand(3) + 1 b["8-5-MAL20"] = core.rand(3) + 1 b["8-4-MAL31"] = core.rand(6) + 1 b["4-8-MAL31"] = core.rand(6) + 1 b["1-1-MAL32"] = core.rand(6) + 1 b["2-2-MAL32"] = core.rand(6) + 1 b["11-1-MAL34"] = core.rand(6) + 1 b["11-11-MAL34"] = core.rand(6) + 1 b["3-7-MAL37"] = core.rand(6) + 1 b["4-7-MAL37"] = core.rand(6) + 1 b["7-7-MAL37"] = core.rand(6) + 1 b["8-7-MAL37"] = core.rand(6) + 1 b["9-7-MAL37"] = core.rand(6) + 1 b["7-8-MAL37"] = core.rand(6) + 1 b["8-8-MAL37"] = core.rand(6) + 1 b["9-8-MAL37"] = core.rand(6) + 1 b["5-8-MAL38"] = core.rand(6) + 1 b["11-6-MAL39"] = core.rand(6) + 1 b["6-9-MAL39"] = core.rand(6) + 1 b["8-2-MAL40"] = core.rand(6) + 1 b["9-2-MAL40"] = core.rand(6) + 1 b["10-2-MAL40"] = core.rand(6) + 1 b["7-4-MAL40"] = core.rand(6) + 1 b["8-4-MAL40"] = core.rand(6) + 1 b["9-4-MAL40"] = core.rand(6) + 1 b["1-9-MAL40"] = core.rand(6) + 1 b["1-11-MAL40"] = core.rand(6) + 1 b["4-11-MAL41"] = core.rand(10) + 1 b["8-11-MAL41"] = core.rand(10) + 1 b["3-11-MAL42"] = core.rand(10) + 1 b["1-3-MAL45"] = core.rand(10) + 1 b["2-3-MAL45"] = core.rand(10) + 1 b["1-5-MAL45"] = core.rand(10) + 1 b["2-5-MAL45"] = core.rand(10) + 1 b["1-3-MAL46"] = core.rand(10) + 1 b["11-2-MAL47"] = core.rand(10) + 1 b["6-10-MAL47"] = core.rand(10) + 1 b["6-8-MAL47"] = core.rand(10) + 1 b["5-8-MAL47"] = core.rand(10) + 1 b["3-8-MAL48"] = core.rand(10) + 1 b["3-11-MAL48"] = core.rand(10) + 1 b["2-4-MAL49"] = core.rand(10) + 1 b["3-4-MAL49"] = core.rand(10) + 1 b["4-4-MAL49"] = core.rand(10) + 1 b["8-4-MAL49"] = core.rand(10) + 1 b["9-4-MAL49"] = core.rand(10) + 1 b["10-4-MAL49"] = core.rand(10) + 1 function filter2(block) { return block.event.id.endsWith("Gem2") } function filter3(block) { return block.event.id.endsWith("Gem3") } function filter4(block) { return block.event.id.endsWith("Gem4") } for (let i = 11; i <= 20; i++) { const Gem2 = core.searchBlockWithFilter(filter2, "MAR" + i) Gem2.forEach(v => { b[`${v.x}-${v.y}-${v.floorId}`] = (core.rand(3) + 1) * 2 }) } b["4-5-MAR20"] = (core.rand(3) + 1) * 2 b["4-6-MAR20"] = (core.rand(3) + 1) * 2 b["4-7-MAR20"] = (core.rand(3) + 1) * 2 b["8-7-MAR20"] = (core.rand(3) + 1) * 2 b["8-6-MAR20"] = (core.rand(3) + 1) * 2 b["8-5-MAR20"] = (core.rand(3) + 1) * 2 b["8-2-MAR40"] = (core.rand(6) + 1) * 2 b["9-2-MAR40"] = (core.rand(6) + 1) * 2 b["10-2-MAR40"] = (core.rand(6) + 1) * 2 b["7-4-MAR40"] = (core.rand(6) + 1) * 2 b["8-4-MAR40"] = (core.rand(6) + 1) * 2 b["9-4-MAR40"] = (core.rand(6) + 1) * 2 b["2-4-MAR49"] = (core.rand(10) + 1) * 2 b["3-4-MAR49"] = (core.rand(10) + 1) * 2 b["4-4-MAR49"] = (core.rand(10) + 1) * 2 b["8-4-MAR49"] = (core.rand(10) + 1) * 2 b["9-4-MAR49"] = (core.rand(10) + 1) * 2 b["10-4-MAR49"] = (core.rand(10) + 1) * 2 b["4-8-MAR25"] = (core.rand(10) + 1) * 2 b["5-8-MAR25"] = (core.rand(10) + 1) * 2 b["7-8-MAR25"] = (core.rand(10) + 1) * 2 b["8-8-MAR25"] = (core.rand(10) + 1) * 2 for (let i = 31; i <= 40; i++) { const Gem3 = core.searchBlockWithFilter(filter3, "MAR" + i) Gem3.forEach(v => { b[`${v.x}-${v.y}-${v.floorId}`] = (core.rand(6) + 1) * 2 }) } for (let i = 41; i <= 49; i++) { const Gem4 = core.searchBlockWithFilter(filter4, "MAR" + i) Gem4.forEach(v => { b[`${v.x}-${v.y}-${v.floorId}`] = (core.rand(10) + 1) * 2 }) } core.setFlag("gemRandom", b) const special1 = [1, 2, void 0, 3, 4, void 0, 5, 7, void 0, 8, 10, void 0, 11, 15, void 0, 22, 29, void 0, 27] const special2 = [1, void 0, void 0, void 0, void 0, 2, void 0, void 0, void 0, void 0, 3, void 0, void 0, void 0, void 0, 4, void 0, void 0, void 0, void 0, 5, void 0, void 0, void 0, void 0, 7, void 0, void 0, void 0, void 0, 8, void 0, void 0, void 0, void 0, 10, void 0, void 0, void 0, void 0, 11, void 0, void 0, void 0, void 0, 15, void 0, void 0, void 0, void 0, 22, void 0, void 0, void 0, void 0, 29, void 0, void 0, void 0, void 0, 27, void 0, void 0, void 0, void 0] const special3 = [1, 2, void 0, 3, 4, void 0, 5, 7, void 0, 8, 10, void 0, 11, 15, void 0, 22, 29, void 0, 32, 27] const special4 = [1, void 0, void 0, void 0, void 0, 2, void 0, void 0, void 0, void 0, 3, void 0, void 0, void 0, void 0, 4, void 0, void 0, void 0, void 0, 5, void 0, void 0, void 0, void 0, 7, void 0, void 0, void 0, void 0, 8, void 0, void 0, void 0, void 0, 10, void 0, void 0, void 0, void 0, 11, void 0, void 0, void 0, void 0, 15, void 0, void 0, void 0, void 0, 22, void 0, void 0, void 0, void 0, 29, void 0, void 0, void 0, void 0, 32, void 0, void 0, void 0, void 0, 27, void 0, void 0, void 0, void 0] const special5 = [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 29, void 0, 27, void 0] const floorIds = [] const floorIds2 = [] for (let i = 11; i <= 49; i++) floorIds.push("MAL" + i) for (let i = 11; i <= 20; i++) floorIds2.push("MAR" + i) for (let i = 31; i <= 49; i++) floorIds2.push("MAR" + i) switch (flags.MapStatus?.Hard) { case "疯狂": const enemys = core.maps.searchBlockWithFilter(filter, floorIds) const enemys2 = core.maps.searchBlockWithFilter(filter, floorIds2) enemys.forEach(v => { if (v.block.event.id === "yellowKnight" || v.block.event.id === "swordEmperor" || v.block.event.id === "magicDragon" || v.block.event.id === "octopus" || v.block.event.id === "blackMagician") return if (v.block.event.id === "blackKing") { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length - 1) const rand2 = core.rand(special2.length - 5) const a = special1[rand] const c = special2[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) return } const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length) const rand2 = core.rand(special2.length) const a = special1[rand] const c = special2[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) enemys2.forEach(v => { if (v.block.event.id === "yellowKnight" || v.block.event.id === "swordEmperor" || v.block.event.id === "magicDragon" || v.block.event.id === "octopus" || v.block.event.id === "blackMagician") return if (v.block.event.id === "blackKing") { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special3.length - 1) const rand2 = core.rand(special4.length - 5) const a = special3[rand] const c = special4[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) return } const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special3.length) const rand2 = core.rand(special4.length) const a = special3[rand] const c = special4[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) core.setBlock(421, 5, 5, "MAL37") core.setBlock(421, 7, 5, "MAR37") core.setBlock(421, 6, 3, "MAL19") break case "噩梦": const floorIds3 = [] for (let i = 31; i <= 49; i++) { floorIds3.push("MAL" + i) floorIds3.push("MAR" + i) } const enemys3 = core.maps.searchBlockWithFilter(filter, floorIds3) enemys3.forEach(v => { if (v.block.event.id === "yellowKnight" || v.block.event.id === "swordEmperor" || v.block.event.id === "magicDragon") return if (v.block.event.id === "blackKing") { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special5.length - 2) const a = special5[rand] const b = [...special] if (a) b.push(a) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) return } const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special2.length) const a = special2[rand] const b = [...special] if (a) b.push(a) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) core.setBlock(421, 5, 5, "MAL37") core.setBlock(421, 7, 5, "MAR37") break } } this.御魂初始化 = function () { const floorIds = [...core.floorIds] switch (flags.MapStatus?.Hard) { case "噩梦": floorIds.pop() floorIds.splice(0, 31) const special1 = [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 29, void 0, 27, void 0] const enemys = core.maps.searchBlockWithFilter(filter, floorIds) enemys.forEach(v => { if (v.block.event.id === "yellowKnight" || v.block.event.id === "swordEmperor" || v.block.event.id === "magicDragon") return if (v.block.event.id === "blackKing") { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length - 2) const a = special1[rand] const b = [...special] if (a) b.push(a) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) return } const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length) const a = special1[rand] const b = [...special] if (a) b.push(a) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) break case "疯狂": floorIds.pop() floorIds.splice(0, 11) const special12 = [1, 2, void 0, 3, 4, void 0, 5, 7, void 0, 8, 10, void 0, 11, 15, void 0, 22, 29, void 0, 30, 27] const special22 = [1, void 0, void 0, void 0, void 0, 2, void 0, void 0, void 0, void 0, 3, void 0, void 0, void 0, void 0, 4, void 0, void 0, void 0, void 0, 5, void 0, void 0, void 0, void 0, 7, void 0, void 0, void 0, void 0, 8, void 0, void 0, void 0, void 0, 10, void 0, void 0, void 0, void 0, 11, void 0, void 0, void 0, void 0, 15, void 0, void 0, void 0, void 0, 22, void 0, void 0, void 0, void 0, 29, void 0, void 0, void 0, void 0, 30, void 0, void 0, void 0, void 0, 27, void 0, void 0, void 0, void 0] const enemys2 = core.maps.searchBlockWithFilter(filter, floorIds) enemys2.forEach(v => { if (v.block.event.id === "yellowKnight" || v.block.event.id === "swordEmperor" || v.block.event.id === "magicDragon" || v.block.event.id === "octopus" || v.block.event.id === "blackMagician") return if (v.block.event.id === "blackKing") { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special12.length - 1) const rand2 = core.rand(special22.length - 5) const a = special12[rand] const c = special22[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) return } const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special12.length) const rand2 = core.rand(special22.length) const a = special12[rand] const c = special22[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) break } core.getItem("yellowKey", 8) core.getItem("blueKey", 2) } this.龙宫初始化 = function () { flags.价格 = (flags.价格 * 4 / 5) flags.rinshop = 1 let b = {} const Gem2 = core.searchBlockWithFilter(block => block.event.id.endsWith("Gem2"), core.floorIds) const Gem3 = core.searchBlockWithFilter(block => block.event.id.endsWith("Gem3"), core.floorIds) const Gem4 = core.searchBlockWithFilter(block => block.event.id.endsWith("Gem4"), core.floorIds) Gem2.forEach(v => b[`${v.x}-${v.y}-${v.floorId}`] = core.rand(3) + 1) Gem3.forEach(v => b[`${v.x}-${v.y}-${v.floorId}`] = core.rand(6) + 1) Gem4.forEach(v => b[`${v.x}-${v.y}-${v.floorId}`] = core.rand(10) + 1) core.setFlag("gemRandom", b) core.getItem("yellowKey", 4) core.getItem("blueKey") flags.MapStatus.Choose = "none" switch (flags.MapStatus?.Hard) { case "疯狂": core.setBlock(421, 6, 5, "DPL4_2") core.setBlock(421, 11, 11, "DPR3_2") case "噩梦": core.setBlock(421, 4, 3, "DPL5_3") core.setBlock(421, 10, 1, "DPR5_2") case "困难": core.setBlock(421, 2, 4, "DPR4_3") core.setBlock(421, 10, 11, "DPL4_3") } core.insertAction({ "type": "setCurtain", "color": [93, 193, 234, 0.2], "time": 1, "keep": true }) } this.落地初始化 = function () { core.events._startGame_upload() main.dom.outerUI.style.display = "block" main.dom.outerBackground.style.display = "block" if (!main.replayChecking && !core.isReplaying() && hero.lv > flags.D290605F2869068624508C972B6C6AE0[hero.name].a) { core.insertAction([ { "type": "text", "text": "\t[米米]\f[Mimi_Full.png,0,15]作弊可非君子所为哦", "pos": [200, 300, 200] }, { "type": "function", "function": "function(){\ncore.closePanel()\ncore.restart()\n}" }, ]) console.error("请勿使用控制台选择等级") } else { for (let i = 0; i < (flags.lv3 ? 5 : 4); i++) { const card = core.plugin.card.cardList[core.randcard()] core.drawFolds(card) } if (hero.name === "小町") flags.komachi = 0 if (hero.name === "芬妮") flags.Fenny = 0 if (hero.name === "茉莉") flags.speedmax = 0 if (flags.lv5) core.insertCommonEvent("自选筹码") delete flags.lv3 delete flags.lv5 core.updateStatusBar() } } }, "问题反馈": function () { const towerName = core.firstData.name; //#region 常量配置 // UI尺寸和位置常量 const UI_SIZE_WIDTH = 264; const UI_SIZE_HEIGHT = 352; const UI_OFFSET_X = 76; const UI_OFFSET_Y = 32; const CENTER_X = UI_SIZE_WIDTH / 2; // 静态星光位置 const STATIC_STARS = [ { x: 60, y: 40, size: 1, brightness: 0.8 }, { x: 180, y: 25, size: 1.2, brightness: 0.9 }, { x: 50, y: 80, size: 0.8, brightness: 0.7 }, { x: 200, y: 70, size: 1.5, brightness: 1.0 }, { x: 120, y: 50, size: 1, brightness: 0.8 }, { x: 80, y: 120, size: 1.3, brightness: 0.9 }, { x: 220, y: 110, size: 0.9, brightness: 0.6 }, { x: 40, y: 160, size: 1.1, brightness: 0.8 }, { x: 160, y: 140, size: 1.4, brightness: 0.9 }, { x: 90, y: 180, size: 1, brightness: 0.7 }, { x: 210, y: 160, size: 1.2, brightness: 0.8 }, { x: 70, y: 200, size: 0.8, brightness: 0.6 }, { x: 140, y: 190, size: 1.3, brightness: 0.9 }, { x: 190, y: 200, size: 1, brightness: 0.7 }, { x: 110, y: 220, size: 1.1, brightness: 0.8 }, { x: 170, y: 240, size: 0.9, brightness: 0.6 }, { x: 30, y: 230, size: 1.2, brightness: 0.8 }, { x: 240, y: 230, size: 1, brightness: 0.7 }, { x: 100, y: 30, size: 0.8, brightness: 0.9 }, { x: 150, y: 90, size: 1.1, brightness: 0.8 } ]; // 粉色渐变配色方案 - 完整版 const colors = { background: '#e91e63', panelBg: '#3b82f6', panelBg2: '#fce7f3', panelBgHighlight: '#f9a8d4', primaryBorder: '#ec4899', highlightBorder: '#db2777', titleText: '#ffffff', normalText: '#2d3748', mutedText: '#718096', brightAccent: '#f472b6', secondaryAccent: '#e879f9', dangerAccent: '#dc2626', selectedHighlight: '#fce7f3', deepAccent: '#be185d', warmAccent: '#f472b6', starPurple: '#e879f9', buttonHover: '#fce7f3', buttonActive: '#f9a8d4', inputBg: '#ffffff', inputBorder: '#e5e7eb', inputFocus: '#ec4899', inputText: '#1f2937', inputPlaceholder: '#9ca3af', starlight: '#f472b6', cosmic: '#e879f9', solvedColor: '#10b981', replyColor: '#3b82f6', deletedColor: '#6b7280', containerBg: '#ffffff', containerBorder: '#ffffff', containerShadow: 'rgba(233, 30, 99, 0.3)', headerBg1: '#e91e63', headerBg2: '#f06292', headerText: '#ffffff', bugBg: '#fef2f2', bugBorder: '#ef4444', bugIcon: '#ef4444', suggestionBg: '#fef3c7', suggestionBorder: '#f59e0b', suggestionIcon: '#f59e0b', otherBg: '#f3e8ff', otherBorder: '#8b5cf6', otherIcon: '#8b5cf6', historyBg: '#fae8ff', historyBorder: '#d946ef', historyIcon: '#d946ef', adminBg: '#fce7f3', adminBorder: '#ec4899', adminIcon: '#ec4899', footerText: '#6b7280' }; const textColors = { mainTitle: '#ffffff', subTitle: '#f9fafb', important: '#1f2937', normal: '#374151', secondary: '#6b7280', highlight: '#ec4899', positive: '#10b981', negative: '#ef4444', faded: '#9ca3af', solved: '#10b981', reply: '#3b82f6', deleted: '#6b7280', inputText: '#1f2937', buttonText: '#2d3748', buttonDesc: '#6b7280', adminText: '#4a5568', adminHighlight: '#d53f8c' }; // 安全配置 const SECURITY_CONFIG = { passwordHash: "a2e75db25a586a8c0fc6d7eb6ba71b73e82f6929efda450c259934f6eeedbb9d", salt: "h5mota_feedback_protection_salt_2024", minPasswordLength: 12, maxPasswordLength: 50, maxAttempts: 3, lockoutTime: 300000, authValidTime: 900000 }; //#endregion //#region 状态管理 // UI状态 let feedbackUI = { currentPage: 'main', hoveredButton: -1, selectedCategory: 0, inputText: '', loginPassword: '', feedbackData: null, currentFeedbackIndex: 0, scrollOffset: 0, activeInputBox: null, inputBoxVisible: false, deleteButtonHover: false, closeButtonHover: false, selectedFeedback: null, playerFeedbacks: null, replyText: '', editingReply: false, currentReplyId: null, statsData: null, contentScrollOffset: 0, contentDragStart: null, isDragging: false, maxContentHeight: 0 }; // 安全验证状态 const createAuthState = (function () { let attempts = 0; let lockedUntil = 0; let authToken = null; let authExpireTime = 0; return { recordFailure: function () { attempts++; if (attempts >= SECURITY_CONFIG.maxAttempts) { lockedUntil = Date.now() + SECURITY_CONFIG.lockoutTime; } return attempts; }, isLocked: function () { return lockedUntil > Date.now(); }, getRemainingLockTime: function () { return Math.ceil((lockedUntil - Date.now()) / 1000); }, authorize: function () { attempts = 0; authToken = generateSecureToken(); authExpireTime = Date.now() + SECURITY_CONFIG.authValidTime; return authToken; }, isAuthorized: function (token) { return token && token === authToken && Date.now() < authExpireTime; }, deauthorize: function () { authToken = null; authExpireTime = 0; }, getExpireTime: function () { return authExpireTime; } }; })(); let currentAuthToken = null; function generateSecureToken() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let token = ''; for (let i = 0; i < 32; i++) { token += chars.charAt(Math.floor(Math.random() * chars.length)); } return token + '_' + Date.now(); } //#endregion //#region 本地存储管理 // 获取玩家删除列表 function getPlayerDeletedList() { return core.getLocalStorage('playerDeletedFeedbacks', []); } // 添加到玩家删除列表 function addToPlayerDeletedList(feedbackId) { const deletedList = getPlayerDeletedList(); if (!deletedList.includes(feedbackId)) { deletedList.push(feedbackId); core.setLocalStorage('playerDeletedFeedbacks', deletedList); } } // 获取作者删除列表 function getAuthorDeletedList() { if (!currentAuthToken) return []; return core.getLocalStorage('authorDeletedFeedbacks', []); } // 添加到作者删除列表 function addToAuthorDeletedList(feedbackId) { if (!currentAuthToken) return; const deletedList = getAuthorDeletedList(); if (!deletedList.includes(feedbackId)) { deletedList.push(feedbackId); core.setLocalStorage('authorDeletedFeedbacks', deletedList); } } // 检查反馈是否被删除 function isFeedbackDeleted(feedback, isAuthor = false) { if (!feedback.id) return false; if (isAuthor) { return getAuthorDeletedList().includes(feedback.id); } else { return getPlayerDeletedList().includes(feedback.id); } } // 过滤有效反馈 function filterValidFeedbacks(feedbackList, isAuthor = false) { return feedbackList.filter(feedback => !isFeedbackDeleted(feedback, isAuthor)); } // 获取反馈的回复 function getFeedbackReplies(feedbackId, allFeedbacks) { if (!feedbackId || !allFeedbacks) return []; return allFeedbacks.filter(item => { if (!item.tags) return false; const tags = item.tags.split(','); const isReply = tags.includes('REPLY'); const isReplyToThis = tags.some(tag => tag === `REPLY_TO_${feedbackId}`); return isReply && isReplyToThis; }); } //#endregion //#region 安全验证 async function sha256(text) { if (crypto && crypto.subtle) { const encoder = new TextEncoder(); const data = encoder.encode(text); const hashBuffer = await crypto.subtle.digest('SHA-256', data); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } else { let hash = 0; for (let i = 0; i < text.length; i++) { const char = text.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return Math.abs(hash).toString(16).padStart(16, '0'); } } async function verifyPassword(inputPassword) { if (createAuthState.isLocked()) { const remainingTime = createAuthState.getRemainingLockTime(); core.drawTip(`验证被锁定,请等待 ${remainingTime} 秒`); return false; } if (inputPassword.length < SECURITY_CONFIG.minPasswordLength || inputPassword.length > SECURITY_CONFIG.maxPasswordLength) { recordFailedAttempt(); return false; } try { const saltedPassword = inputPassword + SECURITY_CONFIG.salt; const inputHash = await sha256(saltedPassword); if (inputHash === SECURITY_CONFIG.passwordHash) { currentAuthToken = createAuthState.authorize(); core.drawTip('作者验证成功'); return true; } else { recordFailedAttempt(); return false; } } catch (error) { console.error('密码验证错误:', error); recordFailedAttempt(); return false; } } function recordFailedAttempt() { const attempts = createAuthState.recordFailure(); core.drawTip(`密码错误 (${attempts}/${SECURITY_CONFIG.maxAttempts})`); if (attempts >= SECURITY_CONFIG.maxAttempts) { core.drawTip('尝试次数过多,已锁定5分钟'); } } function verifyCurrentAuth() { if (!createAuthState.isAuthorized(currentAuthToken)) { core.drawTip('权限已过期或无效,请重新验证'); currentAuthToken = null; return false; } return true; } // UTF-8安全的Base64编码 function secureStoreData(key, data) { try { const dataString = JSON.stringify({ data: data, token: currentAuthToken, timestamp: Date.now() }); const encrypted = btoa(unescape(encodeURIComponent(dataString))); core.setFlag(key, encrypted); return true; } catch (error) { console.error('数据存储失败:', error); return false; } } // UTF-8安全的Base64解码 function secureReadData(key) { try { const encrypted = core.getFlag(key, ''); if (!encrypted) return null; const dataString = decodeURIComponent(escape(atob(encrypted))); const decrypted = JSON.parse(dataString); return decrypted.data; } catch (error) { console.error('数据解密失败:', error); return null; } } //#endregion //#region HTML输入框管理 function getCanvasInfo() { const canvas = document.getElementById('outerBackground') || document.querySelector('canvas'); if (!canvas) return null; const rect = canvas.getBoundingClientRect(); const scale = rect.width / 416; return { canvas, rect, scale }; } function createOverlayInput(x, y, width, height, isPassword = false) { hideInputBox(); const canvasInfo = getCanvasInfo(); if (!canvasInfo) return null; const { rect, scale } = canvasInfo; const input = document.createElement(isPassword ? 'input' : 'textarea'); if (isPassword) { input.type = 'password'; input.placeholder = '请输入密码...'; } else { input.placeholder = '请详细描述问题...'; input.style.resize = 'none'; input.maxLength = 500; } // 设置样式 input.style.position = 'absolute'; input.style.left = (rect.left + (UI_OFFSET_X + x) * scale) + 'px'; input.style.top = (rect.top + (UI_OFFSET_Y + y) * scale) + 'px'; input.style.width = (width * scale) + 'px'; input.style.height = (height * scale) + 'px'; input.style.backgroundColor = '#ffffff'; input.style.border = `2px solid #e5e7eb`; input.style.borderRadius = '4px'; input.style.color = textColors.inputText; input.style.fontSize = (11 * scale) + 'px'; input.style.fontFamily = 'Microsoft YaHei, sans-serif'; input.style.padding = (8 * scale) + 'px'; input.style.zIndex = '10000'; input.style.outline = 'none'; input.style.boxSizing = 'border-box'; // 设置初始值 if (isPassword) { input.value = feedbackUI.loginPassword; } else if (feedbackUI.editingReply) { input.value = feedbackUI.replyText; } else { input.value = feedbackUI.inputText; } // 事件监听 input.addEventListener('focus', function () { this.style.borderColor = '#ec4899'; this.style.backgroundColor = '#ffffff'; }); input.addEventListener('blur', function () { this.style.borderColor = '#e5e7eb'; this.style.backgroundColor = '#ffffff'; saveInputContent(); hideInputBox(); }); input.addEventListener('input', function () { if (isPassword) { feedbackUI.loginPassword = this.value; } else if (feedbackUI.editingReply) { feedbackUI.replyText = this.value; } else { feedbackUI.inputText = this.value; } }); input.addEventListener('keydown', function (e) { if (e.key === 'Escape') { saveInputContent(); hideInputBox(); core.feedback_draw(); } else if (e.key === 'Enter') { if (isPassword) { saveInputContent(); hideInputBox(); handleLogin(); } else if (e.ctrlKey || e.metaKey) { saveInputContent(); hideInputBox(); if (feedbackUI.editingReply) { submitReply(); } else if (feedbackUI.inputText.length > 0 && feedbackUI.inputText.length <= 500) { submitFeedback(feedbackUI.inputText, ['BUG', '建议', '其他'][feedbackUI.selectedCategory]); closeFeedbackSystem(); } } } }); document.body.appendChild(input); feedbackUI.activeInputBox = input; feedbackUI.inputBoxVisible = true; setTimeout(() => { input.focus(); input.select(); }, 50); return input; } function saveInputContent() { if (feedbackUI.activeInputBox) { const isPassword = feedbackUI.activeInputBox.type === 'password'; if (isPassword) { feedbackUI.loginPassword = feedbackUI.activeInputBox.value; } else if (feedbackUI.editingReply) { feedbackUI.replyText = feedbackUI.activeInputBox.value; } else { feedbackUI.inputText = feedbackUI.activeInputBox.value; } } } function hideInputBox() { if (feedbackUI.activeInputBox) { saveInputContent(); if (feedbackUI.activeInputBox.parentNode && document.body.contains(feedbackUI.activeInputBox)) { try { document.body.removeChild(feedbackUI.activeInputBox); } catch (error) { console.warn('输入框移除失败:', error); } } feedbackUI.activeInputBox = null; feedbackUI.inputBoxVisible = false; core.feedback_draw(); } } function showFeedbackInput() { const inputX = UI_OFFSET_X + 22; const inputY = UI_OFFSET_Y + 60; const inputWidth = 220; const inputHeight = 135; createOverlayInput(inputX, inputY, inputWidth, inputHeight, false); } function showPasswordInput() { const inputX = UI_OFFSET_X + 37; const inputY = UI_OFFSET_Y + 120; const inputWidth = 190; const inputHeight = 26; createOverlayInput(inputX, inputY, inputWidth, inputHeight, true); } function showReplyInput() { const inputX = UI_OFFSET_X + 22; const inputY = UI_OFFSET_Y + 150; const inputWidth = 220; const inputHeight = 60; feedbackUI.editingReply = true; createOverlayInput(inputX, inputY, inputWidth, inputHeight, false); } //#endregion //#region 基础绘制函数 function drawBackground() { core.clearUI(); core.clearMap('ui'); // 绘制渐变背景 core.fillRect('ui', UI_OFFSET_X, UI_OFFSET_Y, UI_SIZE_WIDTH, UI_SIZE_HEIGHT, colors.background); // 渐变层 core.setAlpha('ui', 0.8); core.fillRect('ui', UI_OFFSET_X, UI_OFFSET_Y, UI_SIZE_WIDTH / 2, UI_SIZE_HEIGHT, colors.headerBg2); core.setAlpha('ui', 0.6); core.fillRect('ui', UI_OFFSET_X + UI_SIZE_WIDTH / 2, UI_OFFSET_Y, UI_SIZE_WIDTH / 2, UI_SIZE_HEIGHT, '#ba68c8'); core.setAlpha('ui', 0.4); core.fillRect('ui', UI_OFFSET_X, UI_OFFSET_Y + UI_SIZE_HEIGHT / 2, UI_SIZE_WIDTH, UI_SIZE_HEIGHT / 2, '#ec4899'); core.setAlpha('ui', 1); // 容器背景(白色圆角矩形) const containerX = UI_OFFSET_X + 8; const containerY = UI_OFFSET_Y + 8; const containerWidth = UI_SIZE_WIDTH - 16; const containerHeight = UI_SIZE_HEIGHT - 16; // 容器阴影 core.setAlpha('ui', 0.15); core.fillRect('ui', containerX + 4, containerY + 8, containerWidth, containerHeight, '#000000'); core.setAlpha('ui', 1); // 容器背景 core.fillRect('ui', containerX, containerY, containerWidth, containerHeight, colors.containerBg); // 添加淡色花纹装饰 drawBackgroundPattern(containerX, containerY, containerWidth, containerHeight); // 容器边框 core.strokeRect('ui', containerX, containerY, containerWidth, containerHeight, colors.containerBorder, 4); } function drawBackgroundPattern(x, y, width, height) { // 淡粉色点状装饰 core.setAlpha('ui', 0.15); // 绘制圆点花纹 for (let i = 0; i < width; i += 30) { for (let j = 0; j < height; j += 25) { const dotX = x + i + (j % 2) * 15; // 错位排列 const dotY = y + j; if (dotX < x + width - 8 && dotY < y + height - 8) { core.fillRect('ui', dotX, dotY, 3, 3, '#ec4899'); } } } // 绘制斜线花纹 core.setAlpha('ui', 0.12); for (let i = -height; i < width; i += 40) { core.drawLine('ui', x + i, y, x + i + height, y + height, '#f472b6', 1); } // 绘制小圆环装饰 core.setAlpha('ui', 0.2); const decorativePositions = [ [x + 50, y + 80], [x + width - 60, y + 120], [x + 80, y + height - 100], [x + width - 90, y + height - 150], [x + 120, y + 200], [x + width - 130, y + 250] ]; decorativePositions.forEach(([cx, cy]) => { if (cx > x + 20 && cx < x + width - 20 && cy > y + 20 && cy < y + height - 20) { // 绘制圆环 core.strokeRect('ui', cx - 8, cy - 8, 16, 16, '#e879f9', 1); core.strokeRect('ui', cx - 5, cy - 5, 10, 10, '#f472b6', 1); core.fillRect('ui', cx - 1, cy - 1, 2, 2, '#ec4899'); } }); core.setAlpha('ui', 1); } function drawTitle(title) { const headerX = UI_OFFSET_X + 8; const headerY = UI_OFFSET_Y + 8; const headerWidth = UI_SIZE_WIDTH - 16; const headerHeight = 60; // 头部渐变背景 core.fillRect('ui', headerX, headerY, headerWidth, headerHeight, colors.headerBg1); core.setAlpha('ui', 0.8); core.fillRect('ui', headerX, headerY, headerWidth / 2, headerHeight, colors.headerBg2); core.setAlpha('ui', 1); // 头部文字 core.setTextAlign('ui', 'center'); core.setTextBaseline('ui', 'middle'); // 主标题 core.fillText('ui', title, UI_OFFSET_X + CENTER_X, headerY + 25, colors.headerText, 'bold 18px "Microsoft YaHei"'); // 副标题 core.setAlpha('ui', 0.9); core.fillText('ui', '星趴塔', UI_OFFSET_X + CENTER_X, headerY + 42, colors.headerText, '12px "Microsoft YaHei"'); core.setAlpha('ui', 1); // 关闭按钮 const closeX = headerX + headerWidth - 35; const closeY = headerY + 15; // 根据悬浮状态调整样式 if (feedbackUI.closeButtonHover) { // 悬浮时的样式 core.setAlpha('ui', 0.4); core.fillRect('ui', closeX, closeY, 24, 24, colors.headerText); core.setAlpha('ui', 0.6); core.strokeRect('ui', closeX, closeY, 24, 24, colors.headerText, 2); core.setAlpha('ui', 1); // 悬浮时的X图标更粗 core.drawLine('ui', closeX + 6, closeY + 6, closeX + 18, closeY + 18, colors.headerText, 3); core.drawLine('ui', closeX + 18, closeY + 6, closeX + 6, closeY + 18, colors.headerText, 3); } else { // 正常状态 core.setAlpha('ui', 0.25); core.fillRect('ui', closeX, closeY, 24, 24, colors.headerText); core.setAlpha('ui', 0.4); core.strokeRect('ui', closeX, closeY, 24, 24, colors.headerText, 2); core.setAlpha('ui', 1); // 正常的X图标 core.drawLine('ui', closeX + 6, closeY + 6, closeX + 18, closeY + 18, colors.headerText, 2); core.drawLine('ui', closeX + 18, closeY + 6, closeX + 6, closeY + 18, colors.headerText, 2); } } function drawMenuButton(x, y, width, height, config, isHovered = false) { // 按钮阴影 if (isHovered) { core.setAlpha('ui', 0.15); core.fillRect('ui', x + 2, y + 4, width, height, '#000000'); core.setAlpha('ui', 1); y -= 2; // 悬浮效果 } else { core.setAlpha('ui', 0.08); core.fillRect('ui', x + 1, y + 2, width, height, '#000000'); core.setAlpha('ui', 1); } // 按钮背景 core.fillRect('ui', x, y, width, height, config.bgColor); // 按钮边框 if (isHovered) { core.strokeRect('ui', x, y, width, height, config.borderColor, 3); } else { core.strokeRect('ui', x, y, width, height, 'transparent', 3); } // 图标背景 - 调整图标大小 const iconX = x + 8; const iconY = y + 7; const iconSize = 28; core.fillRect('ui', iconX, iconY, iconSize, iconSize, config.iconColor); // 绘制图标 drawIcon(iconX + iconSize / 2, iconY + iconSize / 2, config.iconType); // 按钮文字 - 调整文字位置 core.setTextAlign('ui', 'left'); core.setTextBaseline('ui', 'top'); // 标题 core.fillText('ui', config.title, x + 44, y + 8, textColors.buttonText, 'bold 13px "Microsoft YaHei"'); // 描述 core.fillText('ui', config.desc, x + 44, y + 24, textColors.buttonDesc, '10px "Microsoft YaHei"'); } function drawButton(x, y, width, height, text, isHovered = false, isEnabled = true) { // 按钮阴影 core.setAlpha('ui', 0.15); core.fillRect('ui', x + 1, y + 1, width, height, '#000000'); core.setAlpha('ui', 1); // 按钮背景 let bgColor = colors.panelBg; if (!isEnabled) { bgColor = colors.panelBg2; } else if (isHovered) { bgColor = colors.buttonHover; } core.fillRect('ui', x, y, width, height, bgColor); // 按钮边框 let borderColor = isEnabled ? colors.primaryBorder : colors.mutedText; if (isHovered && isEnabled) { borderColor = colors.highlightBorder; } core.strokeRect('ui', x, y, width, height, borderColor, isHovered ? 2 : 1); // 内部装饰 if (isEnabled) { core.setAlpha('ui', isHovered ? 0.4 : 0.2); core.strokeRect('ui', x + 1, y + 1, width - 2, height - 2, colors.brightAccent, 1); core.setAlpha('ui', 1); } // 按钮文字 let textColor = isEnabled ? (isHovered ? textColors.highlight : textColors.normal) : textColors.faded; core.setTextAlign('ui', 'center'); core.setTextBaseline('ui', 'middle'); core.fillText('ui', text, x + width / 2, y + height / 2, textColor, 'bold 11px "Microsoft YaHei"'); } function drawIcon(centerX, centerY, iconType) { const white = '#ffffff'; switch (iconType) { case 'bug': // 三角形 - 用线条绘制 // 绘制三角形的三条边 core.drawLine('ui', centerX, centerY - 7, centerX - 6, centerY + 5, white, 3); core.drawLine('ui', centerX - 6, centerY + 5, centerX + 6, centerY + 5, white, 3); core.drawLine('ui', centerX + 6, centerY + 5, centerX, centerY - 7, white, 3); // 填充三角形内部 core.fillRect('ui', centerX - 1, centerY - 3, 2, 6, white); core.fillRect('ui', centerX - 2, centerY + 1, 4, 2, white); core.fillRect('ui', centerX - 3, centerY + 3, 6, 1, white); break; case 'suggestion': // 圆形 - 用多个矩形模拟 // 绘制圆形的轮廓 core.fillRect('ui', centerX - 2, centerY - 6, 4, 2, white); // 上 core.fillRect('ui', centerX - 2, centerY + 4, 4, 2, white); // 下 core.fillRect('ui', centerX - 6, centerY - 2, 2, 4, white); // 左 core.fillRect('ui', centerX + 4, centerY - 2, 2, 4, white); // 右 core.fillRect('ui', centerX - 4, centerY - 4, 2, 2, white); // 左上 core.fillRect('ui', centerX + 2, centerY - 4, 2, 2, white); // 右上 core.fillRect('ui', centerX - 4, centerY + 2, 2, 2, white); // 左下 core.fillRect('ui', centerX + 2, centerY + 2, 2, 2, white); // 右下 // 填充中心 core.fillRect('ui', centerX - 2, centerY - 2, 4, 4, white); break; case 'other': // 正方形 core.fillRect('ui', centerX - 6, centerY - 6, 12, 12, white); break; case 'history': // 菱形 // 绘制菱形的四条边 core.drawLine('ui', centerX, centerY - 7, centerX + 7, centerY, white, 3); core.drawLine('ui', centerX + 7, centerY, centerX, centerY + 7, white, 3); core.drawLine('ui', centerX, centerY + 7, centerX - 7, centerY, white, 3); core.drawLine('ui', centerX - 7, centerY, centerX, centerY - 7, white, 3); // 填充菱形内部 core.fillRect('ui', centerX - 1, centerY - 4, 2, 8, white); core.fillRect('ui', centerX - 2, centerY - 3, 4, 6, white); core.fillRect('ui', centerX - 3, centerY - 2, 6, 4, white); core.fillRect('ui', centerX - 4, centerY - 1, 8, 2, white); break; case 'admin': // 六边形 // 绘制八角形轮廓 core.fillRect('ui', centerX - 3, centerY - 6, 6, 2, white); // 上 core.fillRect('ui', centerX - 3, centerY + 4, 6, 2, white); // 下 core.fillRect('ui', centerX - 6, centerY - 3, 2, 6, white); // 左 core.fillRect('ui', centerX + 4, centerY - 3, 2, 6, white); // 右 core.fillRect('ui', centerX - 5, centerY - 5, 2, 2, white); // 左上 core.fillRect('ui', centerX + 3, centerY - 5, 2, 2, white); // 右上 core.fillRect('ui', centerX - 5, centerY + 3, 2, 2, white); // 左下 core.fillRect('ui', centerX + 3, centerY + 3, 2, 2, white); // 右下 // 填充中心 core.fillRect('ui', centerX - 3, centerY - 3, 6, 6, white); break; } } // 绘制可滚动的文本内容 function drawScrollableTextContent(x, y, width, height, text, scrollOffset = 0) { if (!text) return 0; const lineHeight = 14; const maxLines = Math.floor(height / lineHeight); // 多行文本显示 const lines = text.split('\n'); const displayLines = []; // 处理自动换行 for (const line of lines) { if (line.length === 0) { displayLines.push(''); continue; } let currentLine = ''; for (const char of line) { const testLine = currentLine + char; if (testLine.length * 11 > width - 16) { if (currentLine) displayLines.push(currentLine); currentLine = char; } else { currentLine = testLine; } } if (currentLine) displayLines.push(currentLine); } // 计算总行数和可滚动的行数 const totalLines = displayLines.length; const scrollableLines = Math.max(0, totalLines - maxLines); // 限制滚动偏移范围 scrollOffset = Math.max(0, Math.min(scrollableLines, scrollOffset)); // 绘制可见的文本行 core.setTextAlign('ui', 'left'); for (let i = 0; i < Math.min(displayLines.length - scrollOffset, maxLines); i++) { const lineIndex = i + scrollOffset; const lineY = y + i * lineHeight + 16; if (lineIndex < displayLines.length) { core.fillText('ui', displayLines[lineIndex], x, lineY, textColors.important, '12px "Microsoft YaHei"'); } } // 如果有滚动条需要,绘制滚动指示器 if (scrollableLines > 0) { const scrollBarX = x + width - 8; const scrollBarY = y + 8; const scrollBarHeight = height - 16; // 滚动轨道 core.fillRect('ui', scrollBarX, scrollBarY, 6, scrollBarHeight, colors.panelBg); // 滚动手柄 const handleHeight = Math.max(20, scrollBarHeight * maxLines / totalLines); const handleY = scrollBarY + (scrollBarHeight - handleHeight) * scrollOffset / scrollableLines; core.fillRect('ui', scrollBarX + 1, handleY, 4, handleHeight, colors.primaryBorder); } return scrollableLines; // 返回最大滚动偏移 } function drawTextContent(x, y, width, height, text, isPassword = false, status = null) { if (!text) return; const lineHeight = 14; const maxLines = Math.floor(height / lineHeight); if (isPassword) { const maskedText = '*'.repeat(text.length); core.setTextAlign('ui', 'left'); const textColor = verifyCurrentAuth() ? textColors.adminText : textColors.important; core.fillText('ui', maskedText, x, y + 16, textColor, '12px "Microsoft YaHei"'); return; } // 多行文本显示 const lines = text.split('\n'); const displayLines = []; // 处理自动换行 for (const line of lines) { if (line.length === 0) { displayLines.push(''); continue; } let currentLine = ''; for (const char of line) { const testLine = currentLine + char; if (testLine.length * 8 > width - 16) { if (currentLine) displayLines.push(currentLine); currentLine = char; } else { currentLine = testLine; } } if (currentLine) displayLines.push(currentLine); } // 绘制文本行 core.setTextAlign('ui', 'left'); for (let i = 0; i < Math.min(displayLines.length, maxLines); i++) { const lineY = y + i * lineHeight + 16; // 根据状态选择颜色 let textColor; if (verifyCurrentAuth()) { textColor = textColors.adminText; // 作者模式使用专用颜色 } else if (status === 'solved') { textColor = textColors.solved; } else if (status === 'reply') { textColor = textColors.reply; } else if (status === 'deleted') { textColor = textColors.deleted; } else { textColor = textColors.important; } core.fillText('ui', displayLines[i], x, lineY, textColor, '12px "Microsoft YaHei"'); } // 如果文本超出显示区域,显示省略号 if (displayLines.length > maxLines) { core.fillText('ui', '...', x, y + maxLines * lineHeight + 16, verifyCurrentAuth() ? textColors.adminText : textColors.secondary, '12px "Microsoft YaHei"'); } } //#endregion //#region 页面绘制 function drawMainPage() { drawBackground(); drawTitle('问题反馈'); // 菜单项配置(保持不变) const menuItems = [{ title: '报告BUG', desc: '遇到游戏错误或异常', bgColor: colors.bugBg, borderColor: colors.bugBorder, iconColor: colors.bugIcon, iconType: 'bug' }, { title: '提出建议', desc: '分享改进想法', bgColor: colors.suggestionBg, borderColor: colors.suggestionBorder, iconColor: colors.suggestionIcon, iconType: 'suggestion' }, { title: '其他问题', desc: '其他反馈内容', bgColor: colors.otherBg, borderColor: colors.otherBorder, iconColor: colors.otherIcon, iconType: 'other' }, { title: '我的反馈', desc: '查看反馈记录', bgColor: colors.historyBg, borderColor: colors.historyBorder, iconColor: colors.historyIcon, iconType: 'history' }, { title: '作者管理', desc: '管理员功能', bgColor: colors.adminBg, borderColor: colors.adminBorder, iconColor: colors.adminIcon, iconType: 'admin' } ]; // 重新调整按钮布局 const buttonWidth = 200; // 调整为200 const buttonHeight = 45; // 调整为45 const startY = 85; // 从头部下方开始 const spacing = 52; // 按钮间距 menuItems.forEach((item, index) => { const x = UI_OFFSET_X + CENTER_X - buttonWidth / 2; const y = UI_OFFSET_Y + startY + index * spacing; const isHovered = feedbackUI.hoveredButton === index; drawMenuButton(x, y, buttonWidth, buttonHeight, item, isHovered); }); // 底部提示 core.setTextAlign('ui', 'center'); core.fillText('ui', "按 J 键快速打开 | ESC 键关闭", UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 395, colors.footerText, '10px "Microsoft YaHei"'); } function drawInputPage() { drawBackground(); const categories = ['BUG', '建议', '其他']; const categoryNames = ['报告BUG', '提出建议', '其他问题']; drawTitle(categoryNames[feedbackUI.selectedCategory]); // 输入区域 - 调整尺寸 const inputX = UI_OFFSET_X + 25; const inputY = UI_OFFSET_Y + 75; const inputWidth = 230; // 调整宽度 const inputHeight = 120; // 调整高度 // 如果没有显示输入框,则绘制背景和内容 if (!feedbackUI.inputBoxVisible) { // 输入框阴影 core.setAlpha('ui', 0.1); core.fillRect('ui', inputX + 2, inputY + 2, inputWidth, inputHeight, '#000000'); core.setAlpha('ui', 1); // 输入框背景 - 使用新配色 core.fillRect('ui', inputX, inputY, inputWidth, inputHeight, colors.inputBg); core.strokeRect('ui', inputX, inputY, inputWidth, inputHeight, colors.inputBorder, 2); // 内部装饰 core.setAlpha('ui', 0.3); core.strokeRect('ui', inputX + 1, inputY + 1, inputWidth - 2, inputHeight - 2, colors.highlightBorder, 1); core.setAlpha('ui', 1); if (feedbackUI.inputText) { drawTextContent(inputX + 8, inputY + 8, inputWidth - 16, inputHeight - 16, feedbackUI.inputText, false); } else { core.setTextAlign('ui', 'left'); core.fillText('ui', '点击此处开始输入...', inputX + 8, inputY + 20, colors.inputPlaceholder, '12px "Microsoft YaHei"'); } } // 字数统计 const charCount = feedbackUI.inputText.length; const maxChars = 500; const countColor = charCount > maxChars ? textColors.negative : textColors.secondary; core.setTextAlign('ui', 'right'); core.fillText('ui', `${charCount}/${maxChars}`, inputX + inputWidth - 8, inputY + inputHeight + 15, countColor, '10px "Microsoft YaHei"'); // 操作按钮 - 使用新的按钮样式 const buttonY = inputY + inputHeight + 25; const submitEnabled = charCount > 0 && charCount <= maxChars; drawButton(inputX, buttonY, 70, 24, '提交反馈', feedbackUI.hoveredButton === 0, submitEnabled); drawButton(inputX + 78, buttonY, 55, 24, '清空', feedbackUI.hoveredButton === 1, charCount > 0); drawButton(inputX + 140, buttonY, 55, 24, '返回', feedbackUI.hoveredButton === 2, true); // 快捷键提示 core.setTextAlign('ui', 'center'); if (feedbackUI.inputBoxVisible) { core.fillText('ui', "Ctrl+Enter提交 | Esc保存退出", UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 265, textColors.highlight, '9px "Microsoft YaHei"'); } else { core.fillText('ui', "点击输入框开始编辑", UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 265, textColors.faded, '9px "Microsoft YaHei"'); } } function drawLoginPage() { drawBackground(); drawTitle('作者验证'); // 安全提示 core.setTextAlign('ui', 'center'); core.fillText('ui', '🔒 请输入作者密码', UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 75, textColors.important, 'bold 13px "Microsoft YaHei"'); core.fillText('ui', '权限15分钟后自动过期', UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 90, textColors.secondary, '10px "Microsoft YaHei"'); // 锁定状态检查 if (createAuthState.isLocked()) { const remainingTime = createAuthState.getRemainingLockTime(); core.fillText('ui', `⚠️ 验证被锁定,请等待 ${remainingTime} 秒`, UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 105, textColors.negative, 'bold 11px "Microsoft YaHei"'); } // 密码输入框 const inputX = UI_OFFSET_X + 37; const inputY = UI_OFFSET_Y + 120; const inputWidth = 190; const inputHeight = 26; if (!feedbackUI.inputBoxVisible) { core.fillRect('ui', inputX, inputY, inputWidth, inputHeight, colors.inputBg); core.strokeRect('ui', inputX, inputY, inputWidth, inputHeight, colors.inputBorder, 2); if (feedbackUI.loginPassword) { drawTextContent(inputX + 8, inputY, inputWidth - 16, inputHeight, feedbackUI.loginPassword, true); } else { core.setTextAlign('ui', 'left'); core.fillText('ui', '点击此处输入密码...', inputX + 8, inputY + 16, textColors.faded, '12px "Microsoft YaHei"'); } } // 操作按钮 const buttonY = UI_OFFSET_Y + 165; const loginEnabled = feedbackUI.loginPassword.length >= 12 && !createAuthState.isLocked(); drawButton(UI_OFFSET_X + 35, buttonY, 60, 26, '验证', feedbackUI.hoveredButton === 0, loginEnabled); drawButton(UI_OFFSET_X + 102, buttonY, 60, 26, '清空', feedbackUI.hoveredButton === 1, feedbackUI.loginPassword.length > 0); drawButton(UI_OFFSET_X + 169, buttonY, 60, 26, '返回', feedbackUI.hoveredButton === 2, true); // 安全提示 core.setTextAlign('ui', 'center'); core.fillText('ui', '🛡️ SHA-256加密验证', UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 210, textColors.faded, '9px "Microsoft YaHei"'); core.fillText('ui', '密码长度需要12-50个字符', UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 222, textColors.faded, '9px "Microsoft YaHei"'); } function drawAdminPage() { drawBackground(); drawTitle('作者管理面板'); hideInputBox(); // 权限状态 const expireTime = new Date(createAuthState.getExpireTime()).toLocaleTimeString(); core.setTextAlign('ui', 'center'); core.fillText('ui', `🔑 权限有效期至: ${expireTime}`, UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 67, textColors.positive, '10px "Microsoft YaHei"'); // 管理选项 - 使用更大的按钮 const options = [ { text: '查看所有反馈' }, { text: '反馈统计' }, { text: '刷新数据' }, { text: '退出作者模式' } ]; const buttonWidth = 220; // 增大按钮宽度 const buttonHeight = 35; // 增大按钮高度 const startY = 90; // 调整起始位置 const spacing = 45; // 调整间距 options.forEach((option, index) => { const x = UI_OFFSET_X + CENTER_X - buttonWidth / 2; const y = UI_OFFSET_Y + startY + index * spacing; const isHovered = feedbackUI.hoveredButton === index; drawButton(x, y, buttonWidth, buttonHeight, option.text, isHovered, true); }); } function drawFeedbackListPage() { drawBackground(); drawTitle('反馈列表'); hideInputBox(); const feedbackList = filterValidFeedbacks(feedbackUI.feedbackData || [], true); if (feedbackList.length === 0) { core.setTextAlign('ui', 'center'); core.fillText('ui', '暂无反馈', UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 112, textColors.secondary, 'bold 13px "Microsoft YaHei"'); drawButton(UI_OFFSET_X + 102, UI_OFFSET_Y + 210, 60, 26, '返回', feedbackUI.hoveredButton === 0, true); return; } // 列表区域 const listX = UI_OFFSET_X + 19; const listY = UI_OFFSET_Y + 60; const listWidth = 226; const itemHeight = 26; const maxVisible = 5; // 背景 core.fillRect('ui', listX, listY, listWidth, itemHeight * maxVisible + 8, colors.panelBg); core.strokeRect('ui', listX, listY, listWidth, itemHeight * maxVisible + 8, colors.primaryBorder, 1); // 绘制反馈项 const startIndex = feedbackUI.scrollOffset; const endIndex = Math.min(startIndex + maxVisible, feedbackList.length); for (let i = startIndex; i < endIndex; i++) { const item = feedbackList[i]; const itemY = listY + 4 + (i - startIndex) * itemHeight; const isHovered = feedbackUI.hoveredButton === i; // 项目背景 if (isHovered) { core.fillRect('ui', listX + 2, itemY, listWidth - 4, itemHeight, colors.buttonHover); } // 解析反馈信息 const tags = item.tags ? item.tags.split(',') : ['未知']; const category = tags[0] || '未知'; const timestamp = tags[1] ? new Date(parseInt(tags[1])).toLocaleDateString() : '未知'; const preview = item.comment.split('\n')[0].substring(0, 16); const truncated = item.comment.length > 16 ? '...' : ''; // 状态标记 const allFeedbacks = feedbackUI.feedbackData || []; const replies = getFeedbackReplies(item.id, allFeedbacks); const hasReply = replies.length > 0; // 分类标签 const categoryColors = { 'BUG': textColors.negative, '建议': textColors.positive, '其他': textColors.secondary }; const categoryColor = categoryColors[category] || textColors.secondary; core.setTextAlign('ui', 'left'); // 状态图标 let statusIcon = ''; if (hasReply) statusIcon = '💬'; core.fillText('ui', statusIcon, listX + 4, itemY + 9, textColors.positive, 'bold 10px "Microsoft YaHei"'); core.fillText('ui', `[${category}]`, listX + 16, itemY + 9, categoryColor, 'bold 9px "Microsoft YaHei"'); core.fillText('ui', `${preview}${truncated}`, listX + 60, itemY + 9, textColors.normal, '9px "Microsoft YaHei"'); core.fillText('ui', timestamp, listX + 6, itemY + 19, textColors.faded, '8px "Microsoft YaHei"'); // 悬浮边框 if (isHovered) { core.strokeRect('ui', listX + 2, itemY, listWidth - 4, itemHeight, colors.highlightBorder, 1); } } // 滚动指示器 if (feedbackList.length > maxVisible) { const scrollBarX = listX + listWidth + 4; const scrollBarY = listY; const scrollBarHeight = itemHeight * maxVisible + 8; const thumbHeight = Math.max(15, scrollBarHeight * maxVisible / feedbackList.length); const thumbY = scrollBarY + (scrollBarHeight - thumbHeight) * startIndex / (feedbackList.length - maxVisible); core.fillRect('ui', scrollBarX, scrollBarY, 6, scrollBarHeight, colors.panelBg); core.fillRect('ui', scrollBarX + 1, thumbY, 4, thumbHeight, colors.primaryBorder); } // 底部信息和按钮 core.setTextAlign('ui', 'center'); core.fillText('ui', `共 ${feedbackList.length} 条反馈`, UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 217, textColors.normal, '10px "Microsoft YaHei"'); drawButton(UI_OFFSET_X + 57, UI_OFFSET_Y + 229, 60, 23, '刷新', feedbackUI.hoveredButton === 1000, true); drawButton(UI_OFFSET_X + 124, UI_OFFSET_Y + 229, 60, 23, '统计', feedbackUI.hoveredButton === 1001, true); drawButton(UI_OFFSET_X + 191, UI_OFFSET_Y + 229, 60, 23, '返回', feedbackUI.hoveredButton === 1002, true); } // 反馈详情页面 function drawFeedbackDetailPage() { drawBackground(); if (!feedbackUI.selectedFeedback) { core.drawTip('未选择有效反馈'); feedbackUI.currentPage = feedbackUI.feedbackData ? 'list' : 'player'; core.feedback_draw(); return; } const feedback = feedbackUI.selectedFeedback; const tags = feedback.tags ? feedback.tags.split(',') : ['未知']; const category = tags[0] || '未知'; drawTitle(`反馈详情 - ${category}`); // 反馈信息头部 const infoY = UI_OFFSET_Y + 65; const timestamp = tags[1] ? new Date(parseInt(tags[1])).toLocaleString() : '未知时间'; core.setTextAlign('ui', 'left'); core.fillText('ui', `时间: ${timestamp}`, UI_OFFSET_X + 20, infoY, textColors.secondary, '10px "Microsoft YaHei"'); // 内容区域 const contentX = UI_OFFSET_X + 20; const contentY = UI_OFFSET_Y + 85; const contentWidth = UI_SIZE_WIDTH - 40; const contentHeight = 130; // 背景和边框 core.fillRect('ui', contentX, contentY, contentWidth, contentHeight, colors.panelBg); core.strokeRect('ui', contentX, contentY, contentWidth, contentHeight, colors.primaryBorder, 1); // 显示反馈内容 if (feedback.comment) { // 获取回复 let displayText = feedback.comment; const allStoredData = secureReadData('feedbackData'); if (allStoredData) { const replies = getFeedbackReplies(feedback.id, allStoredData); if (replies.length > 0) { displayText += '\n\n--- 作者回复 ---\n'; replies.forEach(reply => { displayText += reply.comment + '\n'; }); } } // 绘制可滚动内容 feedbackUI.maxContentHeight = drawScrollableTextContent( contentX + 8, contentY + 8, contentWidth - 16, contentHeight - 16, displayText, feedbackUI.contentScrollOffset ); } else { core.setTextAlign('ui', 'center'); core.fillText('ui', '无内容', contentX + contentWidth / 2, contentY + contentHeight / 2, textColors.secondary, '12px "Microsoft YaHei"'); } // 操作按钮 const buttonY = UI_OFFSET_Y + 235; if (verifyCurrentAuth()) { drawButton(UI_OFFSET_X + 50, buttonY, 45, 20, '回复', feedbackUI.hoveredButton === 0, true); drawButton(UI_OFFSET_X + 105, buttonY, 40, 20, '删除', feedbackUI.hoveredButton === 1, true); drawButton(UI_OFFSET_X + 160, buttonY, 40, 20, '返回', feedbackUI.hoveredButton === 2, true); } else { drawButton(UI_OFFSET_X + 92, buttonY, 40, 20, '删除', feedbackUI.hoveredButton === 0, true); drawButton(UI_OFFSET_X + 137, buttonY, 40, 20, '返回', feedbackUI.hoveredButton === 1, true); } } // 玩家反馈历史页面 function drawPlayerFeedbackPage() { drawBackground(); drawTitle('我的反馈'); hideInputBox(); const playerFeedbacks = filterValidFeedbacks(feedbackUI.playerFeedbacks || [], false); if (playerFeedbacks.length === 0) { core.setTextAlign('ui', 'center'); core.fillText('ui', '📭 您还没有提交过反馈', UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 150, textColors.secondary, 'bold 13px "Microsoft YaHei"'); const returnButtonX = UI_OFFSET_X + CENTER_X - 30; const returnButtonY = UI_OFFSET_Y + 200; drawButton(returnButtonX, returnButtonY, 60, 26, '返回', feedbackUI.hoveredButton === 0, true); return; } // 使用与管理员列表相同的布局 const listX = UI_OFFSET_X + 19; const listY = UI_OFFSET_Y + 60; const listWidth = 226; const itemHeight = 26; const maxVisible = 5; core.fillRect('ui', listX, listY, listWidth, itemHeight * maxVisible + 8, colors.panelBg); core.strokeRect('ui', listX, listY, listWidth, itemHeight * maxVisible + 8, colors.primaryBorder, 1); const startIndex = feedbackUI.scrollOffset; const endIndex = Math.min(startIndex + maxVisible, playerFeedbacks.length); for (let i = startIndex; i < endIndex; i++) { const item = playerFeedbacks[i]; const itemY = listY + 4 + (i - startIndex) * itemHeight; const isHovered = feedbackUI.hoveredButton === i; if (isHovered) { core.fillRect('ui', listX + 2, itemY, listWidth - 4, itemHeight, colors.buttonHover); } const tags = item.tags ? item.tags.split(',') : ['未知']; const category = tags[0] || '未知'; const timestamp = tags[1] ? new Date(parseInt(tags[1])).toLocaleDateString() : '未知'; const preview = item.comment.split('\n')[0].substring(0, 20); const truncated = item.comment.length > 20 ? '...' : ''; const isSolved = item.solved || false; // 检查是否有作者回复 const allStoredData = secureReadData('feedbackData') || []; const replies = getFeedbackReplies(item.id, allStoredData); const hasReply = replies.length > 0; const categoryColors = { 'BUG': textColors.negative, '建议': textColors.positive, '其他': textColors.secondary }; const categoryColor = categoryColors[category] || textColors.secondary; core.setTextAlign('ui', 'left'); // 状态图标 let statusIcon = ''; if (isSolved) statusIcon = '✓'; else if (hasReply) statusIcon = '💬'; core.fillText('ui', statusIcon, listX + 4, itemY + 9, textColors.positive, 'bold 10px "Microsoft YaHei"'); core.fillText('ui', `[${category}]`, listX + 16, itemY + 9, categoryColor, 'bold 9px "Microsoft YaHei"'); core.fillText('ui', `${preview}${truncated}`, listX + 60, itemY + 9, textColors.normal, '9px "Microsoft YaHei"'); core.fillText('ui', timestamp, listX + 6, itemY + 19, textColors.faded, '8px "Microsoft YaHei"'); if (isHovered) { core.strokeRect('ui', listX + 2, itemY, listWidth - 4, itemHeight, colors.highlightBorder, 1); } } // 滚动指示器 if (playerFeedbacks.length > maxVisible) { const scrollBarX = listX + listWidth + 4; const scrollBarY = listY; const scrollBarHeight = itemHeight * maxVisible + 8; const thumbHeight = Math.max(15, scrollBarHeight * maxVisible / playerFeedbacks.length); const thumbY = scrollBarY + (scrollBarHeight - thumbHeight) * startIndex / (playerFeedbacks.length - maxVisible); core.fillRect('ui', scrollBarX, scrollBarY, 6, scrollBarHeight, colors.panelBg); core.fillRect('ui', scrollBarX + 1, thumbY, 4, thumbHeight, colors.primaryBorder); } // 底部信息和按钮 core.setTextAlign('ui', 'center'); core.fillText('ui', `共 ${playerFeedbacks.length} 条反馈`, UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 217, textColors.normal, '10px "Microsoft YaHei"'); drawButton(UI_OFFSET_X + 102, UI_OFFSET_Y + 229, 60, 23, '刷新', feedbackUI.hoveredButton === 1000, true); drawButton(UI_OFFSET_X + 171, UI_OFFSET_Y + 229, 60, 23, '返回', feedbackUI.hoveredButton === 1001, true); } // 统计页面(内置显示) function drawStatsPage() { drawBackground(); drawTitle('反馈统计'); hideInputBox(); const feedbackList = filterValidFeedbacks(feedbackUI.feedbackData || [], true); if (feedbackList.length === 0) { core.setTextAlign('ui', 'center'); core.fillText('ui', '暂无数据可统计', UI_OFFSET_X + CENTER_X, UI_OFFSET_Y + 112, textColors.secondary, 'bold 13px "Microsoft YaHei"'); drawButton(UI_OFFSET_X + 102, UI_OFFSET_Y + 210, 60, 26, '返回', feedbackUI.hoveredButton === 0, true); return; } // 计算统计数据 const stats = { 'BUG': 0, '建议': 0, '其他': 0 }; let repliedCount = 0; feedbackList.forEach(item => { const category = item.tags ? item.tags.split(',')[0] : '其他'; if (stats.hasOwnProperty(category)) { stats[category]++; } else { stats['其他']++; } // 检查是否有回复 const allStoredData = secureReadData('feedbackData'); if (allStoredData) { const replies = getFeedbackReplies(item.id, allStoredData); if (replies.length > 0) repliedCount++; } }); const latest = feedbackList.reduce((max, item) => { const time = item.tags ? parseInt(item.tags.split(',')[1]) : 0; return time > max ? time : max; }, 0); const latestTime = latest ? new Date(latest).toLocaleString() : '无'; // 显示统计信息 const startY = UI_OFFSET_Y + 70; const lineHeight = 16; core.setTextAlign('ui', 'center'); core.fillText('ui', '📊 反馈统计报告', UI_OFFSET_X + CENTER_X, startY, textColors.mainTitle, 'bold 13px "Microsoft YaHei"'); core.setTextAlign('ui', 'left'); const leftX = UI_OFFSET_X + 50; core.fillText('ui', `总数:${feedbackList.length} 条`, leftX, startY + lineHeight * 2, textColors.important, 'bold 11px "Microsoft YaHei"'); core.fillText('ui', `🐛 BUG报告:${stats.BUG} 条`, leftX, startY + lineHeight * 3.5, textColors.negative, '10px "Microsoft YaHei"'); core.fillText('ui', `💡 游戏建议:${stats.建议} 条`, leftX, startY + lineHeight * 4.5, textColors.positive, '10px "Microsoft YaHei"'); core.fillText('ui', `❓ 其他问题:${stats.其他} 条`, leftX, startY + lineHeight * 5.5, textColors.secondary, '10px "Microsoft YaHei"'); core.fillText('ui', `💬 已回复:${repliedCount} 条`, leftX, startY + lineHeight * 7, textColors.reply, '10px "Microsoft YaHei"'); core.fillText('ui', `🕒 最新反馈:`, leftX, startY + lineHeight * 8.5, textColors.secondary, '10px "Microsoft YaHei"'); core.fillText('ui', latestTime, leftX + 20, startY + lineHeight * 9.5, textColors.normal, '9px "Microsoft YaHei"'); // 返回按钮 drawButton(UI_OFFSET_X + 102, UI_OFFSET_Y + 230, 60, 23, '返回', feedbackUI.hoveredButton === 0, true); } //#endregion //#region 主绘制函数 core.feedback_draw = function () { if (core.status.event.id !== 'feedback') return; switch (feedbackUI.currentPage) { case 'main': drawMainPage(); break; case 'input': drawInputPage(); break; case 'login': drawLoginPage(); break; case 'admin': drawAdminPage(); break; case 'list': drawFeedbackListPage(); break; case 'detail': drawFeedbackDetailPage(); break; case 'player': drawPlayerFeedbackPage(); break; case 'stats': drawStatsPage(); break; default: drawMainPage(); } }; //#endregion //#region 事件处理 core.registerAction('onmove', 'feedback', function (x, y, px, py) { if (core.status.event.id !== 'feedback') return false; const adjustedPx = px - UI_OFFSET_X; const adjustedPy = py - UI_OFFSET_Y; let oldHovered = feedbackUI.hoveredButton; let oldCloseHover = feedbackUI.closeButtonHover || false; feedbackUI.hoveredButton = -1; feedbackUI.closeButtonHover = false; if (adjustedPx < 0 || adjustedPx > UI_SIZE_WIDTH || adjustedPy < 0 || adjustedPy > UI_SIZE_HEIGHT) { if (oldHovered !== feedbackUI.hoveredButton || oldCloseHover !== feedbackUI.closeButtonHover) { core.feedback_draw(); } return false; } const headerWidth = UI_SIZE_WIDTH - 16; const closeX = headerWidth - 35; const closeY = 24; if (adjustedPx >= closeX && adjustedPx <= closeX + 24 && adjustedPy >= closeY && adjustedPy <= closeY + 24) { feedbackUI.closeButtonHover = true; } switch (feedbackUI.currentPage) { case 'main': // 使用新的按钮布局参数 const buttonWidth = 200; const buttonHeight = 45; const startY = 85; const spacing = 52; for (let i = 0; i < 5; i++) { const buttonY = startY + i * spacing; const buttonX = CENTER_X - buttonWidth / 2; if (adjustedPx >= buttonX && adjustedPx <= buttonX + buttonWidth && adjustedPy >= buttonY && adjustedPy <= buttonY + buttonHeight) { feedbackUI.hoveredButton = i; break; } } break; case 'input': // 更新输入页面的按钮坐标 const inputButtonY = 225; const inputButtonStartX = 25; if (adjustedPy >= inputButtonY && adjustedPy <= inputButtonY + 24) { if (adjustedPx >= inputButtonStartX && adjustedPx <= inputButtonStartX + 70) { feedbackUI.hoveredButton = 0; // 提交 } else if (adjustedPx >= inputButtonStartX + 78 && adjustedPx <= inputButtonStartX + 133) { feedbackUI.hoveredButton = 1; // 清空 } else if (adjustedPx >= inputButtonStartX + 140 && adjustedPx <= inputButtonStartX + 195) { feedbackUI.hoveredButton = 2; // 返回 } } break; case 'login': // 更新登录页面的按钮坐标 const loginButtonY = 165; const loginButtonStartX = 35; if (adjustedPy >= loginButtonY && adjustedPy <= loginButtonY + 26) { if (adjustedPx >= loginButtonStartX && adjustedPx <= loginButtonStartX + 60) { feedbackUI.hoveredButton = 0; // 验证 } else if (adjustedPx >= loginButtonStartX + 67 && adjustedPx <= loginButtonStartX + 127) { feedbackUI.hoveredButton = 1; // 清空 } else if (adjustedPx >= loginButtonStartX + 134 && adjustedPx <= loginButtonStartX + 194) { feedbackUI.hoveredButton = 2; // 返回 } } break; case 'admin': // 更新管理页面的按钮坐标 const adminButtonWidth = 220; const adminButtonHeight = 35; const adminStartY = 90; const adminSpacing = 45; for (let i = 0; i < 4; i++) { const adminButtonY = adminStartY + i * adminSpacing; const adminButtonX = CENTER_X - adminButtonWidth / 2; if (adjustedPx >= adminButtonX && adjustedPx <= adminButtonX + adminButtonWidth && adjustedPy >= adminButtonY && adjustedPy <= adminButtonY + adminButtonHeight) { feedbackUI.hoveredButton = i; break; } } break; case 'list': // 更新列表页面坐标 const listY = 64; const itemHeight = 26; const maxVisible = 5; const feedbackList = filterValidFeedbacks(feedbackUI.feedbackData || [], true); if (feedbackList.length === 0) { if (adjustedPx >= 102 && adjustedPx <= 162 && adjustedPy >= 350 && adjustedPy <= 376) { feedbackUI.hoveredButton = 0; } } else { if (adjustedPx >= 19 && adjustedPx <= 281 && adjustedPy >= listY && adjustedPy <= listY + maxVisible * itemHeight) { const index = Math.floor((adjustedPy - listY) / itemHeight) + feedbackUI.scrollOffset; if (index < feedbackList.length) { feedbackUI.hoveredButton = index; } } // 底部按钮 if (adjustedPy >= 229 && adjustedPy <= 252) { if (adjustedPx >= 57 && adjustedPx <= 117) feedbackUI.hoveredButton = 1000; else if (adjustedPx >= 124 && adjustedPx <= 184) feedbackUI.hoveredButton = 1001; else if (adjustedPx >= 191 && adjustedPx <= 251) feedbackUI.hoveredButton = 1002; } } break; case 'detail': // 更新详情页面按钮坐标 if (adjustedPy >= 235 && adjustedPy <= 255) { if (verifyCurrentAuth()) { if (adjustedPx >= 50 && adjustedPx <= 95) feedbackUI.hoveredButton = 0; // 回复 else if (adjustedPx >= 105 && adjustedPx <= 145) feedbackUI.hoveredButton = 1; // 删除 else if (adjustedPx >= 160 && adjustedPx <= 200) feedbackUI.hoveredButton = 2; // 返回 } else { if (adjustedPx >= 92 && adjustedPx <= 132) feedbackUI.hoveredButton = 0; // 删除 else if (adjustedPx >= 137 && adjustedPx <= 177) feedbackUI.hoveredButton = 1; // 返回 } } break; case 'player': const playerListY = 64; const playerItemHeight = 26; const playerMaxVisible = 5; const playerFeedbacks = filterValidFeedbacks(feedbackUI.playerFeedbacks || [], false); if (playerFeedbacks.length === 0) { // 无反馈时,返回按钮在中间位置 - 重新计算正确坐标 const returnButtonX = CENTER_X - 30; const returnButtonY = 200; // 适中的Y位置 if (adjustedPx >= returnButtonX && adjustedPx <= returnButtonX + 60 && adjustedPy >= returnButtonY && adjustedPy <= returnButtonY + 26) { feedbackUI.hoveredButton = 0; } } else { if (adjustedPx >= 19 && adjustedPx <= 281 && adjustedPy >= playerListY && adjustedPy <= playerListY + playerMaxVisible * playerItemHeight) { const index = Math.floor((adjustedPy - playerListY) / playerItemHeight) + feedbackUI.scrollOffset; if (index < playerFeedbacks.length) { feedbackUI.hoveredButton = index; // 删除按钮检查 const listX = 19; const listWidth = 262; const itemY = playerListY + (index - feedbackUI.scrollOffset) * playerItemHeight; const deleteButtonX = listX + listWidth - 18; const deleteButtonY = itemY + 2; if (adjustedPx >= deleteButtonX && adjustedPx <= deleteButtonX + 14 && adjustedPy >= deleteButtonY && adjustedPy <= deleteButtonY + 14) { feedbackUI.deleteButtonHover = true; } else { feedbackUI.deleteButtonHover = false; } } } else { feedbackUI.deleteButtonHover = false; } // 底部按钮 if (adjustedPy >= 370 && adjustedPy <= 393) { if (adjustedPx >= 102 && adjustedPx <= 162) feedbackUI.hoveredButton = 1000; else if (adjustedPx >= 171 && adjustedPx <= 231) feedbackUI.hoveredButton = 1001; } } break; case 'stats': const statsData = filterValidFeedbacks(feedbackUI.feedbackData || [], true); if (statsData.length === 0) { if (adjustedPx >= 102 && adjustedPx <= 162 && adjustedPy >= 210 && adjustedPy <= 236) { feedbackUI.hoveredButton = 0; } } else { if (adjustedPx >= 102 && adjustedPx <= 162 && adjustedPy >= 230 && adjustedPy <= 253) { feedbackUI.hoveredButton = 0; } } break; } if (oldHovered !== feedbackUI.hoveredButton || oldCloseHover !== feedbackUI.closeButtonHover) { core.feedback_draw(); return true; } return false; }, 100); // 点击处理 core.registerAction('onclick', 'feedback_click', function (x, y, px, py) { if (core.status.event.id !== 'feedback') return false; const adjustedPx = px - UI_OFFSET_X; const adjustedPy = py - UI_OFFSET_Y; const headerWidth = UI_SIZE_WIDTH - 16; const closeX = headerWidth - 35; const closeY = 24; if (adjustedPx >= closeX && adjustedPx <= closeX + 24 && adjustedPy >= closeY && adjustedPy <= closeY + 24) { closeFeedbackSystem(); return true; } // 在详情页面的内容区域 if (feedbackUI.currentPage === 'detail') { const contentX = 20; const contentY = 85; const contentWidth = UI_SIZE_WIDTH - 40; const contentHeight = 130; if (adjustedPx >= contentX && adjustedPx <= contentX + contentWidth && adjustedPy >= contentY && adjustedPy <= contentY + contentHeight && feedbackUI.maxContentHeight > 0) { feedbackUI.isDragging = true; feedbackUI.contentDragStart = adjustedPy; return; } } const inUIArea = adjustedPx >= 0 && adjustedPx <= UI_SIZE_WIDTH && adjustedPy >= 0 && adjustedPy <= UI_SIZE_HEIGHT; if (!inUIArea && feedbackUI.inputBoxVisible) { hideInputBox(); return true; } switch (feedbackUI.currentPage) { case 'main': if (feedbackUI.hoveredButton >= 0 && feedbackUI.hoveredButton < 5) { if (feedbackUI.hoveredButton === 4) { // 作者管理 if (verifyCurrentAuth()) { feedbackUI.currentPage = 'admin'; } else { feedbackUI.currentPage = 'login'; } } else if (feedbackUI.hoveredButton === 3) { // 我的反馈 getPlayerFeedbacks(); } else { // 反馈输入 feedbackUI.selectedCategory = feedbackUI.hoveredButton; feedbackUI.currentPage = 'input'; feedbackUI.inputText = ''; } feedbackUI.hoveredButton = -1; core.feedback_draw(); core.playSound('Cursor3.ogg'); } else if (!inUIArea) { closeFeedbackSystem(); } break; case 'input': if (feedbackUI.hoveredButton === 0) { if (feedbackUI.inputText.length > 0 && feedbackUI.inputText.length <= 500) { submitFeedback(feedbackUI.inputText, ['BUG', '建议', '其他'][feedbackUI.selectedCategory]); closeFeedbackSystem(); } } else if (feedbackUI.hoveredButton === 1) { feedbackUI.inputText = ''; hideInputBox(); core.feedback_draw(); } else if (feedbackUI.hoveredButton === 2) { feedbackUI.currentPage = 'main'; hideInputBox(); core.feedback_draw(); } else if (adjustedPx >= 25 && adjustedPx <= 255 && adjustedPy >= 75 && adjustedPy <= 195) { if (!feedbackUI.inputBoxVisible) { showFeedbackInput(); } } else if (!inUIArea) { closeFeedbackSystem(); } break; case 'login': if (feedbackUI.hoveredButton === 0) { handleLogin(); } else if (feedbackUI.hoveredButton === 1) { feedbackUI.loginPassword = ''; hideInputBox(); core.feedback_draw(); } else if (feedbackUI.hoveredButton === 2) { feedbackUI.currentPage = 'main'; feedbackUI.loginPassword = ''; hideInputBox(); core.feedback_draw(); } else if (adjustedPx >= 37 && adjustedPx <= 263 && adjustedPy >= 120 && adjustedPy <= 146) { if (!feedbackUI.inputBoxVisible) { showPasswordInput(); } } else if (!inUIArea) { closeFeedbackSystem(); } break; case 'admin': if (feedbackUI.hoveredButton === 0) { // 查看所有反馈 getAllFeedback(); core.playSound('item.mp3'); } else if (feedbackUI.hoveredButton === 1) { // 反馈统计 showFeedbackStats(); core.playSound('item.mp3'); } else if (feedbackUI.hoveredButton === 2) { // 刷新数据 getAllFeedback(); core.drawTip('正在刷新数据...'); core.playSound('item.mp3'); } else if (feedbackUI.hoveredButton === 3) { // 退出作者模式 createAuthState.deauthorize(); currentAuthToken = null; core.drawTip('已退出作者模式'); feedbackUI.currentPage = 'main'; core.playSound('item.mp3'); } core.feedback_draw(); if (!inUIArea) { closeFeedbackSystem(); } break; case 'list': const feedbackList = filterValidFeedbacks(feedbackUI.feedbackData || [], true); if (feedbackList.length === 0) { if (feedbackUI.hoveredButton === 0) { // 无反馈时的返回按钮 feedbackUI.currentPage = 'admin'; core.playSound('item.mp3'); } } else { // 处理列表项点击 if (feedbackUI.hoveredButton >= 0 && feedbackUI.hoveredButton < feedbackList.length) { feedbackUI.selectedFeedback = feedbackList[feedbackUI.hoveredButton]; feedbackUI.contentScrollOffset = 0; feedbackUI.currentPage = 'detail'; core.playSound('item.mp3'); } // 处理底部按钮 else if (feedbackUI.hoveredButton === 1000) { // 刷新 getAllFeedback(); core.playSound('item.mp3'); } else if (feedbackUI.hoveredButton === 1001) { // 统计 showFeedbackStats(); core.playSound('item.mp3'); } else if (feedbackUI.hoveredButton === 1002) { // 返回 feedbackUI.currentPage = 'admin'; core.playSound('item.mp3'); } } core.feedback_draw(); if (!inUIArea) { closeFeedbackSystem(); } break; case 'detail': if (verifyCurrentAuth()) { // 作者模式下的操作 if (feedbackUI.hoveredButton === 0) { // 回复按钮 showReplyInput(); core.playSound('item.mp3'); } else if (feedbackUI.hoveredButton === 1) { // 删除按钮 deleteFeedback(); core.playSound('item.mp3'); } else if (feedbackUI.hoveredButton === 2) { // 返回按钮 feedbackUI.currentPage = feedbackUI.feedbackData ? 'list' : 'player'; core.playSound('item.mp3'); } } else { // 玩家模式下的操作 if (feedbackUI.hoveredButton === 0) { // 删除按钮 if (feedbackUI.selectedFeedback && feedbackUI.selectedFeedback.id) { deletePlayerFeedback(feedbackUI.selectedFeedback.id); core.playSound('item.mp3'); } } else if (feedbackUI.hoveredButton === 1) { // 返回按钮 feedbackUI.currentPage = 'player'; core.playSound('item.mp3'); } } core.feedback_draw(); if (!inUIArea) { closeFeedbackSystem(); } break; case 'player': const playerFeedbacks = filterValidFeedbacks(feedbackUI.playerFeedbacks || [], false); if (playerFeedbacks.length === 0) { // 无反馈时,只处理返回按钮点击 if (feedbackUI.hoveredButton === 0) { feedbackUI.currentPage = 'main'; core.feedback_draw(); } } else { // 有反馈时的正常处理 if (feedbackUI.hoveredButton >= 0 && feedbackUI.hoveredButton < 1000) { if (feedbackUI.hoveredButton < playerFeedbacks.length) { // 检查是否点击了删除按钮 if (feedbackUI.deleteButtonHover) { const feedback = playerFeedbacks[feedbackUI.hoveredButton]; deletePlayerFeedback(feedback.id); } else { // 点击反馈项,进入详情 feedbackUI.selectedFeedback = playerFeedbacks[feedbackUI.hoveredButton]; feedbackUI.contentScrollOffset = 0; feedbackUI.currentPage = 'detail'; core.feedback_draw(); } } } else if (feedbackUI.hoveredButton === 1000) { getPlayerFeedbacks(); } else if (feedbackUI.hoveredButton === 1001) { feedbackUI.currentPage = 'main'; core.feedback_draw(); } } if (!inUIArea) { closeFeedbackSystem(); } break; case 'stats': if (feedbackUI.hoveredButton === 0) { feedbackUI.currentPage = 'admin'; core.playSound('item.mp3'); core.feedback_draw(); } if (!inUIArea) { closeFeedbackSystem(); } break; } return true; }, 100); // 鼠标滚轮处理 core.registerAction('onmousewheel', 'feedback', function (direct) { if (core.status.event.id !== 'feedback') return false; // 在详情页面处理内容滚动 if (feedbackUI.currentPage === 'detail' && feedbackUI.maxContentHeight > 0) { if (direct === -1 && feedbackUI.contentScrollOffset < feedbackUI.maxContentHeight) { feedbackUI.contentScrollOffset++; core.feedback_draw(); return true; } else if (direct === 1 && feedbackUI.contentScrollOffset > 0) { feedbackUI.contentScrollOffset--; core.feedback_draw(); return true; } } // 在列表页面处理列表滚动 if ((feedbackUI.currentPage === 'list' || feedbackUI.currentPage === 'player') && !feedbackUI.inputBoxVisible) { const dataList = feedbackUI.currentPage === 'list' ? filterValidFeedbacks(feedbackUI.feedbackData || [], true) : filterValidFeedbacks(feedbackUI.playerFeedbacks || [], false); if (direct === -1) { // 向下滚动 const maxScroll = Math.max(0, dataList.length - 5); if (feedbackUI.scrollOffset < maxScroll) { feedbackUI.scrollOffset++; core.feedback_draw(); return true; } } else if (direct === 1) { // 向上滚动 if (feedbackUI.scrollOffset > 0) { feedbackUI.scrollOffset--; core.feedback_draw(); return true; } } } return false; }, 100); // 鼠标拖拽处理 core.registerAction('onmove', 'feedback_drag', function (x, y, px, py) { if (core.status.event.id !== 'feedback' || !feedbackUI.isDragging) return false; const adjustedPy = py - UI_OFFSET_Y; const deltaY = adjustedPy - feedbackUI.contentDragStart; const scrollDelta = Math.floor(deltaY / 10); // 拖拽灵敏度 if (Math.abs(scrollDelta) >= 1) { const newScrollOffset = Math.max(0, Math.min(feedbackUI.maxContentHeight, feedbackUI.contentScrollOffset - scrollDelta)); if (newScrollOffset !== feedbackUI.contentScrollOffset) { feedbackUI.contentScrollOffset = newScrollOffset; feedbackUI.contentDragStart = adjustedPy; core.feedback_draw(); } } return true; }, 100); // 鼠标松开处理 core.registerAction('onup', 'feedback', function (x, y, px, py) { if (core.status.event.id !== 'feedback') return false; if (feedbackUI.isDragging) { feedbackUI.isDragging = false; feedbackUI.contentDragStart = null; return true; } return false; }, 100); // 键盘处理 core.registerAction('keyUp', 'feedback_keyDown', function (keycode) { if (core.status.event.id !== 'feedback') { if (keycode === 74 && core.status.event.id === null && !core.status.lockControl) { //J showFeedbackSystem(); } return false; } if (keycode === 27) { // ESC if (feedbackUI.inputBoxVisible) { hideInputBox(); } else { closeFeedbackSystem(); } return true; } // 列表页面的方向键滚动 if ((feedbackUI.currentPage === 'list' || feedbackUI.currentPage === 'player') && !feedbackUI.inputBoxVisible) { const dataList = feedbackUI.currentPage === 'list' ? filterValidFeedbacks(feedbackUI.feedbackData || [], true) : filterValidFeedbacks(feedbackUI.playerFeedbacks || [], false); if (keycode === 38) { // 上箭头 if (feedbackUI.scrollOffset > 0) { feedbackUI.scrollOffset--; core.feedback_draw(); } } else if (keycode === 40) { // 下箭头 const maxScroll = Math.max(0, dataList.length - 5); if (feedbackUI.scrollOffset < maxScroll) { feedbackUI.scrollOffset++; core.feedback_draw(); } } } // 详情页面的上下箭头内容滚动 if (feedbackUI.currentPage === 'detail' && feedbackUI.maxContentHeight > 0) { if (keycode === 38) { // 上箭头 if (feedbackUI.contentScrollOffset > 0) { feedbackUI.contentScrollOffset--; core.feedback_draw(); } } else if (keycode === 40) { // 下箭头 if (feedbackUI.contentScrollOffset < feedbackUI.maxContentHeight) { feedbackUI.contentScrollOffset++; core.feedback_draw(); } } } return true; }, 100) //#endregion //#region 反馈功能实现 function submitFeedback(content, category) { if (core.isReplaying()) return; const gameInfo = { 楼层: core.status.floorId, 等级: core.status.hero.level, 生命: core.status.hero.hp, 攻击: core.status.hero.attack, 防御: core.status.hero.defense, 位置: `(${core.getHeroLoc().x}, ${core.getHeroLoc().y})`, 提交时间: new Date().toLocaleString() }; const fullContent = `【${category}】${content}\n\n游戏状态:\n${JSON.stringify(gameInfo, null, 2)}`; const timestamp = Date.now(); const tags = `${category},${timestamp},FEEDBACK`; let form = new FormData(); form.append('type', 2); form.append('towername', towerName); form.append('comment', fullContent); form.append('tags', tags); core.drawTip('正在提交反馈...'); utils.prototype.http( 'POST', 'https://h5mota.com/backend/tower/barrage.php', form, function (response) { try { const result = JSON.parse(response); if (result?.code === 0) { core.drawTip('反馈提交成功,感谢您的建议!'); core.playSound('item.mp3'); // 记录到玩家本地历史 const playerHistory = JSON.parse(localStorage.getItem('playerFeedbackHistory') || '[]'); playerHistory.push({ id: `feedback_${timestamp}`, // 生成唯一ID comment: fullContent, tags: tags, timestamp: timestamp, solved: false }); localStorage.setItem('playerFeedbackHistory', JSON.stringify(playerHistory)); } else { core.drawTip('提交失败:' + (result?.message || '未知错误')); } } catch (error) { core.drawTip('提交失败:' + error.message); } }, function (error) { console.error('网络错误:', error); core.drawTip('网络错误,请稍后重试'); }, null, null, null, 5000 ); } function getAllFeedback() { if (!verifyCurrentAuth()) { core.drawTip('权限验证失败,请重新登录'); feedbackUI.currentPage = 'login'; core.feedback_draw(); return; } let form = new FormData(); form.append('type', 1); form.append('towername', towerName); core.drawTip('正在获取反馈列表...'); utils.prototype.http( 'POST', 'https://h5mota.com/backend/tower/barrage.php', form, function (response) { if (!verifyCurrentAuth()) { core.drawTip('权限在请求过程中失效'); return; } try { const result = JSON.parse(response); const feedbackList = result?.list || []; if (feedbackList.length === 0) { core.drawTip('暂无反馈'); return; } const actualFeedbacks = feedbackList.filter(item => { if (!item.tags) return true; // 没有tags的项目保留 const tags = item.tags.split(','); return !tags.includes('REPLY'); // 排除包含REPLY标签的项目 }); // 为每个反馈生成ID(如果没有) feedbackList.forEach((item, index) => { if (!item.id) { const tags = item.tags ? item.tags.split(',') : []; const timestamp = tags[1] || Date.now(); item.id = `feedback_${timestamp}_${index}`; } }); // 按时间倒序排列 feedbackList.sort((a, b) => { const timeA = a.tags ? parseInt(a.tags.split(',')[1]) : 0; const timeB = b.tags ? parseInt(b.tags.split(',')[1]) : 0; return timeB - timeA; }); const validCount = filterValidFeedbacks(feedbackList, true).length; core.drawTip(`获取成功!共 ${validCount} 条反馈`); if (secureStoreData('feedbackData', feedbackList)) { feedbackUI.feedbackData = actualFeedbacks; feedbackUI.allFeedbackData = feedbackList; feedbackUI.currentPage = 'list'; feedbackUI.scrollOffset = 0; core.feedback_draw(); } else { core.drawTip('数据存储失败'); } } catch (error) { core.drawTip('数据解析失败:' + error.message); } }, function (error) { console.error('获取反馈失败:', error); core.drawTip('网络错误,无法获取反馈'); }, null, null, null, 3000 ); } // 根据玩家UID获取反馈历史 function getPlayerFeedbacks() { core.drawTip('正在获取您的反馈历史...'); // 获取玩家UID(需要根据你的系统调整) const playerUid = core.getCookie('id'); if (!playerUid) { core.drawTip('无法获取玩家信息'); feedbackUI.playerFeedbacks = []; feedbackUI.currentPage = 'player'; feedbackUI.scrollOffset = 0; core.feedback_draw(); 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 (response) { try { const result = JSON.parse(response); const allFeedbacks = result?.list || []; secureStoreData('feedbackData', allFeedbacks) console.log(allFeedbacks) // 为反馈分配ID(如果没有) allFeedbacks.forEach((item, index) => { if (!item.id) { const tags = item.tags ? item.tags.split(',') : []; const timestamp = tags[1] || Date.now(); item.id = `feedback_${timestamp}_${index}`; } }); // 过滤出玩家自己的反馈 const playerFeedbacks = allFeedbacks.filter(item => { // 检查是否是玩家的反馈 if (parseInt(item.userid) !== playerUid) return false; // 检查是否是FEEDBACK类型,并且不是REPLY类型 if (!item.tags) return false; const tags = item.tags.split(','); return tags.includes('FEEDBACK') && !tags.includes('REPLY'); }); // 保存所有反馈数据以便查找回复 feedbackUI.feedbackData = allFeedbacks; // 按时间倒序排列 playerFeedbacks.sort((a, b) => { const timeA = a.tags ? parseInt(a.tags.split(',')[1]) : 0; const timeB = b.tags ? parseInt(b.tags.split(',')[1]) : 0; return timeB - timeA; }); feedbackUI.playerFeedbacks = playerFeedbacks; feedbackUI.currentPage = 'player'; feedbackUI.scrollOffset = 0; core.feedback_draw(); const validCount = filterValidFeedbacks(playerFeedbacks, false).length; core.drawTip(`找到 ${validCount} 条您的反馈`); } catch (error) { console.error('获取反馈失败:', error); core.drawTip('获取反馈失败:' + error.message); feedbackUI.playerFeedbacks = []; feedbackUI.currentPage = 'player'; feedbackUI.scrollOffset = 0; core.feedback_draw(); } }, function (error) { console.error('网络错误:', error); core.drawTip('网络错误,无法获取反馈'); feedbackUI.playerFeedbacks = []; feedbackUI.currentPage = 'player'; feedbackUI.scrollOffset = 0; core.feedback_draw(); }, null, null, null, 3000 ); } async function handleLogin() { if (feedbackUI.loginPassword.length >= 12 && !createAuthState.isLocked()) { const success = await verifyPassword(feedbackUI.loginPassword); if (success) { feedbackUI.currentPage = 'admin'; feedbackUI.loginPassword = ''; hideInputBox(); core.feedback_draw(); } } } function showFeedbackStats() { if (!verifyCurrentAuth()) { core.drawTip('无权查看统计信息'); return; } const feedbackList = secureReadData('feedbackData'); if (!feedbackList || feedbackList.length === 0) { core.drawTip('暂无数据可统计,请先获取反馈'); return; } // 切换到统计页面 feedbackUI.currentPage = 'stats'; core.feedback_draw(); } // 提交回复(作为新弹幕) function submitReply() { if (!verifyCurrentAuth()) { core.drawTip('无权限操作'); return; } if (!feedbackUI.selectedFeedback) { core.drawTip('没有选中的反馈'); return; } // 弹出输入框让作者输入回复内容 const replyText = prompt('请输入回复内容:'); if (!replyText || replyText.trim().length === 0) { return; } const timestamp = Date.now(); const originalFeedbackId = feedbackUI.selectedFeedback.id; const tags = `REPLY,${timestamp},REPLY_TO_${originalFeedbackId}`; let form = new FormData(); form.append('type', 2); form.append('towername', towerName); form.append('comment', replyText.trim()); form.append('tags', tags); core.drawTip('正在提交回复...'); utils.prototype.http( 'POST', 'https://h5mota.com/backend/tower/barrage.php', form, function (response) { try { const result = JSON.parse(response); if (result?.code === 0) { core.drawTip('回复提交成功!'); core.playSound('item.mp3'); // 刷新反馈列表 getAllFeedback(); } else { core.drawTip('回复失败:' + (result?.message || '未知错误')); } } catch (error) { core.drawTip('回复失败:' + error.message); } }, function (error) { console.error('网络错误:', error); core.drawTip('网络错误,请稍后重试'); }, null, null, null, 5000 ); } // 删除反馈(添加到本地删除列表) function deleteFeedback() { if (!verifyCurrentAuth()) { core.drawTip('无权限操作'); return; } if (!feedbackUI.selectedFeedback || !feedbackUI.selectedFeedback.id) { core.drawTip('没有有效的反馈ID'); return; } // 确认删除 if (!confirm('确定要删除这条反馈吗?此操作不可撤销。')) { return; } // 添加到作者删除列表 addToAuthorDeletedList(feedbackUI.selectedFeedback.id); core.drawTip('反馈已删除'); // 返回列表页面 feedbackUI.currentPage = feedbackUI.feedbackData ? 'list' : 'player'; core.feedback_draw(); } // 玩家删除自己的反馈 function deletePlayerFeedback(feedbackId) { if (!confirm('确定要删除这条反馈吗?')) { return; } // 添加到玩家删除列表 addToPlayerDeletedList(feedbackId); core.drawTip('反馈已删除'); // 如果在详情页,返回列表页 if (feedbackUI.currentPage === 'detail') { feedbackUI.currentPage = 'player'; } // 刷新显示 core.feedback_draw(); } //#endregion //#region 主入口和退出函数 function showFeedbackSystem() { if (core.status.event.id && core.status.event.id !== 'action') return; core.lockControl(); core.status.event.id = 'feedback'; feedbackUI.currentPage = 'main'; feedbackUI.hoveredButton = -1; feedbackUI.closeButtonHover = false; feedbackUI.inputText = ''; feedbackUI.loginPassword = ''; feedbackUI.scrollOffset = 0; feedbackUI.replyText = ''; feedbackUI.editingReply = false; feedbackUI.selectedFeedback = null; feedbackUI.contentScrollOffset = 0; feedbackUI.isDragging = false; feedbackUI.contentDragStart = null; feedbackUI.maxContentHeight = 0; feedbackUI.deleteButtonHover = false; hideInputBox(); core.feedback_draw(); } function closeFeedbackSystem() { core.unlockControl(); hideInputBox(); core.status.event.id = null; feedbackUI.currentPage = 'main'; feedbackUI.hoveredButton = -1; feedbackUI.inputText = ''; feedbackUI.loginPassword = ''; feedbackUI.replyText = ''; feedbackUI.editingReply = false; feedbackUI.selectedFeedback = null; feedbackUI.contentScrollOffset = 0; feedbackUI.isDragging = false; feedbackUI.contentDragStart = null; feedbackUI.maxContentHeight = 0; feedbackUI.deleteButtonHover = false; core.updateStatusBar(); core.drawMap(); } this.showFeedbackSystem = showFeedbackSystem; //#endregion //#region 开发工具 this.generateHash = async function (password) { if (!password) { console.log('使用方法: await core.plugin.generateHash("yourPassword")'); return; } const saltedPassword = password + SECURITY_CONFIG.salt; const hash = await sha256(saltedPassword); console.log('==============================='); console.log('密码哈希生成器'); console.log('==============================='); console.log('原密码:', password); console.log('盐值:', SECURITY_CONFIG.salt); console.log('SHA-256哈希:', hash); console.log('==============================='); console.log('请将哈希值替换到 SECURITY_CONFIG.passwordHash'); return hash; }; // 调试工具:查看删除列表 this.showDeletedLists = function () { console.log('==============================='); console.log('删除列表查看器'); console.log('==============================='); console.log('玩家删除列表:', getPlayerDeletedList()); console.log('作者删除列表:', getAuthorDeletedList()); console.log('==============================='); }; // 调试工具:清空删除列表 this.clearDeletedLists = function () { core.setLocalStorage('playerDeletedFeedbacks', []); if (currentAuthToken) { core.setLocalStorage('authorDeletedFeedbacks', []); } console.log('删除列表已清空'); }; }, "boss战(新)": function () { // Boss战画布设置 const boss1 = document.createElement("canvas"); boss1.style.position = "absolute"; boss1.style.zIndex = 310; boss1.style.display = "none"; boss1.id = "boss1"; main.dom.gameGroup.insertAdjacentElement("afterend", boss1); boss1.style.top = "50%"; boss1.style.left = "50%"; boss1.style.transform = "translate(-50%,-50%)"; const ctx1 = boss1.getContext("2d"); main.dom.boss1 = boss1; const boss2 = document.createElement("canvas"); boss2.style.position = "absolute"; boss2.style.zIndex = 312; boss2.style.display = "none"; boss2.id = "boss"; main.dom.gameGroup.insertAdjacentElement("afterend", boss2); boss2.style.top = "50%"; boss2.style.left = "50%"; boss2.style.transform = "translate(-50%,-50%)"; const ctx2 = boss2.getContext("2d"); main.dom.boss2 = boss2; const boss3 = document.createElement("canvas"); boss3.style.position = "absolute"; boss3.style.zIndex = 313; boss3.style.display = "none"; boss3.id = "boss3"; main.dom.gameGroup.insertAdjacentElement("afterend", boss3); boss3.style.top = "50%"; boss3.style.left = "50%"; boss3.style.transform = "translate(-50%,-50%)"; const ctx3 = boss3.getContext("2d"); main.dom.boss3 = boss3; const boss4 = document.createElement("canvas"); boss4.style.position = "absolute"; boss4.style.zIndex = 314; boss4.style.display = "none"; boss4.id = "boss4"; main.dom.gameGroup.insertAdjacentElement("afterend", boss4); boss4.style.top = "50%"; boss4.style.left = "50%"; boss4.style.transform = "translate(-50%,-50%)"; const ctx4 = boss4.getContext("2d"); main.dom.boss4 = boss4; const boss5 = document.createElement("canvas"); boss5.style.position = "absolute"; boss5.style.zIndex = 315; boss5.style.display = "none"; boss5.id = "boss5"; main.dom.gameGroup.insertAdjacentElement("afterend", boss5); boss5.style.top = "50%"; boss5.style.left = "50%"; boss5.style.transform = "translate(-50%,-50%)"; const ctx5 = boss5.getContext("2d"); main.dom.boss5 = boss5; const { sleep } = core.plugin.animate; const ExCard = ["DefendM", "DefendL", "DefendG", "AttackM", "AttackL", "AttackG", "GaWuCut", "Charge", "PowerfulAttack"]; // 特殊战允许的卡牌 // 点击区域判断 function inRect(a, b, x, y, w, h) { var dx = x + w, dy = y + h; return x <= a && a <= dx && y <= b && b <= dy } // 动画系统 let ani = []; core.bosstime = 0; function animateFrame(name, animate, centerX, centerY, index) { var a = core.getContextByName(name); if (!a) return; var frame = animate.frames[index % animate.frame]; core.playSound((animate.se || {})[index % animate.frame + 1], (animate.pitch || {})[index % animate.frame + 1]); var ratio = animate.ratio; if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(a, 416, 676); } else { core.maps._setHDCanvasSize(a, 676, 416); } frame.forEach(function (t) { var image = animate.images[t.index]; if (!image) return; var realWidth = image.width * ratio * t.zoom / 100; var realHeight = image.height * ratio * t.zoom / 100; core.setAlpha(a, t.opacity / 255); var cx = centerX + t.x, cy = centerY + t.y; var ix = cx - realWidth / 2, iy = cy - realHeight / 2; var mirror = t.mirror ? 'x' : null; var angle = t.angle ? -t.angle * Math.PI / 180 : null; core.drawImage(a, image, ix, iy, realWidth, realHeight, null, null, null, null, angle, mirror); core.setAlpha(a, 1); }); } function drawanimate(timestamp) { if (timestamp - core.bosstime < 50) return; core.bosstime = timestamp; if (!ani || ani.length == 0) return; core.clearMap(ctx4); // 更新帧 for (var i = 0; i < ani.length; i++) { var obj = ani[i]; if (obj.index >= obj.animate.frames.length) { (callback => { setTimeout(function () { callback?.(); }, 1); })(obj.callback); } } ani = ani.filter(function (obj) { return obj.index < obj.animate.frames.length; }); ani.forEach(function (obj) { animateFrame(ctx4, obj.animate, obj.centerX, obj.centerY, obj.index++); }); } class BossBattle { constructor() { this.init(); } init() { // 战斗基础属性 this.enemyId = null; this.x = 0; this.y = 0; this.floorId = null; // 战斗状态 this.mon_hp = 0; this.hero_hp = 0; this.hero_atk = 0; this.hero_def = 0; this.hero_money = 0; this.cards = []; this.minATK = 0; this.maxATK = 0; this.minDEF = 0; this.maxDEF = 0; this.randATK = 0; this.randDEF = 0; this.全力攻击 = false; // 战斗变量 this.ATKbet = 0; this.DEFbet = 0; this.mon_hp_status = 0; this.mon_atk_status = 0; this.mon_def_status = 0; this.mon_mark_status = 0; this.health_count = 0; this.turn = 1; this.k = 1; this.hero_atk_status = 0; this.hero_def_status = 0; this.deadfor = 0; this.oneTurnDamage = 0; this.nocardshift = false; // UI状态 this.isReplay = false; this.battleResult = null; // 牌库 this.battleDeck = { AttackM: { cost: 1, effect: () => { this.minATK++; this.maxATK += 10 } }, AttackL: { cost: 2, effect: () => { this.minATK++; this.maxATK += 20 } }, AttackG: { cost: 3, effect: () => { this.minATK++; this.maxATK += 50 } }, GaWuCut: { cost: 4, effect: () => { this.minATK++; this.maxATK += 120 } }, Charge: { cost: 5, effect: () => { this.minATK += 20 this.maxATK += 20 this.cards.push("PowerfulAttack") } }, PowerfulAttack: { cost: 3, effect: () => { if (this.全力攻击) { this.minATK += 6; this.maxATK += 6; } else { this.minATK += 150; this.maxATK += 150; this.全力攻击 = true } } }, DefendM: { cost: 1, effect: () => { this.minDEF++; this.maxDEF += 20 } }, DefendL: { cost: 2, effect: () => { this.minDEF++; this.maxDEF += 50 } }, DefendG: { cost: 3, effect: () => { this.minDEF++; this.maxDEF += 110 } }, } } setup(enemyId, x, y, floorId) { this.init(); this.enemyId = enemyId; this.x = x; this.y = y; this.floorId = floorId || core.status.floorId; this.isReplay = main.replayChecking || core.isReplaying() this.oneTurnDamage = 0; const enemy = core.getEnemyInfo(this.enemyId, undefined, this.x, this.y, this.floorId); this.mon_hp = this.mon_hp_status = enemy.hp; this.mon_atk_status = enemy.atk; this.mon_def_status = enemy.def; this.mon_mark_status = enemy.mark; this.cards = core.status.hero.myCard.filter(v => ExCard.includes(v)); this.hero_hp = core.getRealStatus('hp'); this.hero_atk = core.getRealStatus('atk'); this.hero_def = core.getRealStatus('def'); this.hero_money = core.status.hero.money; this.ATKbet = flags.Trident * 3; this.DEFbet = flags.Trident * 5; this.health_count = flags.health.count; this.applyHeroModifiers(); // 初始化BossUI if (!this.isReplay) { core.registerAnimationFrame("Boss", true, drawanimate); boss1.style.display = "block"; boss2.style.display = "block"; boss3.style.display = "block"; boss4.style.display = "block"; boss5.style.display = "block"; this.update(); } } // 应用英雄属性修正 applyHeroModifiers() { if (core.status.hero.name === "娜蒂斯") this.hero_atk += core.status.hero.myCard.length * flags.ratio; if (flags.KingsPower) this.hero_atk += 15 * flags.ratio; if (flags.Immovable) this.hero_def += 15 * flags.ratio; if (core.hasItem("FlashlightS2")) this.hero_atk += Math.floor(flags.star.level * Math.sqrt(flags.ratio)); if (flags.jasmine) this.hero_def = 0; if (core.hasItem("RevengeHalberd")) this.ATKbet += Math.floor(core.status.hero.statistics.battleDamage / 1000); if (core.hasItem("PremiumSwordShield2")) { const PSS = flags.hasChip.length; this.ATKbet += PSS * 2; this.DEFbet += PSS * 5; } // 战斗连击修正 if (core.hasItem("RollerSkates")) this.k *= 2; if (flags.jasmine) this.k *= 3; if (flags.Haste) this.k *= 3; } recordAction(action) { core.status.route.push(core.encodeBase64(JSON.stringify(action))); } getNextAction() { if (this.isReplay && core.status.replay.toReplay.length > 0) { const a = core.status.replay.toReplay.shift() const action = JSON.parse(core.decodeBase64(a)); core.status.route.push(a); // 重新记录操作 return action; } return null; } drawFolds(cards) { this.cards = this.cards.concat(cards) for (;;) { if (this.cards.length <= 8) return this.cards.shift() } } * battleFlow() { while (true) { // 回合开始 hero.cost = hero.costmax // 状态修正 if (core.hasItem("AdrenalineR") && this.hero_hp <= 125 * flags.ratio) this.hero_atk_status += 30 * flags.ratio; if (core.hasItem("AdrenalineH") && this.hero_hp <= 125 * flags.ratio) this.hero_atk_status += 80 * flags.ratio; if (core.hasItem("UtilityKnifeB2") && this.hero_hp >= 1000 * flags.ratio) this.hero_atk_status += flags.health.count; if (core.hasItem("UtilityKnifeS2") && this.hero_hp >= 800 * flags.ratio) this.hero_atk_status += flags.health.count; if (core.hasItem("StarCoinHammer2") && flags.star.level > 24) this.hero_atk_status += Math.min( Math.floor(this.hero_money * flags.ratio / (flags.MapStatus?.Map === "魔法学院" ? 1000 : 100)), 1000 * flags.ratio ); if (core.hasItem("NormalSight")) this.hero_def_status += this.mon_mark_status * 2; if (core.hasItem("EagleEyeSight")) this.hero_def_status += Math.floor(this.mon_mark_status * Math.sqrt(flags.ratio * 2)); if (core.status.hero.name === "帕帕拉") this.hero_atk_status += Math.floor(this.hero_hp / 300); this.update() // 玩家回合 const playerAction = this.isReplay ? this.getNextAction() : yield this.playerTurn(); if (!playerAction || playerAction.type !== "PLAYER_TURN") break; //此处增加攻击模式抉择后的执行 if (playerAction.action === "SKIP") { for (let i = 0; i < 4; i++) { this.drawFolds(ExCard[core.rand(ExCard.length - 1)]) } if (this.turn % 5 === 3 && this.enemyId === "Mamushi") this.mon_atk_status += 300; } else if (playerAction.action === "ATTACK") { if (this.isReplay) { playerAction.card.forEach(v => { this.costdown(this.cards[v]) this.cards.splice(v, 1) }) } // 执行玩家攻击 const playerDamage = this.calculatePlayerDamage(); this.mon_hp_status -= playerDamage; this.damage += playerDamage; if (!this.isReplay) { yield this.playAnimate("hand", "boss"); } } else { console.error("无法识别的action:" + playerAction.action) } this.update(); if (this.mon_hp_status <= 0) { return { win: true, damage: core.getRealStatus('hp') - this.hero_hp, turn: this.turn }; } // 怪物回合 // 执行怪物攻击 const enemyDamage = this.calculateEnemyDamage(); this.hero_hp -= enemyDamage; this.update(); if (!this.isReplay) { yield this.playAnimate("hand", "hero"); } // 检查战斗结果 if (this.hero_hp <= 0) { return { win: false }; } // 回合结束 this.turnEnd(); } } costdown(card) { if (hero.cost < this.battleDeck[card].cost) return false hero.cost -= this.battleDeck[card].cost this.battleDeck[card].effect() return true } playerTurn() { // 游玩模式:等待玩家操作 return new Promise(resolve => { // 这里可以显示UI让玩家选择卡牌或技能 // 示例:模拟玩家点击攻击按钮 this.cho = "player" this.drawplayer() const card = [] const fn = e => { try { e.preventDefault(); if (!core.isPlaying()) return false; const left = core.dom.gameGroup.offsetLeft; const top = core.dom.gameGroup.offsetTop; const px = Math.floor((e.clientX - left) / core.domStyle.scale), py = Math.floor((e.clientY - top) / core.domStyle.scale); let action if (this.cho !== "player") return if (!core.domStyle.isVertical) { if (py >= 254) { if (this.cards.length <= 6) { const cardweight = (676 - 102 * this.cards.length) / 2 if (px < cardweight || px > 676 - cardweight) return const index = Math.floor((px - cardweight) / 102) if (!this.costdown(this.cards[index])) return card.push(index) this.cards.splice(index, 1) } else { let index if (px >= 574) index = this.cards.length - 1 else index = Math.floor(px / (574 / (this.cards.length - 1))) if (!this.costdown(this.cards[index])) return card.push(index) this.cards.splice(index, 1) } this.update() return } else if (inRect(px, py, 400, 220, 130, 28) && this.minATK === 0 && this.minDEF === 0) { action = { type: "PLAYER_TURN", action: "SKIP" } } else if (inRect(px, py, 550, 220, 110, 28)) { action = { type: "PLAYER_TURN", card: card, action: "ATTACK" } } else return } else { if (this.cards.length > 4) { if (py >= 345.66 & py < 510.83) { const index = 4 + Math.floor(px / 104) if (!this.costdown(this.cards[index])) return card.push(index) this.cards.splice(index, 1) this.update() return } else if (py >= 510.83) { const index = Math.floor(px / 104) if (!this.costdown(this.cards[index])) return card.push(index) this.cards.splice(index, 1) this.update() return } else if (inRect(px, py, 187, 312.66, 113, 28) && this.minATK === 0 && this.minDEF === 0) action = { type: "PLAYER_TURN", action: "SKIP" } else if (inRect(px, py, 303, 312.66, 110, 28)) action = { type: "PLAYER_TURN", card: card, action: "ATTACK" } else return } else { if (py >= 510.83) { const index = Math.floor(px / 104) if (!this.costdown(this.cards[index])) return card.push(index) this.cards.splice(index, 1) this.update() return } else if (inRect(px, py, 187, 478.53, 113, 28) && this.minATK === 0 && this.minDEF === 0) action = { type: "PLAYER_TURN", action: "SKIP" } else if (inRect(px, py, 303, 478.53, 110, 28)) action = { type: "PLAYER_TURN", card: card, action: "ATTACK" } else return } } this.recordAction(action); core.clearMap(ctx5) delete this.cho boss5.removeEventListener("click", fn) resolve(action); } catch (ee) { main.log(ee); } } boss5.addEventListener("click", fn) }); } playAnimate(name, who) { // 游玩模式:异步等待动画完成 return new Promise(resolve => { this.animate(name, who, resolve); }) } // 计算玩家伤害 calculatePlayerDamage() { if (core.hasItem("NormalSight")) this.mon_mark_status += 2; if (core.hasItem("EagleEyeSight")) this.mon_mark_status += 3; // 出牌调整 this.randATK = core.rand(this.maxATK - this.minATK + 1) + this.minATK // 计算基础伤害 let mondamage = Math.max( Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.randATK) / 100) - this.mon_def_status, 0 ) * this.k + this.mon_mark_status; // 特殊效果 if (core.status.hero.name === "梅加斯") mondamage += Math.floor(core.getRealStatus('atk') * Math.sqrt(flags.ratio) / 5); if (core.hasItem("StarCoinHammer2") && flags.star.level > 24) this.hero_money = Math.max(this.hero_money - 75 * flags.ratio, 0); if (this.turn % 5 === 3 && this.enemyId === "Mamushi") { mondamage = Math.max(mondamage - Math.floor(this.mon_hp_status / 37.5), 0); if (mondamage > 0) { this.nocardshift = true; } else { this.mon_atk_status += 300; } } if (this.enemyId === "Souri") { mondamage = Math.floor(mondamage * (1 + this.turn / 10)); } return mondamage; } // 计算怪物伤害 calculateEnemyDamage() { let damage = 0; this.鼠鼠 = false; // 出牌调整 this.randDEF = core.rand(this.maxDEF - this.minDEF + 1) + this.minDEF switch (this.enemyId) { case "Masao": if (this.turn % 6 === 0) { this.mon_hp_status += this.mon_atk_status } else damage = this.mon_atk_status - Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.randDEF) / 100); break; case "SeaGawu": if (this.turn % 6 === 5) { this.mon_def_status += Math.floor(this.mon_def_status / 10); this.mon_atk_status += Math.floor(this.mon_atk_status / 10); } if (this.turn % 6 === 3) damage = this.mon_atk_status * 3 - Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.randDEF) / 100); else damage = this.mon_atk_status - Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.randDEF) / 100); break; case "LionGawu": damage = this.mon_atk_status; this.mon_atk_status += Math.floor(this.mon_atk_status / 50); break; case "ADMasao": damage = this.mon_atk_status - Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.randDEF) / 100); if (this.turn % 4 === 2) { damage += this.mon_atk_status; this.mon_hp_status += Math.floor(this.mon_hp / 10); } break; case "SOMasao": damage = this.mon_atk_status - Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.randDEF) / 100); if (this.turn % 6 === 0) { damage += Math.floor(this.hero_hp / 10); this.mon_atk_status += Math.floor(this.mon_hp / 100); } break; case "Souri": damage = Math.floor(this.mon_atk_status * (1 + this.turn / 10)) - Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.randDEF) / 100); if (this.turn % 4 === 0) this.mon_atk_status += Math.max(0, Math.floor(damage / 10)); break; case "Mamushi": damage = this.mon_atk_status - Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.randDEF) / 100) if (this.cards.length < 5) { damage = Math.floor(damage * (10 - this.cards.length) / 5); } if (!this.nocardshift) { this.cards.shift(); if (this.turn % 5 === 3) { damage *= 2; } } else { this.nocardshift = false; } break; } return this.applyDamageModifiers(damage); } // 应用伤害修正 applyDamageModifiers(damage) { if (core.hasItem("BufferShieldR")) damage -= Math.floor(core.getRealStatus('def') * (flags.health.count + 1) / (15 * flags.ratio)); if (core.status.hero.name === "恋" && damage > 0 && flags.mouseShield > 0) { if (damage <= 75 * flags.ratio) damage = 0; else damage = Math.max(0, damage - 75 * flags.ratio); flags.mouseShield--; this.鼠鼠 = true; } let damage_per_down = 0; if (core.hasItem("BufferShieldP2")) damage_per_down += Math.floor(core.getRealStatus('def') * flags.health.count / (75 * flags.ratio)); if (core.status.hero.name === "阿尔") damage_per_down += flags.star.level; if (core.status.hero.name === "潘大猛") damage_per_down -= 40; return Math.max(Math.ceil(damage * (100 - Math.min(damage_per_down, 75)) / 100), 0); } // 回合结束处理 turnEnd() { this.turn++; flags.health.count += flags.health.upbuff; this.hero_hp += flags.health.count; this.minATK = this.minDEF = this.maxATK = this.maxDEF = this.hero_atk_status = this.hero_def_status = this.oneTurnDamage = 0; this.全力攻击 = false; if (core.hasItem("VitaminPills")) { flags.health.count = Math.floor(flags.health.count * 3 / 4); } else { flags.health.count = Math.floor(flags.health.count * 3 / 5); } } afterBattle() { if (!this.isReplay) { core.unregisterAnimationFrame("Boss", true, drawanimate); boss1.style.display = "none"; boss2.style.display = "none"; boss3.style.display = "none"; boss4.style.display = "none"; boss5.style.display = "none"; core.clearMap(ctx1); core.clearMap(ctx2); core.clearMap(ctx3); core.clearMap(ctx4); core.clearMap(ctx5); } return this.battleResult; } animate(name, who, callback) { if (core.domStyle.isVertical) { if (who === "hero") { this.playanimate(name, 59, 147, callback); } else { this.playanimate(name, 365, 147, callback); } } else { if (who === "hero") { this.playanimate(name, 154, 64, callback); } else { this.playanimate(name, 522, 64, callback); } } } playanimate(name, x, y, callback) { name = core.getMappedName(name); if (this.isReplay || !core.material.animates[name] || x == null || y == null) { if (callback) callback(); return Promise.resolve(-1); } var animate = core.material.animates[name], centerX = x, centerY = y; animate.se = animate.se || {}; if (typeof animate.se == 'string') animate.se = { 1: animate.se }; var id = setTimeout(null); ani.push({ "name": name, "id": id, "animate": animate, "centerX": centerX, "centerY": centerY, "index": 0, "callback": () => { callback?.(); } }); } // UI相关方法 drawplayer() { if (!this.cho) return if (!core.domStyle.isVertical) { const gradient = ctx5.createLinearGradient(5, 220, hero.costmax * 22 + 14, 220) gradient.addColorStop(0, 'rgba(51,51,51,1)') gradient.addColorStop(0.4, 'rgba(51,51,51,1)') gradient.addColorStop(0.55, 'rgba(51,51,51,0)') gradient.addColorStop(1, 'rgba(51,51,51,0)') core.fillRoundRect(ctx5, 5, 220, hero.costmax * 22 + 9, 28, 14, gradient) core.strokeRoundRect(ctx5, 5, 220, hero.costmax * 22 + 9, 28, 14, "#000", 2) for (let i = 0; i < hero.costmax; i++) { if (i < hero.cost) core.drawIcon(ctx5, "AttackCost", 10 + i * 22, 225, 20, 20) else core.drawIcon(ctx5, "EmptyCost", 11 + i * 22, 226, 18, 18) } if (this.minATK === 0 && this.minDEF === 0) { core.fillRoundRect(ctx5, 400, 220, 130, 28, 14, "#FF2B91") core.strokeRoundRect(ctx5, 400, 220, 130, 28, 14, "#000", 2) core.drawIcon(ctx5, "card", 408, 224, 20, 20) core.fillBoldText(ctx5, "+4 跳过攻击", 428, 237.5, "#000", "#FFF", 2, "14px STHUPO") } core.fillRoundRect(ctx5, 550, 220, 110, 28, 14, "#FF2B91") core.strokeRoundRect(ctx5, 550, 220, 110, 28, 14, "#000", 2) core.setTextAlign(ctx5, "center") core.fillBoldText(ctx5, "开 始 战 斗", 605, 237.5, "#000", "#FFF", 2, "14px STHUPO") if (this.cards.length <= 6) { let postX = 338 - 51 * this.cards.length for (let i = 0; i < this.cards.length; i++) { core.drawImage(ctx5, "Battle" + this.cards[i] + ".webp", postX, 254, 102, 162) postX += 102 } } else { let postX = 0, dx = 574 / (this.cards.length - 1) for (let i = 0; i < this.cards.length; i++) { core.drawImage(ctx5, "Battle" + this.cards[i] + ".webp", postX, 254, 102, 162) postX += dx } } } else { const costBar = this.cards.length <= 4 ? 165.87 : 0 const gradient = ctx5.createLinearGradient(3, 312.66 + costBar, hero.costmax * 22 + 14, 312.66 + costBar) gradient.addColorStop(0, 'rgba(51,51,51,1)') gradient.addColorStop(0.4, 'rgba(51,51,51,1)') gradient.addColorStop(0.55, 'rgba(51,51,51,0)') gradient.addColorStop(1, 'rgba(51,51,51,0)') core.fillRoundRect(ctx5, 3, 312.66 + costBar, hero.costmax * 22 + 5, 28, 14, gradient) core.strokeRoundRect(ctx5, 3, 312.66 + costBar, hero.costmax * 22 + 5, 28, 14, "#000", 2) for (let i = 0; i < hero.costmax; i++) { if (i < hero.cost) core.drawIcon(ctx5, "AttackCost", 6 + i * 22, 317 + costBar, 20, 20) else core.drawIcon(ctx5, "EmptyCost", 7 + i * 22, 318 + costBar, 18, 18) } if (this.minATK === 0 && this.minDEF === 0) { core.fillRoundRect(ctx5, 187, 312.66 + costBar, 113, 28, 14, "#FF2B91") core.strokeRoundRect(ctx5, 187, 312.66 + costBar, 113, 28, 14, "#000", 2) core.drawIcon(ctx5, "card", 192, 316.66 + costBar, 20, 20) core.fillBoldText(ctx5, "+4 跳过攻击", 210, 330.16 + costBar, "#000", "#FFF", 2, "14px STHUPO") } core.fillRoundRect(ctx5, 303, 312.66 + costBar, 110, 28, 14, "#FF2B91") core.strokeRoundRect(ctx5, 303, 312.66 + costBar, 110, 28, 14, "#000", 2) core.setTextAlign(ctx5, "center") core.fillBoldText(ctx5, "开 始 战 斗", 358, 330.16 + costBar, "#000", "#FFF", 2, "14px STHUPO") for (let i = 0; i < (this.cards.length <= 4 ? this.cards.length : 4); i++) core.drawImage(ctx5, "Battle" + this.cards[i] + ".webp", i * 104, 510.83, 104, 165.87) for (let i = 4; i < this.cards.length; i++) core.drawImage(ctx5, "Battle" + this.cards[i] + ".webp", (i - 4) * 104, 345.66, 104, 165.87) } } drawStatus() { let C16 = "#FFF" switch (this.enemyId) { case "Masao": case "ADMasao": case "SOMasao": if (this.turn % 6 === 0) C16 = "#F00" break; case "SeaGawu": if (this.turn % 6 === 3) C16 = "#F00" if (this.turn % 6 === 5) C16 = "#6FC" break; case "Souri": if (this.turn % 4 === 0) C16 = "#F00" break; case "Mamushi": if (this.turn % 5 === 3) C16 = "#F00" break; } if (core.domStyle.isVertical) { if (this.enemyId === "LionGawu") { core.drawTextContent(ctx1, `\\i[BuffHealth]${flags.health.count} \\i[hp]${this.hero_hp} \\i[atk]${Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.minATK) / 100)}~${Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.maxATK) / 100)} }`, { left: 0, top: 7, color: "#FFFFFF", align: "left", maxWidth: 200, fontSize: 15, font: "cjk" }) } else { core.drawTextContent(ctx1, `\\i[BuffHealth]${flags.health.count} \\i[hp]${this.hero_hp} \\i[atk]${Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.minATK) / 100)}~${Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.maxATK) / 100)} \\i[def]${Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.minDEF) / 100)}~${Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.maxDEF) / 100)}`, { left: 0, top: 7, color: "#FFFFFF", align: "left", maxWidth: 200, fontSize: 15, font: "cjk" }) } if (this.enemyId === "Mamushi" && this.turn % 5 === 3) { core.drawTextContent(ctx1, `${this.mon_mark_status}\\i[BuffMark] ${this.mon_hp_status}\\i[hp] ${this.mon_atk_status}\\i[atk] ${this.mon_def_status}\\i[def] ${Math.floor(this.mon_hp_status / 37.5)}\\i[shield0]`, { left: 216, top: 7, color: "#FFFFFF", align: "right", maxWidth: 200, fontSize: 15, font: "cjk" }) } else { core.drawTextContent(ctx1, `${this.mon_mark_status}\\i[BuffMark] ${this.mon_hp_status}\\i[hp] ${this.mon_atk_status}\\i[atk] ${this.mon_def_status}\\i[def]`, { left: 216, top: 7, color: "#FFFFFF", align: "right", maxWidth: 200, fontSize: 15, font: "cjk" }) } ctx1.textAlign = "center" core.fillBoldText(ctx1, "回合 " + this.turn, 208, 23, C16, "#000", 2, "24px STHUPO") } else { if (this.enemyId === "LionGawu") { core.drawTextContent(ctx1, `\\i[BuffHealth]${flags.health.count} \\i[hp]${this.hero_hp} \\i[atk]${Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.minATK) / 100)}~${Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.maxATK) / 100)} `, { left: 0, top: 30, color: "#FFFFFF", align: "left", maxWidth: 200, fontSize: 15, font: "cjk" }) } else { core.drawTextContent(ctx1, `\\i[BuffHealth]${flags.health.count} \\i[hp]${this.hero_hp} \\i[atk]${Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.minATK) / 100)}~${Math.floor((this.hero_atk + this.hero_atk_status) * (100 + this.ATKbet + this.maxATK) / 100)} \\i[def]${Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.minDEF) / 100)}~${Math.floor((this.hero_def_status + this.hero_def) * (100 + this.DEFbet + this.maxDEF) / 100)}`, { left: 0, top: 30, color: "#FFFFFF", align: "left", maxWidth: 200, fontSize: 15, font: "cjk" }) } if (this.enemyId === "Mamushi" && this.turn % 5 === 3) { core.drawTextContent(ctx1, `${this.mon_mark_status}\\i[BuffMark] ${this.mon_hp_status}\\i[hp] ${this.mon_atk_status}\\i[atk] ${this.mon_def_status}\\i[def] ${Math.floor(this.mon_hp_status / 37.5)}\\i[shield0]`, { left: 476, top: 30, color: "#FFFFFF", align: "right", maxWidth: 200, fontSize: 15, font: "cjk" }) } else { core.drawTextContent(ctx1, `${this.mon_mark_status}\\i[BuffMark] ${this.mon_hp_status}\\i[hp] ${this.mon_atk_status}\\i[atk] ${this.mon_def_status}\\i[def] `, { left: 476, top: 30, color: "#FFFFFF", align: "right", maxWidth: 200, fontSize: 15, font: "cjk" }) } ctx1.textAlign = "center" core.fillBoldText(ctx1, "回合 " + this.turn, 338, 23, C16, "#000", 2, "24px STHUPO") } } drawHero() { core.clearMap(ctx2); if (core.domStyle.isVertical) { core.drawImage(ctx2, core.plugin.Character[core.status.hero.name].Sprite, 5, 93, 108, 108); } else { core.drawImage(ctx2, core.plugin.Character[core.status.hero.name].Sprite, 100, 10, 108, 108); } } drawEnemy() { core.clearMap(ctx3); if (core.domStyle.isVertical) { core.drawImage(ctx3, this.enemyId + "_Sprite.png", 311, 93, 108, 108); } else { core.drawImage(ctx3, this.enemyId + "_Sprite.png", 468, 10, 108, 108); } } update() { core.clearMap(ctx1); core.clearMap(ctx2); core.clearMap(ctx3); core.clearMap(ctx4); core.clearMap(ctx5); if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(ctx1, 416, 676); core.maps._setHDCanvasSize(ctx2, 416, 676); core.maps._setHDCanvasSize(ctx3, 416, 676); core.maps._setHDCanvasSize(ctx4, 416, 676); core.maps._setHDCanvasSize(ctx5, 416, 676); core.drawImage(ctx1, "VerticalStartUI.png", 0, 0, 416, 676); //竖屏背景 } else { core.maps._setHDCanvasSize(ctx1, 676, 416); core.maps._setHDCanvasSize(ctx2, 676, 416); core.maps._setHDCanvasSize(ctx3, 676, 416); core.maps._setHDCanvasSize(ctx4, 676, 416); core.maps._setHDCanvasSize(ctx5, 676, 416); core.drawImage(ctx1, "StartUI.png", 0, 0, 676, 416); //横屏背景 } this.drawStatus(); this.drawHero(); this.drawEnemy(); this.drawplayer(); } } // 录像适配 - 同步执行 this.ExBoss_replay = function (enemyId, x, y, floorId, callback) { core.lockControl(); const battle = core.boss; battle.setup(enemyId, x, y, floorId); // 同步执行战斗流程 const battleGenerator = battle.battleFlow(); let result, nextValue; while (true) { const { value, done } = battleGenerator.next(nextValue); if (done) { result = value; break; } nextValue = value; // 对于同步执行,这里不需要处理异步值 } battle.afterBattle(); // 处理战斗结果 processBattleResult(result, enemyId, x, y, floorId, callback); }; // 战斗调用 - 异步执行 this.ExBoss = function (enemyId, x, y, floorId, callback) { core.lockControl(); core.playBgm("Valsqotch.mp3") const battle = core.boss; battle.setup(enemyId, x, y, floorId); // 异步执行战斗流程 (async function () { const battleGenerator = battle.battleFlow(); let result, nextValue; while (true) { const { value, done } = battleGenerator.next(nextValue); if (done) { result = value; break; } nextValue = await value; // 等待异步操作完成 } battle.afterBattle(); // 处理战斗结果 processBattleResult(result, enemyId, x, y, floorId, callback); })(); }; // 公共的战斗结果处理 function processBattleResult(result, enemyId, x, y, floorId, callback) { // 事件的处理 var todo = []; if (result && result.win) { var enemy = core.material.enemys[enemyId]; var special = core.getEnemyValue(enemy, 'special', x, y); var mark = battle.mon_mark_status; // 胜利处理 if (core.hasItem("DreamlinerModel")) { // 梦想号模型 flags.DreamlinerModel += result.turn if (flags.DreamlinerModel >= 30) { const round = Math.floor(flags.DreamlinerModel / 30) flags.DreamlinerModel -= 30 * round if (hero.ActiveCD > 0) hero.ActiveCD = Math.max(hero.ActiveCD - round, 0) } } // 扣减体力值并记录统计数据 const damage = result.damage core.status.hero.hp -= result.damage; core.status.hero.statistics.battleDamage += result.damage; core.status.hero.statistics.battle++; flags.cardATK = 0; flags.cardDEF = 0; if (flags.KingsPower) flags.KingsPower-- if (flags.KingsPower <= 0) delete flags.KingsPower if (flags.Immovable) flags.Immovable-- if (flags.Immovable <= 0) delete flags.Immovable delete flags.FinalAttack delete flags.Haste // 技能冷却缩减 if (hero.ActiveCD > 0) hero.ActiveCD-- if (core.hasItem("ExtraBatteryC") && hero.ActiveCD > 0) hero.ActiveCD-- if (hero.cost < hero.costmax) hero.costCD-- if (hero.costCD <= 0) { hero.costCD = 10 hero.cost++ } if (hero.cost === hero.costmax) hero.costCD = 10 // 获得金币 var money = core.getEnemyInfo(enemy, void 0, x, y).money if (core.hasItem('BankcardU2')) money *= 2; // 银行卡-用不完 if (core.hasItem('coin')) money *= 2; // 幸运金币 core.status.hero.money += money; core.status.hero.statistics.money += money; //智能手表 if (core.hasItem("Smartwatch") && hero.myCard.length < 3) { core.drawFolds(core.plugin.card.cardList[core.randcard()]) hero.cost++ if (hero.cost > hero.costmax) hero.cost = hero.costmax } // 角色技能处理 const Character = core.plugin.Character[hero.name] if (Character && Character.PassiveTime.includes("afterBattle")) { Character.Passive() } if (hero.name === "美咲") { if (damage <= 50 && flags.CharacterATK < 30 * flags.ratio) flags.CharacterATK += 3 if (damage > 200 * flags.ratio) flags.CharacterATK = 0 } if (hero.name === "蓝海晴") { hero.money += battle.mon_mark_status ?? 0 hero.statistics.money += battle.mon_mark_status ?? 0 } if (hero.name === "璐璐") { if (damage > flags.ratio * 3 * flags.health.count && hero.ActiveCD > 0) hero.ActiveCD-- } if (hero.name === "阿兰娜") { if (damage === 0 && flags.CharacterDEF < 80 * flags.ratio) flags.CharacterDEF = Math.min(flags.CharacterDEF + flags.ratio, 80 * flags.ratio) if (damage > 0) flags.CharacterDEF = Math.max(flags.CharacterDEF - damage, 0) } if (hero.name === "姬梦朝") { hero.myCard.forEach(i => { if (i === "TalismanMisfortune") hero.hp -= flags.ratio ** 2 if (hero.hp <= 0) return core.lose("世事无常") }) } delete flags.jasmine // 星光额外金币加成 if (flags.star.level) flags.star.count++; if (flags.star.count === 3) { const StarlightPrice = flags.star.level * flags.ratio flags.star.count = 0; hero.money += StarlightPrice; hero.statistics.money += StarlightPrice; core.drawTip("获得星光奖励,星币+" + StarlightPrice, "BuffStarlight") if (core.hasItem("FlashlightB")) flags.star.level++ } if (core.hasItem("Dice8") && (damage % 8 === 0) && (damage !== 0)) { //8面骰 hero.money += 8 * flags.ratio hero.def += Math.floor(Math.sqrt(flags.ratio)) hero.statistics.money += 8 * flags.ratio core.drawTip("“8面”骰子触发," + core.getStatusLabel('money') + "+" + (8 * flags.ratio) + "," + core.getStatusLabel('def') + "+" + Math.floor(Math.sqrt(flags.ratio)), "Dice8") } core.drawTip("打败 " + core.getEnemyValue(enemy, "name", x, y), enemy.id); // 诅咒之剑 if (core.hasItem("CursedSword")) { flags.Cursed++ if (flags.Cursed === 10 || damage > 125 * flags.ratio) { flags.Cursed = 0 hero.atk += flags.ratio hero.def-- core.drawTip("激活诅咒之剑效果,攻击+" + flags.ratio + ",防御-1", "CursedSword") } } // 魔法秘典 if (core.hasItem("MagicTome")) { flags.Tome = (flags.Tome ?? 0) + 1 if (flags.Tome === 5) { flags.Tome = 0 core.drawFolds("Firecrackers2") core.drawTip("魔法秘典触发", "MagicTome") } } // 魔法学院图书馆规则 if (floorId.startsWith("MAL")) flags[enemy.family] += enemy.def // 加点事件 var point = core.getEnemyValue(enemy, "point", x, y) || 0; if (core.flags.enableAddPoint && point > 0) { core.push(todo, [{ "type": "insert", "name": "加点事件", "args": [point] }]); } // 战后事件 core.push(todo, enemy.afterBattle); if (floorId != null) { core.push(todo, core.floors[floorId].afterBattle[x + "," + y]); } //芬妮战后技能处理 if (hero.name === "芬妮") { for (let i = 0; i < (x ?? 13) * 10 + (y ?? 13) * 7 + core.rand(70); i++) core.rand() const rand = core.rand(100) if (rand < flags.Fenny && flags.Fenny > 29) { flags.Fenny = 0 if (todo.length > 0) core.insertAction(todo, x, y, () => { core.plugin.event.doEvent(core.plugin.event.eventList[core.randevent()]) }) else core.plugin.event.doEvent(core.plugin.event.eventList[core.randevent()]) } else { flags.Fenny++ if (todo.length > 0) core.insertAction(todo, x, y) } } else { // 如果事件不为空,将其插入 if (todo.length > 0) core.insertAction(todo, x, y); } // 删除该点设置的怪物信息 delete((flags.enemyOnPoint || {})[floorId] || {})[x + "," + y]; // 因为removeBlock和hideBlock都会刷新状态栏,因此将删除部分移动到这里并保证刷新只执行一次,以提升效率 if (core.getBlock(x, y) != null) { // 检查是否是重生怪物;如果是则仅隐藏不删除 if (core.hasSpecial(enemy.special, 23)) { core.hideBlock(x, y); } else { core.removeBlock(x, y); } } else { core.updateStatusBar(); } // 如果已有事件正在处理中 if (core.status.event.id == null) core.continueAutomaticRoute(); else core.clearContinueAutomaticRoute() } else { // 失败处理 core.status.hero.hp = 0; core.updateStatusBar(false, true); core.events.lose('战斗失败'); } if (todo.length === 0) core.unlockControl() callback?.(); } const battle = new BossBattle(); core.boss = battle; }, "龙宫气泡": function () { const bubbles = []; const smallBubbleClusters = []; const giantBubbles = []; let lastBubbleTime = 0; let lastClusterTime = 0; let lastUpdateTime = 0; const UPDATE_INTERVAL = 50; // 50ms更新一次 // 速度参数配置 const SPEED = { SMALL_CLUSTER: { MIN: -1.5, MAX: -2.4 }, // 小泡泡串 REGULAR: { MIN: -1.4, MAX: -2.2 }, // 普通泡泡 (比小泡泡慢) GIANT: { MIN: -1, MAX: -1.8 }, // 特大泡泡 (最慢) BURST: { MIN: -1.3, MAX: -2 } // 爆裂泡泡 (比普通泡泡慢) }; // 初始化小泡泡串 function initSmallBubbleClusters(timestamp) { if (smallBubbleClusters.length >= 4 || timestamp - lastClusterTime < 3000) { return; } lastClusterTime = timestamp; const clusterCount = Math.min(4 - smallBubbleClusters.length, 1 + Math.floor(Math.random() * 2)); for (let i = 0; i < clusterCount; i++) { smallBubbleClusters.push({ x: Math.random() * 416, y: 416 + Math.random() * 100, speedY: SPEED.SMALL_CLUSTER.MIN + Math.random() * (SPEED.SMALL_CLUSTER.MAX - SPEED.SMALL_CLUSTER.MIN), // 小泡泡速度基准 speedX: -0.1 + Math.random() * 0.2, sway: Math.random() * 0.3, drift: Math.random() * 0.1 - 0.05, bubbles: [], centerX: 0, centerY: 0, angle: 0, rotationSpeed: 0.01 + Math.random() * 0.02, ellipseWidth: 10 + Math.random() * 10, ellipseHeight: 5 + Math.random() * 10, startTime: timestamp }); const cluster = smallBubbleClusters[smallBubbleClusters.length - 1]; cluster.centerX = cluster.x; cluster.centerY = cluster.y; const bubbleCount = 5 + Math.floor(Math.random() * 4); const verticalSpread = 15 + Math.random() * 20; for (let j = 0; j < bubbleCount; j++) { cluster.bubbles.push({ angle: Math.random() * Math.PI * 2, distance: 0.5 + Math.random() * 0.5, size: 1 + Math.random() * 2, alpha: 0.5 + Math.random() * 0.3, life: 1, startTime: timestamp }); } } } // 初始化普通气泡(速度减慢) function initBubbles(timestamp) { if (bubbles.filter(v => !v.create).length >= 10 || timestamp - lastBubbleTime < 2000) { return; } lastBubbleTime = timestamp; const bubbleCount = Math.min(10 - bubbles.length, 1 + Math.floor(Math.random() * 3)); for (let i = 0; i < bubbleCount; i++) { bubbles.push({ x: Math.random() * 416, y: 416 + Math.random() * 100, size: 5 + Math.random() * 10, speedY: SPEED.REGULAR.MIN + Math.random() * (SPEED.REGULAR.MAX - SPEED.REGULAR.MIN), // 比小泡泡稍慢 speedX: -0.5 + Math.random() * 1.0, alpha: 0.2 + Math.random() * 0.3, sway: Math.random() * 0.4, drift: Math.random() * 0.6 - 0.3, life: 1, startTime: timestamp, baseX: Math.random() * 416, // 用于左右摆动 swingRange: 5 + Math.random() * 10, // 摆动幅度 swingSpeed: 0.001 + Math.random() * 0.002 // 摆动速度 }); } } // 更新普通气泡位置(增加左右摆动) function updateBubbles(timestamp) { for (let i = bubbles.length - 1; i >= 0; i--) { const bubble = bubbles[i]; // 计算新位置(带左右摆动) bubble.y = bubble.y + bubble.speedY; bubble.x = bubble.baseX + Math.sin(timestamp * bubble.swingSpeed) * bubble.swingRange; // 随机漂移 bubble.x += bubble.drift bubble.life -= 0.002 // 检查是否在顶部1/3区域 const inTopThird = bubble.y < 416 / 3; // 气泡消失条件 if (bubble.y < -bubble.size || bubble.x < -bubble.size || bubble.x > 416 + bubble.size || bubble.life <= 0 || (inTopThird && Math.random() < 0.01)) { if (!bubble.create && (bubble.y > 0 && bubble.y < 416 && bubble.x > 0 && bubble.x < 416)) { createBurst(bubble.x, bubble.y, bubble.size, bubble.alpha, timestamp); } bubbles.splice(i, 1); } } } // 更新小泡泡串位置(基于时间戳) function updateSmallBubbleClusters(timestamp) { for (let i = smallBubbleClusters.length - 1; i >= 0; i--) { const cluster = smallBubbleClusters[i]; const elapsed = timestamp - cluster.startTime; // 计算新位置 cluster.y = cluster.y + cluster.speedY; cluster.x += Math.sin(timestamp * 0.001 * cluster.sway) * cluster.speedX + cluster.drift; cluster.angle = cluster.rotationSpeed * (elapsed / 16); cluster.centerX = cluster.x; cluster.centerY = cluster.y; // 检查是否在顶部1/3区域 const inTopThird = cluster.y < 416 / 3; // 更新小泡泡 for (let j = cluster.bubbles.length - 1; j >= 0; j--) { const bubble = cluster.bubbles[j]; const bubbleElapsed = timestamp - bubble.startTime; // 在顶部区域消失 if (inTopThird && Math.random() < 0.02) { createBurst( cluster.centerX + Math.cos(bubble.angle + cluster.angle) * cluster.ellipseWidth * bubble.distance, cluster.centerY + Math.sin(bubble.angle + cluster.angle) * cluster.ellipseHeight * bubble.distance, bubble.size, bubble.alpha, timestamp ); cluster.bubbles.splice(j, 1); } } // 重置条件 if (cluster.y < -50 || cluster.x < -30 || cluster.x > 446 || cluster.bubbles.length === 0) { smallBubbleClusters.splice(i, 1); } } } // 优化后的爆裂效果(不再轻量化) function createBurst(x, y, baseSize, baseAlpha, timestamp) { const burstCount = 5 + Math.floor(Math.random() * 5); // 恢复完整爆裂效果 for (let i = 0; i < burstCount; i++) { const angle = Math.random() * Math.PI * 2; const distance = Math.random() * baseSize * 0.5; bubbles.push({ create: true, x: x + Math.cos(angle) * distance, y: y + Math.sin(angle) * distance, size: baseSize * 0.2 + Math.random() * baseSize * 0.2, speedY: SPEED.BURST.MIN + Math.random() * (SPEED.BURST.MAX - SPEED.BURST.MIN), // -0.2 ~ -0.4 speedX: -0.6 + Math.random() * 1.2, // 减少水平速度范围 alpha: baseAlpha * 0.7, sway: Math.random() * 0.4, drift: Math.random() * 0.2 - 0.1, life: 0.5 + Math.random() * 0.5, startTime: timestamp, baseX: x + Math.cos(angle) * distance, swingRange: 3 + Math.random() * 5, swingSpeed: 0.001 + Math.random() * 0.002 }); } } // 初始化特大泡泡(速度减慢) function initGiantBubbles(timestamp) { if (giantBubbles.length >= 2) return; if (Math.random() < 0.005) { const radius = 150 + Math.random() * 100; const isLeft = Math.random() < 0.5; const x = isLeft ? -radius + radius * 0.6 : 416 + radius - radius * 0.6; giantBubbles.push({ x: x, y: 416 + radius, size: radius, speedY: SPEED.GIANT.MIN + Math.random() * (SPEED.GIANT.MAX - SPEED.GIANT.MIN), // 最慢速度 alpha: 0.2 + Math.random() * 0.1, blur: 1 + Math.random() * 2, edgeWidth: 3 + Math.random() * 3, edgeAlpha: 0.7 + Math.random() * 0.3, isLeft: isLeft, edgeColor: `rgba(220, 240, 255, ${0.7 + Math.random() * 0.3})`, startTime: timestamp, baseX: x, // 用于左右摆动 swingRange: 10 + Math.random() * 20, // 更大摆动幅度 swingSpeed: 0.0005 + Math.random() * 0.001 // 更慢摆动速度 }); } } // 更新特大泡泡位置(增加左右摆动) function updateGiantBubbles(timestamp) { for (let i = giantBubbles.length - 1; i >= 0; i--) { const bubble = giantBubbles[i]; const elapsed = timestamp - bubble.startTime; // 计算新位置(带左右摆动) bubble.y = bubble.y + bubble.speedY; bubble.x = bubble.baseX + Math.sin(timestamp * bubble.swingSpeed) * bubble.swingRange; if (bubble.y < -bubble.size) { // 特大泡泡消失时也产生爆裂效果 createBurst( bubble.x, bubble.y + bubble.size, bubble.size * 0.3, bubble.alpha, timestamp ); giantBubbles.splice(i, 1); } } } // 绘制小泡泡串 function drawSmallBubbleClusters(ctx) { for (let i = 0; i < smallBubbleClusters.length; i++) { const cluster = smallBubbleClusters[i]; for (let j = 0; j < cluster.bubbles.length; j++) { const bubble = cluster.bubbles[j]; // 椭圆运动位置 const x = cluster.centerX + Math.cos(bubble.angle + cluster.angle) * cluster.ellipseWidth * bubble.distance; const y = cluster.centerY + Math.sin(bubble.angle + cluster.angle) * cluster.ellipseHeight * bubble.distance; ctx.save(); ctx.beginPath(); ctx.arc(x, y, bubble.size, 0, Math.PI * 2); const gradient = ctx.createRadialGradient(x, y, 0, x, y, bubble.size); gradient.addColorStop(0, `rgba(230, 245, 255, ${bubble.alpha * 0.9})`); gradient.addColorStop(0.7, `rgba(190, 225, 245, ${bubble.alpha * 0.7})`); gradient.addColorStop(1, `rgba(160, 210, 235, ${bubble.alpha * 0.4})`); ctx.fillStyle = gradient; ctx.fill(); ctx.beginPath(); ctx.arc(x, y, bubble.size, 0, Math.PI * 2); ctx.strokeStyle = `rgba(255, 255, 255, ${bubble.alpha * 0.9})`; ctx.lineWidth = 0.8; ctx.stroke(); ctx.restore(); } } } // 绘制普通气泡 function drawBubbles(ctx) { for (let i = 0; i < bubbles.length; i++) { const bubble = bubbles[i]; const currentAlpha = bubble.alpha * Math.min(1, bubble.life * 2); ctx.save(); const gradient = ctx.createRadialGradient(bubble.x, bubble.y, 0, bubble.x, bubble.y, bubble.size); gradient.addColorStop(0, `rgba(230, 245, 255, ${currentAlpha * 0.8})`); gradient.addColorStop(0.7, `rgba(190, 225, 245, ${currentAlpha * 0.6})`); gradient.addColorStop(1, `rgba(160, 210, 235, ${currentAlpha * 0.4})`); ctx.beginPath(); ctx.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2); ctx.fillStyle = gradient; ctx.fill(); ctx.beginPath(); ctx.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2); ctx.strokeStyle = `rgba(255, 255, 255, ${currentAlpha * 0.9})`; ctx.lineWidth = 1.5; ctx.stroke(); ctx.restore(); } } // 绘制特大泡泡 function drawGiantBubbles(ctx) { for (let i = 0; i < giantBubbles.length; i++) { const bubble = giantBubbles[i]; ctx.save(); ctx.filter = `blur(${bubble.blur}px)`; const gradient1 = ctx.createRadialGradient( bubble.x, bubble.y, bubble.size * 0.2, bubble.x, bubble.y, bubble.size ); gradient1.addColorStop(0, `rgba(200, 230, 250, ${bubble.alpha * 0.9})`); gradient1.addColorStop(0.5, `rgba(170, 210, 240, ${bubble.alpha * 0.7})`); gradient1.addColorStop(0.8, `rgba(140, 190, 230, ${bubble.alpha * 0.5})`); gradient1.addColorStop(1, `rgba(120, 180, 220, ${bubble.alpha * 0.4})`); ctx.beginPath(); ctx.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2); ctx.fillStyle = gradient1; ctx.fill(); for (let j = 0; j < 3; j++) { const edgeAlpha = bubble.edgeAlpha * (1 - j * 0.3); const edgeWidth = bubble.edgeWidth * (1 - j * 0.4); ctx.beginPath(); ctx.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2); ctx.strokeStyle = bubble.edgeColor; ctx.lineWidth = edgeWidth; ctx.stroke(); } ctx.restore(); } } // 光斑动画 const frameCount = 240; const frames = []; for (let i = 0; i < frameCount; i++) { frames.push(`02B_Caribbean_Caustics_Deep_FREE_SAMPLE_${String(i).padStart(4, '0')}.jpg`); } let current = 0; let frameCounter = 0; const frameDelay = 16; const imgSize = 512; const targetAlpha = 0.16; function animateCaustics(ctx) { const scale = Math.min(416 / imgSize, 416 / imgSize); const drawWidth = imgSize * scale; const drawHeight = imgSize * scale; const offsetX = (416 - drawWidth) / 2; const offsetY = (416 - drawHeight) / 2; frameCounter++; if (frameCounter >= frameDelay) { frameCounter = 0; current = (current + 1) % frameCount; } ctx.globalCompositeOperation = 'lighter'; ctx.globalAlpha = targetAlpha; const img = core.material.images.images[frames[current]]; if (img) { ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight); } ctx.globalAlpha = 1.0; } core.registerAnimationFrame('caustics', true, () => { if (flags.MapStatus?.Map !== "龙宫游乐园") return if (!core.dymCanvas.caustics) { core.dymCanvas.caustics = core.createCanvas('caustics', 0, 0, 416, 416, 68) } core.clearMap(core.dymCanvas.caustics) // 绘制 animateCaustics(core.dymCanvas.caustics); }); core.registerAnimationFrame('bubble', true, (timestamp) => { if (flags.MapStatus?.Map !== "龙宫游乐园") return // 控制更新频率 if (timestamp - lastUpdateTime < UPDATE_INTERVAL) return if (!core.dymCanvas.bubble) { core.dymCanvas.bubble = core.createCanvas('bubble', 0, 0, 416, 416, 68) } const ctx = core.dymCanvas.bubble core.clearMap(ctx) // 初始化新泡泡 initBubbles(timestamp); initSmallBubbleClusters(timestamp); initGiantBubbles(timestamp); // 更新位置 updateBubbles(timestamp); updateSmallBubbleClusters(timestamp); updateGiantBubbles(timestamp); lastUpdateTime = timestamp; // 绘制 drawGiantBubbles(ctx); drawBubbles(ctx); drawSmallBubbleClusters(ctx); }); }, "机关门绑定": function () { // 在此增加新插件 core.registerEvent("specialDoor", function (data) { let bool = true; let loc_arr = data.loc[0] instanceof Array ? data.loc : [data.loc] loc_arr.forEach(loc => { if (core.getBlockCls(loc[0], loc[1]) && core.getBlockCls(loc[0], loc[1]).startsWith("enemys")) { bool = false; } }); if (bool) { let doors = data.doors[0] instanceof Array ? data.doors : [data.doors] let action = [] doors.forEach(v => action.push({ "type": "openDoor", "loc": [v[0], v[1]], "async": true })) action.push({ "type": "waitAsync" }) core.insertAction(action) } core.doAction(); }); core.http("GET", "https://h5mota.com/backend/user/info.php", void 0, e => core.username = JSON.parse(e)?.username, e => console.warn(e)) }, "DragonPlaceChoose": function () { // 在此增加新插件 const choose = document.createElement("canvas"); //画布设置 choose.style.position = "absolute"; choose.style.zIndex = 300; choose.style.display = "none"; choose.id = "choose"; main.dom.gameGroup.insertAdjacentElement("afterend", choose); choose.style.top = "50%"; choose.style.left = "50%"; choose.style.transform = "translate(-50%,-50%)"; const ctx = choose.getContext("2d"); main.dom.choose = choose; choose.onclick = function (e) { try { e.preventDefault() const left = core.dom.gameGroup.offsetLeft; const top = core.dom.gameGroup.offsetTop; const px = Math.floor((e.clientX - left) / core.domStyle.scale), py = Math.floor((e.clientY - top) / core.domStyle.scale); let x, y; if (core.domStyle.isVertical) { //对竖屏进行坐标转换 x = py; y = 416 - px; } else { x = px; y = py; } core.ui.choose.onclick(x, y); } catch (ee) { main.log(ee); } }; const { Animation, sleep, linear, hyper, trigo } = core.plugin.animate class Choose { constructor() { this.moving = false this.select = "none" if (!main.replayChecking) { this.animate = new Animation() this.animate.register("aa", 0) this.animate.register("ss", 0) this.animate.register("mm", 0) this.animate.register("fy", 0) this.animate.register("fa", 1) this.animate.register("gx", 0) this.animate.register("gy", 0) this.animate.register("gb", 1) this.animate.register("picmx", 0) this.animate.register("picmy", 0) this.animate.register("picma", 1) this.animate.register("picsx", 0) this.animate.register("picsy", 0) this.animate.register("picsa", 1) this.animate.register("fontx", 0) this.animate.register("roundrectsourix", 0) this.animate.register("roundrectsouriy", 0) this.animate.register("roundrectsouriw", 0) this.animate.register("roundrectsourih", 0) this.animate.register("roundrectmamushix", 0) this.animate.register("roundrectmamushiw", 0) this.animate.register("roundrectmamushih", 0) this.animate.register("fsx", 0) this.animate.register("fsy", 0) this.animate.register("fmx", 0) this.animate.register("fmy", 0) this.animate.register("fontsize", 0) this.animate.register("GA", 0.3) } this.filter = block => block.event.cls.indexOf("enemy") === 0 } update() { choose.style.display = "block" if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(ctx, 416, 676) ctx.save() ctx.translate(416, 0) ctx.rotate(Math.PI / 2) } else core.maps._setHDCanvasSize(ctx, 676, 416) core.clearMap(ctx) if (this.select === "none") { ctx.fillStyle = "#7432FF" ctx.fillRect(0, 0, 676, 416) ctx.beginPath() ctx.moveTo(300, 0) ctx.lineTo(416, 416) ctx.lineTo(0, 416) ctx.lineTo(0, 0) ctx.lineTo(300, 0) ctx.closePath() ctx.fillStyle = "#FF793B" ctx.fill() ctx.globalAlpha = 0.3 core.drawImage(ctx, "Souri_background.png", -140, 28, 460.8, 385.2) core.drawImage(ctx, "Mamushi_background.png", 386, 0, 460.8, 493.6) ctx.globalAlpha = 1 core.drawImage(ctx, "Li.png", 8, 35, 124.7, 99) core.drawImage(ctx, "Jiao.png", 543.3, 35, 124.7, 99) core.drawImage(ctx, this.image("Mamushi.png"), 356, 92, 250.6, 324) core.drawImage(ctx, this.image("Souri.png"), 0, 0, 415.8, 419) ctx.textAlign = "center" core.fillBoldText(ctx, "你选择帮助", 319, 325, "#FFF", "#000", 3, "18px STHUPO") core.fillBoldText(ctx, "谁?", 395, 325, "#FFF", "#000", 3, "30px STHUPO") core.fillRoundRect(ctx, 70, 340, 160, 30, 15, "#EA2201") core.fillRoundRect(ctx, 446, 340, 160, 30, 15, "#0105E9") core.fillBoldText(ctx, "天川苍鲤", 150, 359, "#FFF", "#000", 1, "16px STHUPO") core.fillBoldText(ctx, "天川真梦梓", 526, 359, "#FFF", "#000", 1, "16px STHUPO") } else if (this.select === "Souri") { ctx.fillStyle = "#7432FF" ctx.fillRect(0, 0, 676, 416) ctx.beginPath() ctx.moveTo(460, 0) ctx.lineTo(576, 416) ctx.lineTo(0, 416) ctx.lineTo(0, 0) ctx.lineTo(460, 0) ctx.closePath() ctx.fillStyle = "#FF793B" ctx.fill() ctx.globalAlpha = 0.3 core.drawImage(ctx, "Souri_background.png", -100, 28, 460.8, 385.2) ctx.globalAlpha = 1 core.drawImage(ctx, "Li.png", 8, 35, 187, 149.5) core.drawImage(ctx, "Jiao.png", 543.3, 8, 124.7, 99) core.drawImage(ctx, this.image("Mamushi.png"), 516, 173, 188, 243) core.drawImage(ctx, "Souri.png", 25, 0, 577.5, 581.5) ctx.textAlign = "center" core.fillBoldText(ctx, "你选择帮助", 359, 325, "#FFF", "#000", 3, "18px STHUPO") core.fillBoldText(ctx, "天川苍鲤", 465, 325, "#FFF", "#000", 3, "30px STHUPO") core.fillRoundRect(ctx, 70, 340, 200, 38, 19, "#EA2201") core.fillRoundRect(ctx, 556, 340, 80, 20, 10, "#0105E9") core.strokeRoundRect(ctx, 70, 340, 200, 38, 19, "#63E900", 4) core.fillBoldText(ctx, "天川苍鲤", 170, 365, "#FFF", "#000", 1, "20px STHUPO") core.fillBoldText(ctx, "天川真梦梓", 596, 354, "#FFF", "#000", 1, "12px STHUPO") core.fillRoundRect(ctx, 362, 335, 120, 30, 15, "#FF1796") core.strokeRoundRect(ctx, 362, 335, 120, 30, 15, "#000", 3) core.fillBoldText(ctx, "确 定", 422, 354, "#000", "#FFF", 1, "16px STHUPO") core.fillRoundRect(ctx, 6, 175, 175, 70, 8, "rgba(0,0,0,0.3)") core.drawTextContent(ctx, `将挑战\r[#7638F8]天川真梦梓\r 出牌不消耗\\i[cost]而是\\i[hp] 疯狂难度怪物特殊属性-牌切: 手牌<5张时每少1张攻防提升20%`, { left: 8, top: 177, color: "#FFFFFF", align: "left", maxWidth: 173, fontSize: 12, font: "cjk" }) } else { ctx.fillStyle = "#7432FF" ctx.fillRect(0, 0, 676, 416) ctx.beginPath() ctx.moveTo(130, 0) ctx.lineTo(246, 416) ctx.lineTo(0, 416) ctx.lineTo(0, 0) ctx.lineTo(130, 0) ctx.closePath() ctx.fillStyle = "#FF793B" ctx.fill() ctx.globalAlpha = 0.5 core.drawImage(ctx, "Mamushi_background.png", 178, 0, 460.8, 493.6) ctx.globalAlpha = 1 core.drawImage(ctx, "Li.png", 8, 8, 124.7, 99) core.drawImage(ctx, "Jiao.png", 487, 35, 187, 149.5) core.drawImage(ctx, this.image("Souri.png"), -40, 156.2, 288.8, 290.8) core.drawImage(ctx, "Mamushi.png", 200, 12, 347.5, 450) ctx.textAlign = "center" core.fillBoldText(ctx, "你选择帮助", 189, 325, "#FFF", "#000", 3, "18px STHUPO") core.fillBoldText(ctx, "天川真梦梓", 310, 325, "#FFF", "#000", 3, "30px STHUPO") core.fillRoundRect(ctx, 60, 358, 80, 20, 10, "#EA2201") core.fillRoundRect(ctx, 386, 340, 200, 38, 19, "#0105E9") core.strokeRoundRect(ctx, 386, 340, 200, 38, 19, "#63E900", 4) core.fillBoldText(ctx, "天川苍鲤", 100, 372, "#FFF", "#000", 1, "12px STHUPO") core.fillBoldText(ctx, "天川真梦梓", 486, 365, "#FFF", "#000", 1, "20px STHUPO") core.fillRoundRect(ctx, 220, 335, 120, 30, 15, "#FF1796") core.strokeRoundRect(ctx, 220, 335, 120, 30, 15, "#000", 3) core.fillBoldText(ctx, "确 定", 280, 354, "#000", "#FFF", 1, "16px STHUPO") core.fillRoundRect(ctx, 495, 175, 175, 70, 8, "rgba(0,0,0,0.3)") core.drawTextContent(ctx, `将挑战\r[#F97B42]天川苍鲤\r 拾取宝石血瓶后额外获得±2倍属性 疯狂难度怪物特殊属性-虚影: 战斗回合数<=3时变为7`, { left: 497, top: 177, color: "#FFFFFF", align: "left", maxWidth: 173, fontSize: 12, font: "cjk" }) } ctx.restore() } image(file) { const photo = document.createElement("canvas") file = core.getMappedName(file) file = core.material.images.images[file] photo.width = file.width photo.height = file.height const photoctx = photo.getContext("2d") photoctx.filter = "grayscale(1)" core.drawImage(photoctx, file, 0, 0, file.width, file.height) photoctx.globalCompositeOperation = "source-atop" core.fillRect(photoctx, 0, 0, file.width, file.height, "rgba(0,0,0,0.7)") return photo } init() { this.select = "none" this.animate.time(1).absolute().mode(linear()) .apply("aa", 0) .apply("ss", 0) .apply("mm", 0) .apply("fy", 0) .apply("fa", 1) .apply("gx", 0) .apply("gy", 0) .apply("gb", 1) .apply("picmx", 0) .apply("picmy", 0) .apply("picma", 1) .apply("picsx", 0) .apply("picsy", 0) .apply("picsa", 1) .apply("fontx", 0) .apply("roundrectsourix", 0) .apply("roundrectsouriy", 0) .apply("roundrectsouriw", 0) .apply("roundrectsourih", 0) .apply("roundrectmamushix", 0) .apply("roundrectmamushiw", 0) .apply("roundrectmamushih", 0) .apply("fsx", 0) .apply("fsy", 0) .apply("fmx", 0) .apply("fmy", 0) .apply("fontsize", 0) .apply("GA", 0.3) this.update() } souri() { flags.MapStatus.Choose = "Souri" flags.出牌计数器 = 0 core.removeBlock(7, 6, "DP8") const FID = [...core.floorIds] FID.pop() const enemys = core.maps.searchBlockWithFilter(this.filter, FID) switch (flags.MapStatus?.Hard) { case "噩梦": const special1 = [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 29, void 0, 27, void 0] enemys.forEach(v => { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length) const a = special1[rand] const b = [...special] if (a) b.push(a) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) break case "疯狂": const special201 = [1, 2, void 0, 3, 4, void 0, 5, 7, void 0, 8, 10, void 0, 11, 15, void 0, 22, 29, void 0, 27, 34, void 0] const special202 = [1, void 0, void 0, void 0, void 0, 2, void 0, void 0, void 0, void 0, 3, void 0, void 0, void 0, void 0, 4, void 0, void 0, void 0, void 0, 5, void 0, void 0, void 0, void 0, 7, void 0, void 0, void 0, void 0, 8, void 0, void 0, void 0, void 0, 10, void 0, void 0, void 0, void 0, 11, void 0, void 0, void 0, void 0, 15, void 0, void 0, void 0, void 0, 22, void 0, void 0, void 0, void 0, 29, void 0, void 0, void 0, void 0, 27, void 0, void 0, void 0, void 0, 34, void 0, void 0, void 0, void 0] enemys.forEach(v => { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand1 = core.rand(special201.length) const rand2 = core.rand(special202.length) const a = special201[rand1] const c = special201[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) } core.setMapBgm("maps5B2.mp3") core.playBgm("maps5B2.mp3") core.insertAction([ { "type": "setCurtain", "color": [143, 109, 141, 0.2], "time": 1, "keep": true }, { "type": "show", "loc": [ [12, 6] ], "floorID": "DP3" } ]) choose.style.display = "none" core.doAction() } mamushi() { flags.MapStatus.Choose = "Mamushi" core.removeBlock(5, 6, "DP8") const FID = [...core.floorIds] FID.pop() const enemys = core.maps.searchBlockWithFilter(this.filter, FID) switch (flags.MapStatus?.Hard) { case "噩梦": const special1 = [1, void 0, 2, void 0, 3, void 0, 4, void 0, 5, void 0, 7, void 0, 8, void 0, 10, void 0, 11, void 0, 15, void 0, 22, void 0, 29, void 0, 27, void 0] enemys.forEach(v => { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand = core.rand(special1.length) const a = special1[rand] const b = [...special] if (a) b.push(a) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) break case "疯狂": const special201 = [1, 2, void 0, 3, 4, void 0, 5, 7, void 0, 8, 10, void 0, 11, 15, void 0, 22, 29, void 0, 27, 33, void 0] const special202 = [1, void 0, void 0, void 0, void 0, 2, void 0, void 0, void 0, void 0, 3, void 0, void 0, void 0, void 0, 4, void 0, void 0, void 0, void 0, 5, void 0, void 0, void 0, void 0, 7, void 0, void 0, void 0, void 0, 8, void 0, void 0, void 0, void 0, 10, void 0, void 0, void 0, void 0, 11, void 0, void 0, void 0, void 0, 15, void 0, void 0, void 0, void 0, 22, void 0, void 0, void 0, void 0, 29, void 0, void 0, void 0, void 0, 27, void 0, void 0, void 0, void 0, 33, void 0, void 0, void 0, void 0] enemys.forEach(v => { const special = core.getEnemyValue(void 0, "special", v.x, v.y, v.floorId) const rand1 = core.rand(special201.length) const rand2 = core.rand(special202.length) const a = special201[rand1] const c = special201[rand2] const b = [...special] if (a) b.push(a) if (c) b.push(c) core.setEnemyOnPoint(v.x, v.y, v.floorId, "special", b, "=", void 0, true) }) } core.setMapBgm("maps5B1.mp3") core.playBgm("maps5B1.mp3") core.insertAction([{ "type": "setCurtain", "color": [254, 189, 141, 0.2], "time": 1, "keep": true }, { "type": "show", "loc": [ [0, 6] ], "floorID": "DP3" }]) choose.style.display = "none" core.doAction() } async animateSouri() { this.moving = true const fn = () => { if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(ctx, 416, 676) ctx.save() ctx.translate(416, 0) ctx.rotate(Math.PI / 2) } else core.maps._setHDCanvasSize(ctx, 676, 416) core.clearMap(ctx) ctx.fillStyle = "#7432FF" ctx.fillRect(0, 0, 676, 416) ctx.beginPath() ctx.moveTo(300 + this.animate.value.aa, 0) ctx.lineTo(416 + this.animate.value.aa, 416) ctx.lineTo(0, 416) ctx.lineTo(0, 0) ctx.lineTo(300 + this.animate.value.aa, 0) ctx.closePath() ctx.fillStyle = "#FF793B" ctx.fill() ctx.globalAlpha = this.animate.value.GA core.drawImage(ctx, "Souri_background.png", -140 + this.animate.value.ss, 28, 460.8, 385.2) core.drawImage(ctx, "Mamushi_background.png", 386 - this.animate.value.mm, 0, 460.8, 493.6) ctx.globalAlpha = 1 core.drawImage(ctx, "Li.png", 8, 35 + this.animate.value.fy, 124.7 * this.animate.value.fa, 99 * this.animate.value.fa) core.drawImage(ctx, "Jiao.png", 543.3 + this.animate.value.gx, 35 + this.animate.value.gy, 124.7 * this.animate.value.gb, 99 * this.animate.value.gb) core.drawImage(ctx, this.image("Mamushi.png"), 356 + this.animate.value.picmx, 92 + this.animate.value.picmy, 250.6 * this.animate.value.picma, 324 * this.animate.value.picma) core.drawImage(ctx, "Souri.png", this.animate.value.picsx, this.animate.value.picsy, 415.8 * this.animate.value.picsa, 419 * this.animate.value.picsa) ctx.textAlign = "center" core.fillBoldText(ctx, "你选择帮助", 319 + this.animate.value.fontx, 325, "#FFF", "#000", 3, "18px STHUPO") core.fillBoldText(ctx, "天川苍鲤", 425 + this.animate.value.fontx, 325, "#FFF", "#000", 3, "30px STHUPO") core.fillRoundRect(ctx, 70 + this.animate.value.roundrectsourix, 340 + this.animate.value.roundrectsouriy, 160 + this.animate.value.roundrectsouriw, 30 + this.animate.value.roundrectsourih, 15 + this.animate.value.roundrectsourih / 2, "#EA2201") core.fillRoundRect(ctx, 446 + this.animate.value.roundrectmamushix, 340, 160 + this.animate.value.roundrectmamushiw, 30 + this.animate.value.roundrectmamushih, 15 + this.animate.value.roundrectmamushih / 2, "#0105E9") core.fillBoldText(ctx, "天川苍鲤", 150 + this.animate.value.fsx, 359 + this.animate.value.fsy, "#FFF", "#000", 1, `${16+this.animate.value.fontsize}px STHUPO`) core.fillBoldText(ctx, "天川真梦梓", 526 + this.animate.value.fmx, 359 + this.animate.value.fmy, "#FFF", "#000", 1, `${16-this.animate.value.fontsize}px STHUPO`) } this.animate.ticker.add(fn) this.animate.time(750).absolute().mode(hyper("sin", "in-out")) this.animate.apply("aa", 160) this.animate.apply("ss", 40) this.animate.apply("mm", -290) this.animate.apply("fy", 0) this.animate.apply("fa", 1.5) this.animate.apply("gx", 0) this.animate.apply("gy", -27) this.animate.apply("gb", 1) this.animate.apply("picmx", 160) this.animate.apply("picmy", 81) this.animate.apply("picma", 0.75) this.animate.apply("picsx", 25) this.animate.apply("picsy", 0) this.animate.apply("picsa", 1.5 / 1.08) this.animate.apply("fontx", 40) this.animate.apply("roundrectsourix", 0) this.animate.apply("roundrectsouriy", 0) this.animate.apply("roundrectsourih", 8) this.animate.apply("roundrectmamushix", 110) this.animate.apply("roundrectmamushih", -10) this.animate.apply("roundrectsouriw", 40) this.animate.apply("roundrectmamushiw", -80) this.animate.apply("fsx", 20) this.animate.apply("fsy", 6) this.animate.apply("fmx", 70) this.animate.apply("fmy", -5) this.animate.apply("fontsize", 4) this.animate.apply("GA", 0.3) await this.animate.all() this.animate.ticker.clear() this.select = "Souri" //这个地方之后移到点击部分 this.moving = false this.update() } async animateMamushi() { this.moving = true const fn = () => { if (core.domStyle.isVertical) { core.maps._setHDCanvasSize(ctx, 416, 676) ctx.save() ctx.translate(416, 0) ctx.rotate(Math.PI / 2) } else core.maps._setHDCanvasSize(ctx, 676, 416) core.clearMap(ctx) ctx.fillStyle = "#7432FF" ctx.fillRect(0, 0, 676, 416) ctx.beginPath() ctx.moveTo(300 + this.animate.value.aa, 0) ctx.lineTo(416 + this.animate.value.aa, 416) ctx.lineTo(0, 416) ctx.lineTo(0, 0) ctx.lineTo(300 + this.animate.value.aa, 0) ctx.closePath() ctx.fillStyle = "#FF793B" ctx.fill() ctx.globalAlpha = this.animate.value.GA core.drawImage(ctx, "Souri_background.png", -140 + this.animate.value.ss, 28, 460.8, 385.2) core.drawImage(ctx, "Mamushi_background.png", 386 - this.animate.value.mm, 0, 460.8, 493.6) ctx.globalAlpha = 1 core.drawImage(ctx, "Li.png", 8, 35 + this.animate.value.fy, 124.7 * this.animate.value.fa, 99 * this.animate.value.fa) core.drawImage(ctx, "Jiao.png", 543.3 + this.animate.value.gx, 35 + this.animate.value.gy, 124.7 * this.animate.value.gb, 99 * this.animate.value.gb) core.drawImage(ctx, this.image("Souri.png"), this.animate.value.picsx, this.animate.value.picsy, 415.8 * this.animate.value.picsa, 419 * this.animate.value.picsa) core.drawImage(ctx, "Mamushi.png", 356 + this.animate.value.picmx, 92 + this.animate.value.picmy, 250.6 * this.animate.value.picma, 324 * this.animate.value.picma) ctx.textAlign = "center" core.fillBoldText(ctx, "你选择帮助", 319 + this.animate.value.fontx, 325, "#FFF", "#000", 3, "18px STHUPO") core.fillBoldText(ctx, "天川真梦梓", 440 + this.animate.value.fontx, 325, "#FFF", "#000", 3, "30px STHUPO") core.fillRoundRect(ctx, 70 + this.animate.value.roundrectsourix, 340 + this.animate.value.roundrectsouriy, 160 + this.animate.value.roundrectsouriw, 30 + this.animate.value.roundrectsourih, 15 + this.animate.value.roundrectsourih / 2, "#EA2201") core.fillRoundRect(ctx, 446 + this.animate.value.roundrectmamushix, 340, 160 + this.animate.value.roundrectmamushiw, 30 + this.animate.value.roundrectmamushih, 15 + this.animate.value.roundrectmamushih / 2, "#0105E9") core.fillBoldText(ctx, "天川苍鲤", 150 + this.animate.value.fsx, 359 + this.animate.value.fsy, "#FFF", "#000", 1, `${16+this.animate.value.fontsize}px STHUPO`) core.fillBoldText(ctx, "天川真梦梓", 526 + this.animate.value.fmx, 359 + this.animate.value.fmy, "#FFF", "#000", 1, `${16-this.animate.value.fontsize}px STHUPO`) } this.animate.ticker.add(fn) this.animate.time(750).absolute().mode(hyper("sin", "in-out")) this.animate.apply("aa", -170) this.animate.apply("ss", -321) this.animate.apply("mm", 208) this.animate.apply("fy", -27) this.animate.apply("fa", 1) this.animate.apply("gx", -56.3) this.animate.apply("gy", 0) this.animate.apply("gb", 1.5) this.animate.apply("picmx", -156) this.animate.apply("picmy", -80) this.animate.apply("picma", 1.5 / 1.08) this.animate.apply("picsx", -40) this.animate.apply("picsy", 156.2) this.animate.apply("picsa", 0.695) this.animate.apply("fontx", -130) this.animate.apply("roundrectsourix", -10) this.animate.apply("roundrectsouriy", 18) this.animate.apply("roundrectsourih", -10) this.animate.apply("roundrectmamushix", -60) this.animate.apply("roundrectmamushih", 8) this.animate.apply("roundrectsouriw", -80) this.animate.apply("roundrectmamushiw", 40) this.animate.apply("fsx", -50) this.animate.apply("fsy", 13) this.animate.apply("fmx", -40) this.animate.apply("fmy", 6) this.animate.apply("fontsize", -4) this.animate.apply("GA", 0.5) await this.animate.all() this.animate.ticker.clear() this.select = "Mamushi" //这个地方之后移到点击部分 this.moving = false this.update() } onclick(x, y) { if (this.moving) return if (this.select === "none") { if (x < 300 + 116 / 416 * y) this.animateSouri() else this.animateMamushi() } else if (this.select === "Souri") { if ((377 <= x && x <= 467 && 335 <= y && y <= 365) || ((x - 377) ** 2 + (y - 350) ** 2 <= 225 && x <= 377) || ((x - 437) ** 2 + (y - 350) ** 2 <= 225 && x >= 467)) { this.souri() core.status.route.push("choices:0") } else if (x > 460 + 116 / 416 * y) this.animateMamushi() } else { if ((235 <= x && x <= 325 && 335 <= y && y <= 365) || ((x - 235) ** 2 + (y - 350) ** 2 <= 225 && x <= 235) || ((x - 325) ** 2 + (y - 350) ** 2 <= 225 && x >= 325)) { this.mamushi() core.status.route.push("choices:1") } else if (x < 130 + 116 / 416 * y) this.animateSouri() } } } core.ui.choose = new Choose() }, "公告": function () { // 在此增加新插件 if (main.replayChecking) return const style = document.createElement('style'); style.textContent = ` #updateLogBtn { position: absolute; top: 3%; left: 2%; width: 15%; height: 10%; background: white; border: 4px solid black; border-radius: 27.5px; color: black; font-size: ${parseFloat(core.dom.gameGroup.style.width)*0.04}px; font-weight: bold; cursor: pointer; z-index: 1001; transition: all 0.3s ease; display: none; align-items: center; justify-content: center; box-shadow: 0 0 0 3px white, 0 2px 8px rgba(0, 0, 0, 0.2); outline: none; margin: 3px; } #updateLogBtn:hover { background: #f5f5f5; transform: translateY(-1px); box-shadow: 0 0 0 3px white, 0 4px 12px rgba(0, 0, 0, 0.3); } #updateLogBtn:active { transform: translateY(0); } /* 隐藏状态 */ #updateLogBtn.hidden { opacity: 0; transform: translateY(-10px); pointer-events: none; transition: all 0.5s ease; } /* 显示动画 */ #updateLogBtn:not(.hidden) { animation: buttonAppear 0.6s ease-out; } @keyframes buttonAppear { 0% { opacity: 0; transform: translateY(-10px) scale(0.9); } 60% { opacity: 1; transform: translateY(2px) scale(1.05); } 100% { opacity: 1; transform: translateY(0) scale(1); } } #updateLogModal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 2000; display: none; justify-content: center; align-items: center; } #updateLogWindow { background: white; border-radius: 0; overflow: hidden; box-shadow: 0 0 30px rgba(0, 0, 0, 0.5); position: relative; display: flex; flex-direction: column; } .isVertical #updateLogWindow { width: min(95vw, 90vh * 416 / 676); height: min(95vh, 95vw * 676 / 416); } body:not(.isVertical) #updateLogWindow { width: min(95vw, 95vh * 676 / 416); height: min(95vh, 95vw * 416 / 676); } #updateLogHeader { background: #1a1a1a; color: white; padding: 15px 20px; font-size: 18px; font-weight: bold; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #333; } #updateLogTitle { color: white; font-size: 30px; font-weight: bold; margin: 0; } /* 右上角超大超粗的叉号 */ #updateLogClose { background: none; border: none; color: white; font-size: 60px; font-weight: 900; cursor: pointer; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; border-radius: 3px; transition: background 0.2s ease; line-height: 1; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); } #updateLogClose:hover { background: rgba(255, 255, 255, 0.1); text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); } .main-container { flex: 1; display: flex; overflow: hidden; } #updateLogTabs { background: #ff00b4; color: black; overflow-y: auto; border-right: 1px solid #ff00b4; display: flex; flex-direction: column; } .isVertical .main-container { flex-direction: column; } .isVertical #updateLogTabs { width: 100%; max-height: 200px; flex-direction: row; flex-wrap: wrap; } body:not(.isVertical) .main-container { flex-direction: row; } body:not(.isVertical) #updateLogTabs { width: 250px; flex-direction: column; } .tab-button { padding: 12px 15px; border: none; border-bottom: 1px solid #c0c0c0; background: transparent; color: black; cursor: pointer; transition: background 0.2s ease; font-size: 16px; line-height: 1.4; text-align: left; font-weight: bold; } .tab-button:hover { background: rgba(255, 255, 255, 0.1); } /* 选中后变成和文字区域背景色一样的颜色 */ .tab-button.active { background: #f5f1eb; color: black; } .tab-button:last-child { border-bottom: none; } /* 右侧内容区域 - 淡棕色底色 */ #updateLogContent { flex: 1; background: #f5f1eb; overflow-y: auto; padding: 20px; position: relative; } /* 自定义滚动条 */ #updateLogContent::-webkit-scrollbar { width: 8px; } #updateLogContent::-webkit-scrollbar-track { background: #e8e8e8; } #updateLogContent::-webkit-scrollbar-thumb { background: #c0c0c0; border-radius: 4px; } #updateLogContent::-webkit-scrollbar-thumb:hover { background: #a0a0a0; } /* 内容样式 - 统一使用Microsoft YaHei字体 */ .content-title { font-size: 24px; font-weight: bold; color: #333; margin-bottom: 15px; text-align: center; } .content-subtitle { font-size: 16px; color: #666; margin-bottom: 20px; text-align: center; font-style: italic; } .content-section { margin-bottom: 20px; } .section-title { font-size: 18px; font-weight: bold; color: #333; margin-bottom: 10px; padding-bottom: 5px; border-bottom: 1px solid #ddd; } .section-content { color: #555; line-height: 1.6; font-size: 16px; margin-bottom: 15px; } .section-content ul { padding-left: 20px; margin: 0; } .section-content li { margin-bottom: 5px; } .section-content p { margin: 0 0 10px 0; } .content-image { width: 100%; max-width: 100%; height: auto; margin: 15px 0; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } @media (max-width: 400px) or (max-height: 400px) { #updateLogWindow { width: 98vw !important; height: 98vh !important; } #updateLogTabs { font-size: 14px; } #updateLogClose { font-size: 36px; width: 45px; height: 45px; } } #gameGroup { position: relative; } /* 竖屏适配 */ .isVertical #updateLogBtn { top: 12px; right: 12px; left: auto; width: 110px; height: 50px; font-size: 30px; border-radius: 25px; } /* 鼠标拖拽滚动支持 */ #updateLogContent { scroll-behavior: smooth; } .isVertical #updateLogTabs { overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; scrollbar-color: #ff00b4 #f5f1eb; -webkit-overflow-scrolling: touch; scroll-snap-type: x mandatory; display: flex; flex-wrap: nowrap; } /* 滚动条样式 */ .isVertical #updateLogTabs::-webkit-scrollbar { height: 6px; } .isVertical #updateLogTabs::-webkit-scrollbar-track { background: #f5f1eb; border-radius: 3px; } .isVertical #updateLogTabs::-webkit-scrollbar-thumb { background-color: #ff00b4; border-radius: 3px; } /* 标签按钮在竖屏下的样式 */ .isVertical .tab-button { flex: 0 0 auto; width: auto; min-width: 120px; scroll-snap-align: start; white-space: nowrap; border-bottom: none; border-right: 1px solid #c0c0c0; } /* 进度条容器 */ #scrollProgressContainer { display: none; width: 100%; height: 4px; background: #f5f1eb; position: relative; } .isVertical #scrollProgressContainer { display: block; } /* 进度条 */ #scrollProgressBar { height: 100%; background: #ff00b4; width: 0; transition: width 0.2s ease; } `; // 将样式添加到head document.head.appendChild(style); // 创建公告按钮 const updateLogBtn = document.createElement('button'); updateLogBtn.id = 'updateLogBtn'; updateLogBtn.innerHTML = '公告'; core.dom.startPanel.appendChild(updateLogBtn); // 创建公告弹窗 const updateLogModal = document.createElement('div'); updateLogModal.id = 'updateLogModal'; const updateLogWindow = document.createElement('div'); updateLogWindow.id = 'updateLogWindow'; // 创建顶部标题栏 const updateLogHeader = document.createElement('div'); updateLogHeader.id = 'updateLogHeader'; const updateLogTitle = document.createElement('h3'); updateLogTitle.id = 'updateLogTitle'; updateLogTitle.textContent = '公告'; const updateLogClose = document.createElement('button'); updateLogClose.id = 'updateLogClose'; updateLogClose.textContent = '×'; updateLogHeader.appendChild(updateLogTitle); updateLogHeader.appendChild(updateLogClose); updateLogWindow.appendChild(updateLogHeader); // 创建主要内容区域 const mainContainer = document.createElement('div'); mainContainer.className = 'main-container'; // 创建左侧更新列表 const updateLogTabs = document.createElement('div'); updateLogTabs.id = 'updateLogTabs'; // 创建标签按钮 const tabButton1 = document.createElement('button'); tabButton1.className = 'tab-button'; tabButton1.dataset.tab = 'updatesecond'; tabButton1.textContent = '2025.06.23更新'; const tabButton2 = document.createElement('button'); tabButton2.className = 'tab-button'; tabButton2.dataset.tab = 'changelog'; tabButton2.textContent = '2025.06.21第一次平衡性调整'; const tabButton3 = document.createElement('button'); tabButton3.className = 'tab-button active'; tabButton3.dataset.tab = 'ver211'; tabButton3.textContent = '2025.09.04版本更新'; updateLogTabs.appendChild(tabButton3); updateLogTabs.appendChild(tabButton1); updateLogTabs.appendChild(tabButton2); mainContainer.appendChild(updateLogTabs); // 创建右侧内容区域 const updateLogContent = document.createElement('div'); updateLogContent.id = 'updateLogContent'; // 初始化内容 updateContent('ver211'); mainContainer.appendChild(updateLogContent); updateLogWindow.appendChild(mainContainer); updateLogModal.appendChild(updateLogWindow); document.body.appendChild(updateLogModal); // 添加事件监听器 updateLogBtn.addEventListener('click', () => { checkOrientation(); if (typeof core !== 'undefined') { core.setLocalStorage("_version", core.version); } updateLogModal.style.display = 'flex'; document.body.style.overflow = 'hidden'; }); // 关闭弹窗 function closeModal() { updateLogModal.style.display = 'none'; document.body.style.overflow = 'auto'; } updateLogClose.addEventListener('click', closeModal); // 点击背景关闭弹窗 updateLogModal.addEventListener('click', function (e) { if (e.target === updateLogModal) { closeModal(); } }); // ESC键关闭弹窗 document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && updateLogModal.style.display === 'flex') { closeModal(); } }); // 标签页切换 const tabButtons = document.querySelectorAll('.tab-button'); tabButtons.forEach(button => { button.addEventListener('click', function () { // 移除所有活动状态 tabButtons.forEach(btn => btn.classList.remove('active')); // 激活当前标签 this.classList.add('active'); // 更新内容 updateContent(this.getAttribute('data-tab')); }); }); // 更新右侧内容 function updateContent(type) { const contentData = { ver211: { title: '2025.09.04版本更新', sections: [{ title: '■ 新地图:龙宫游乐园', content: '

传说在龙宫有秘宝可以帮助少女化为真龙,秘宝只有一个,却有两名少女需要它!怎么办,只能二选其一了呀!
在本地图中,你需要击败大嗓门和练家子后,选择帮助其中一位少女
至于另一位嘛……做好准备迎接战斗
恋的温馨提醒:这只是一场模拟游戏,请玩家切勿担心,至于剧情我们还在施工,敬请期待

' }, { title: '■ 新角色:照 三神御主', content: '

“三神教”现任的教主,不拘泥于非要穿着传统服饰以彰显自己的“教主”地位,相反她很喜欢穿一些很符合她身型的衣服

CD40,对前方可视距离你最近的怪物造成你原始攻击力的伤害。
可储存使用,初始次数为1,上限为3。技能完成冷却后次数+1并重新进入冷却

  • 被动技能
  • 出牌后消耗的费用>=2时,提升10%主动技能造成的伤害

    ' }, { title: '■ 新筹码:放大镜&狐狸面具', content: '' }, { title: '■ BOSS战全面革新', content: '

    现在你可以真正的在BOSS战打牌了!

    BOSS战中仅能使用部分卡牌,且大部分的筹码和技能无效
    当然,你可以在BOSS战中抽牌,且每回合你的cost都会补满

    为应对新BOSS战平衡,我们加强了嘎呜家族

    ' }, { title: '■ 角色养成LV6登场', content: '

    你还在为同时出现3个好筹码选什么而纠结吗?现在开始,告别选择,我全都要!

    角色等级达到lv6后,可在挑选筹码时拥有1次全部选择的机会(手快全拿)
    不能对开局的精选筹码使用此功能

    ' }, { title: '■ 全新排行榜功能', content: '

    在主界面的右上角可点击排行榜查看某个角色/某张地图/某个难度下的排行榜

    ' }, { title: '■ 平衡性调整', content: '' }, { title: '■ BUG修复', content: '

    修复了等级经验错误的问题,原3升4仅需2500经验,现已修正为3000
    修复了反馈BUG功能看不清字的问题
    还有更多的BUG有修的但我们忘记了

    ' }, { title: '■ LAST', content: '

    只要我们仍然保持着对游戏的热爱就会继续更新下去的,下次大版本更新:2026.1.1

    ' }, ] }, updatesecond: { title: '2025.06.23更新', sections: [{ title: '■ 更新内容', content: '' }, { title: '■ 目前已知问题', content: '' } ] }, changelog: { title: '2025.06.21第一次平衡性调整', sections: [{ title: '■ 战斗筹码调整', content: '' }, { title: '■ 角色调整', content: '' }, { title: '■ 卡牌调整', content: '', }, { title: '■ 地图机制调整', content: '' }, { title: '■ 其他', content: '' } ] } }; const data = contentData[type] || contentData.changelog; let sectionsHTML = ''; data.sections.forEach(section => { sectionsHTML += `
    ${section.title}
    ${section.content}
    `; }); updateLogContent.innerHTML = `
    ${data.title}
    ${sectionsHTML} `; } function checkOrientation() { let updateLogBtn = document.getElementById('updateLogBtn') updateLogBtn.style.fontSize = `${parseFloat(core.dom.gameGroup.style.width)*0.04}px` if (typeof core !== 'undefined' && core.domStyle && core.domStyle.isVertical !== undefined) { if (core.domStyle.isVertical) { document.body.classList.add('isVertical'); updateLogBtn.style.top = `3%`; updateLogBtn.style.left = `2%`; updateLogBtn.style.width = `20%`; updateLogBtn.style.height = `6%`; updateLogBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.03}px` updateLogBtn.style.border = `${4*core.domStyle.scale}px solid black` } else { document.body.classList.remove('isVertical'); updateLogBtn.style.top = `3%`; updateLogBtn.style.left = `2%`; updateLogBtn.style.width = `15%`; updateLogBtn.style.height = `10%`; updateLogBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.05}px` updateLogBtn.style.border = `${4*core.domStyle.scale}px solid black` } } } // 鼠标拖拽滚动支持 let isMouseDown = false; let startY = 0; let scrollTop = 0; updateLogContent.addEventListener('mousedown', function (e) { isMouseDown = true; startY = e.pageY - updateLogContent.offsetTop; scrollTop = updateLogContent.scrollTop; updateLogContent.style.cursor = 'grabbing'; }); updateLogContent.addEventListener('mouseleave', function () { isMouseDown = false; updateLogContent.style.cursor = 'default'; }); updateLogContent.addEventListener('mouseup', function () { isMouseDown = false; updateLogContent.style.cursor = 'default'; }); updateLogContent.addEventListener('mousemove', function (e) { if (!isMouseDown) return; e.preventDefault(); const y = e.pageY - updateLogContent.offsetTop; const walk = (y - startY) * 2; updateLogContent.scrollTop = scrollTop - walk; }); document.addEventListener('DOMContentLoaded', function () { const tabsContainer = document.getElementById('updateLogTabs'); const progressContainer = document.createElement('div'); progressContainer.id = 'scrollProgressContainer'; const progressBar = document.createElement('div'); progressBar.id = 'scrollProgressBar'; progressContainer.appendChild(progressBar); // 将进度条插入到tabs容器后面 if (tabsContainer) { tabsContainer.parentNode.insertBefore(progressContainer, tabsContainer.nextSibling); // 检测是否是竖屏模式 function checkOrientation() { if (window.innerHeight > window.innerWidth || (typeof core !== 'undefined' && core.domStyle && core.domStyle.isVertical)) { setupScrollControls(); } } // 初始化滚动控制 function setupScrollControls() { // 鼠标滚轮控制横向滚动 tabsContainer.addEventListener('wheel', function (e) { if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) { this.scrollLeft += e.deltaY; e.preventDefault(); } }); // 触摸拖动控制 let isDragging = false; let startX, scrollLeft; tabsContainer.addEventListener('touchstart', function (e) { isDragging = true; startX = e.touches[0].pageX - this.offsetLeft; scrollLeft = this.scrollLeft; }); tabsContainer.addEventListener('touchmove', function (e) { if (!isDragging) return; e.preventDefault(); const x = e.touches[0].pageX - this.offsetLeft; const walk = (x - startX) * 2; this.scrollLeft = scrollLeft - walk; updateProgressBar(); }); tabsContainer.addEventListener('touchend', function () { isDragging = false; }); // 更新进度条 function updateProgressBar() { const scrollWidth = tabsContainer.scrollWidth - tabsContainer.clientWidth; const scrollPosition = tabsContainer.scrollLeft; const progress = (scrollPosition / scrollWidth) * 100; progressBar.style.width = progress + '%'; } // 监听滚动事件更新进度条 tabsContainer.addEventListener('scroll', updateProgressBar); // 初始更新进度条 updateProgressBar(); } // 初始检查和监听窗口变化 checkOrientation(); window.addEventListener('resize', checkOrientation); } }); document.addEventListener('DOMContentLoaded', function () { const tabsContainer = document.getElementById('updateLogTabs'); const progressContainer = document.createElement('div'); progressContainer.id = 'scrollProgressContainer'; const progressBar = document.createElement('div'); progressBar.id = 'scrollProgressBar'; progressContainer.appendChild(progressBar); // 将进度条插入到tabs容器后面 if (tabsContainer) { tabsContainer.parentNode.insertBefore(progressContainer, tabsContainer.nextSibling); // 添加横屏模式下的CSS样式 const style = document.createElement('style'); style.textContent = ` #scrollProgressContainer { width: 100%; height: 4px; background: #e0e0e0; position: relative; margin-top: 5px; } #scrollProgressBar { height: 100%; width: 0; background: #ff00b4; transition: width 0.2s ease; } body:not(.isVertical) #updateLogTabs { overflow-x: auto; white-space: nowrap; scrollbar-width: none; } body:not(.isVertical) #updateLogTabs::-webkit-scrollbar { display: none; } `; document.head.appendChild(style); // 初始化滚动控制 function setupScrollControls() { // 鼠标滚轮控制横向滚动 tabsContainer.addEventListener('wheel', function (e) { this.scrollLeft += e.deltaY; e.preventDefault(); }); // 鼠标拖动控制 let isDragging = false; let startX, scrollLeft; tabsContainer.addEventListener('mousedown', function (e) { isDragging = true; startX = e.pageX - this.offsetLeft; scrollLeft = this.scrollLeft; this.style.cursor = 'grabbing'; }); tabsContainer.addEventListener('mouseleave', function () { isDragging = false; this.style.cursor = 'default'; }); tabsContainer.addEventListener('mouseup', function () { isDragging = false; this.style.cursor = 'default'; }); tabsContainer.addEventListener('mousemove', function (e) { if (!isDragging) return; e.preventDefault(); const x = e.pageX - this.offsetLeft; const walk = (x - startX) * 2; this.scrollLeft = scrollLeft - walk; updateProgressBar(); }); // 更新进度条 function updateProgressBar() { const scrollWidth = tabsContainer.scrollWidth - tabsContainer.clientWidth; const scrollPosition = tabsContainer.scrollLeft; const progress = (scrollPosition / scrollWidth) * 100; progressBar.style.width = progress + '%'; } // 监听滚动事件更新进度条 tabsContainer.addEventListener('scroll', updateProgressBar); // 初始更新进度条 updateProgressBar(); } // 检查是否是横屏模式 function checkOrientation() { if (window.innerWidth > window.innerHeight || (typeof core !== 'undefined' && core.domStyle && !core.domStyle.isVertical)) { setupScrollControls(); } } // 初始检查和监听窗口变化 checkOrientation(); window.addEventListener('resize', checkOrientation); } }); }, "rankscore": function () { // 创建排行榜按钮和弹窗的样式 if (main.replayChecking) return const leaderboardStyle = document.createElement('style'); leaderboardStyle.textContent = ` #leaderboardBtn { position: absolute; top: 3%; right: 5%; width: 15%; height: 10%; background: white; border: 4px solid black; border-radius: 27.5px; color: black; font-size: ${parseFloat(core.dom.gameGroup.style.width)*0.04}px; font-weight: bold; cursor: pointer; z-index: 261; transition: all 0.3s ease; display: none; align-items: center; justify-content: center; box-shadow: 0 0 0 3px white, 0 2px 8px rgba(0, 0, 0, 0.2); outline: none; margin: 3px; } #leaderboardBtn:hover { background: #f5f5f5; transform: translateY(-1px); box-shadow: 0 0 0 3px white, 0 4px 12px rgba(0, 0, 0, 0.3); } #leaderboardBtn:active { transform: translateY(0); } /* 隐藏状态 */ #leaderboardBtn.hidden { opacity: 0; transform: translateY(-10px); pointer-events: none; transition: all 0.5s ease; } /* 显示动画 */ #leaderboardBtn:not(.hidden) { animation: buttonAppear 0.6s ease-out; } #leaderboardModal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 2000; display: none; justify-content: center; align-items: center; } #leaderboardWindow { background: white; border-radius: 0; overflow: hidden; box-shadow: 0 0 30px rgba(0, 0, 0, 0.5); position: relative; display: flex; flex-direction: column; } .isVertical #leaderboardWindow { width: min(95vw, 90vh * 416 / 676); height: min(95vh, 95vw * 676 / 416); } body:not(.isVertical) #leaderboardWindow { width: min(95vw, 95vh * 676 / 416); height: min(95vh, 95vw * 416 / 676); } #leaderboardHeader { background: #1a1a1a; color: white; padding: 15px 20px; font-size: 18px; font-weight: bold; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #333; } #leaderboardTitle { color: white; font-size: 30px; font-weight: bold; margin: 0; } #leaderboardClose { background: none; border: none; color: white; font-size: 60px; font-weight: 900; cursor: pointer; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; border-radius: 3px; transition: background 0.2s ease; line-height: 1; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); } #leaderboardClose:hover { background: rgba(255, 255, 255, 0.1); text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); } .leaderboard-main-container { flex: 1; display: flex; flex-direction: column; overflow: hidden; } #filterControls { padding: 15px; background: #f0f0f0; border-bottom: 1px solid #ddd; display: flex; flex-wrap: wrap; gap: 10px; } .filter-group { display: flex; align-items: center; margin-right: 5px; } .filter-group label { margin-right: 0px; font-weight: bold; white-space: nowrap; } .filter-group select { padding: 5px 5px; border: 1px solid #ccc; border-radius: 4px; background: white; width: 100px; } #leaderboardContent { flex: 1; background: #f5f1eb; overflow-y: auto; padding: 0px; position: relative; } #leaderboardTable { width: 100%; border-collapse: collapse; margin-top: 15px; } #leaderboardTable th { background: #1a1a1a; color: white; padding: 8px 5px; /* 减小内边距 */ text-align: left; position: sticky; top: 0; z-index: 10; white-space: nowrap; /* 防止换行 */ font-size: 14px; /* 减小字体大小 */ height: 30px; /* 固定高度 */ min-width: 60px; /* 最小宽度 */ } /* 特定列设置更小的宽度 */ #leaderboardTable th:nth-child(1) { width: 50px; } /* 排名 */ #leaderboardTable th:nth-child(2) { width: 80px; } /* 地图 */ #leaderboardTable th:nth-child(3) { width: 60px; } /* 难度 */ #leaderboardTable th:nth-child(4) { width: 60px; } /* 角色 */ #leaderboardTable th:nth-child(5) { width: 50px; } /* 等级 */ #leaderboardTable th:nth-child(6) { width: 80px; } /* 结局 */ #leaderboardTable th:nth-child(7) { width: 70px; } /* 计分模式 */ #leaderboardTable th:nth-child(8) { width: 60px; } /* 分数 */ #leaderboardTable th:nth-child(9) { width: 80px; } /* 用户名 */ #leaderboardTable th:nth-child(10) { width: 70px; } /* 游戏时间 */ #leaderboardTable th:nth-child(11) { width: 100px; } /* 提交时间 */ /* 表格单元格样式也相应调整 */ #leaderboardTable td { padding: 8px 5px; border-bottom: 1px solid #ddd; background: white; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #leaderboardTable tr:nth-child(even) td { background: #f9f9f9; } #leaderboardTable tr:hover td { background: #f0f0f0; } .rank-cell { font-weight: bold; text-align: center; } .score-cell { text-align: right; } .time-cell { text-align: right; } #leaderboardContent::-webkit-scrollbar { width: 8px; } #leaderboardContent::-webkit-scrollbar-track { background: #e8e8e8; } #leaderboardContent::-webkit-scrollbar-thumb { background: #c0c0c0; border-radius: 4px; } #leaderboardContent::-webkit-scrollbar-thumb:hover { background: #a0a0a0; } .no-results { text-align: center; padding: 30px; color: #666; font-size: 18px; } .loading { text-align: center; padding: 30px; color: #666; font-size: 18px; } @media (max-width: 400px) or (max-height: 400px) { #leaderboardWindow { width: 98vw !important; height: 98vh !important; } #filterControls { flex-direction: row; flex-wrap: wrap; gap: 5px; padding: 8px; } #leaderboardClose { font-size: 36px; width: 45px; height: 45px; } } /* 竖屏适配 */ .isVertical #leaderboardBtn { top: 12px; right: 12px; left: auto; width: 110px; height: 50px; font-size: 30px; border-radius: 25px; } /* 鼠标拖拽滚动支持 */ #leaderboardContent { scroll-behavior: smooth; } `; // 将样式添加到head document.head.appendChild(leaderboardStyle); // 创建排行榜按钮 const leaderboardBtn = document.createElement('button'); leaderboardBtn.id = 'leaderboardBtn'; leaderboardBtn.innerHTML = '排行榜'; core.dom.startPanel.appendChild(leaderboardBtn); // 创建排行榜弹窗 const leaderboardModal = document.createElement('div'); leaderboardModal.id = 'leaderboardModal'; const leaderboardWindow = document.createElement('div'); leaderboardWindow.id = 'leaderboardWindow'; // 创建顶部标题栏 const leaderboardHeader = document.createElement('div'); leaderboardHeader.id = 'leaderboardHeader'; const leaderboardTitle = document.createElement('h3'); leaderboardTitle.id = 'leaderboardTitle'; leaderboardTitle.textContent = '排行榜'; const leaderboardClose = document.createElement('button'); leaderboardClose.id = 'leaderboardClose'; leaderboardClose.textContent = '×'; leaderboardHeader.appendChild(leaderboardTitle); leaderboardHeader.appendChild(leaderboardClose); leaderboardWindow.appendChild(leaderboardHeader); // 创建主要内容区域 const leaderboardMainContainer = document.createElement('div'); leaderboardMainContainer.className = 'leaderboard-main-container'; // 创建筛选控件区域 const filterControls = document.createElement('div'); filterControls.id = 'filterControls'; // 地图选择 const mapGroup = document.createElement('div'); mapGroup.className = 'filter-group'; mapGroup.innerHTML = ` `; // 难度选择 const difficultyGroup = document.createElement('div'); difficultyGroup.className = 'filter-group'; difficultyGroup.innerHTML = ` `; // 角色选择 const characterGroup = document.createElement('div'); characterGroup.className = 'filter-group'; characterGroup.innerHTML = ` `; // 等级选择 const levelGroup = document.createElement('div'); levelGroup.className = 'filter-group'; levelGroup.innerHTML = ` `; // 结局选择 const endingGroup = document.createElement('div'); endingGroup.className = 'filter-group'; endingGroup.innerHTML = ` `; // 计分选择 const scoreTypeGroup = document.createElement('div'); scoreTypeGroup.className = 'filter-group'; scoreTypeGroup.innerHTML = ` `; const checkGroup = document.createElement('div'); checkGroup.className = 'filter-group'; checkGroup.innerHTML = ` `; filterControls.appendChild(mapGroup); filterControls.appendChild(difficultyGroup); filterControls.appendChild(characterGroup); filterControls.appendChild(levelGroup); filterControls.appendChild(endingGroup); filterControls.appendChild(scoreTypeGroup); filterControls.appendChild(checkGroup); leaderboardMainContainer.appendChild(filterControls); // 创建内容区域 const leaderboardContent = document.createElement('div'); leaderboardContent.id = 'leaderboardContent'; // 添加加载中提示 leaderboardContent.innerHTML = '
    加载中...
    '; leaderboardMainContainer.appendChild(leaderboardContent); leaderboardWindow.appendChild(leaderboardMainContainer); leaderboardModal.appendChild(leaderboardWindow); document.body.appendChild(leaderboardModal); // 添加事件监听器 leaderboardBtn.addEventListener('click', () => { checkLeaderboardOrientation(); leaderboardModal.style.display = 'flex'; document.body.style.overflow = 'hidden'; loadLeaderboardData(); }); // 关闭弹窗 function closeLeaderboardModal() { leaderboardModal.style.display = 'none'; document.body.style.overflow = 'auto'; } leaderboardClose.addEventListener('click', closeLeaderboardModal); // 点击背景关闭弹窗 leaderboardModal.addEventListener('click', function (e) { checkLeaderboardOrientation() if (e.target === leaderboardModal) { closeLeaderboardModal(); } }); // ESC键关闭弹窗 document.addEventListener('keydown', function (e) { checkLeaderboardOrientation() if (e.key === 'Escape' && leaderboardModal.style.display === 'flex') { closeLeaderboardModal(); } }); // 筛选器变化时重新加载数据 document.getElementById('mapSelect').addEventListener('change', loadLeaderboardData); document.getElementById('difficultySelect').addEventListener('change', loadLeaderboardData); document.getElementById('characterSelect').addEventListener('change', loadLeaderboardData); document.getElementById('endingSelect').addEventListener('change', loadLeaderboardData); document.getElementById('scoreTypeSelect').addEventListener('change', loadLeaderboardData); document.getElementById('levelSelect').addEventListener('change', loadLeaderboardData); document.getElementById('greenSelect').addEventListener('change', loadLeaderboardData); let scoreList = [] getList().then(res => { res.sort((a, b) => b.score - a.score === 0 ? a.timestamp - b.timestamp : b.score - a.score) let isin = [] res.forEach(v => { let name = v.map + v.difficulty + v.ending + v.scoreType + v.character + v.level + v if (!isin.includes(name)) { isin.push(name) scoreList.push(v) } }) }) // 加载排行榜数据 function loadLeaderboardData() { // 获取筛选条件 const map = document.getElementById('mapSelect').value; const difficulty = document.getElementById('difficultySelect').value; const character = document.getElementById('characterSelect').value; const level = document.getElementById('levelSelect').value; const ending = document.getElementById('endingSelect').value; const scoreType = document.getElementById('scoreTypeSelect').value; const green = !document.getElementById('greenSelect').checked; // 显示加载中 leaderboardContent.innerHTML = '
    加载中...
    '; if (scoreList) { renderLeaderboard(scoreList.filter(v => { let maped = v.map === map || map === "all" let difficultyed = v.difficulty === difficulty || difficulty === "all" let charactered = v.character === character || character === "all" let leveled = v.level === level || level === "all" let endinged = v.ending === ending || ending === "all" let scoreTypeed = v.scoreType === scoreType || scoreType === "all" let greened = v.green || green return maped && difficultyed && charactered && leveled && endinged && scoreTypeed && greened })) } else { setTimeout(() => { leaderboardContent.innerHTML = '
    排行榜数据暂未加载完毕,请稍后重试
    '; }, 500) } } //读取网站数据 async function getList() { let req req = await fetch("https://h5mota.com/backend/submission/list.php?name=51_AstralParty&page=1").then(res => { return res.json(); }) let { list, total } = req const reqlist = [] const length = Math.ceil(total / 50) for (let i = 2; i <= length; i++) { reqlist.push(fetch(`https://h5mota.com/backend/submission/list.php?name=51_AstralParty&page=${i}`).then(res => { return res.json(); })) } await Promise.all(reqlist).then(res => { res.forEach(v => list = list.concat(v.list)) }) return list.map(v => { let a = {} if (!v) return if (v.ending.indexOf("梦想号") >= 0) { a.map = "星趴·梦想号" } else if (v.ending.indexOf("御魂庆典") >= 0) { a.map = "御魂庆典" } else if (v.ending.indexOf("水乡古镇") >= 0) { a.map = "水乡古镇" } else if (v.ending.indexOf("魔法学院") >= 0) { a.map = "魔法学院" } else if (v.ending.indexOf("龙宫游乐园") >= 0) { a.map = "龙宫游乐园" } if (v.ending.indexOf("普通") >= 0) { a.difficulty = "普通" } else if (v.ending.indexOf("困难") >= 0) { a.difficulty = "困难" } else if (v.ending.indexOf("噩梦") >= 0) { a.difficulty = "噩梦" } else if (v.ending.indexOf("疯狂") >= 0) { a.difficulty = "疯狂" } let template = v.user.template if (template && template.indexOf("_") >= 0) { let characters = template.split("_") a.character = characters[0] a.level = characters[1] } a.name = v.user.name if (v.ending.indexOf("星趴大神") >= 0) { a.ending = "星趴大神" } else if (v.ending.indexOf("星趴高手") >= 0) { a.ending = "星趴高手" } else if (v.ending.indexOf("锦鲤之友") >= 0) { a.ending = "锦鲤之友" } else if (v.ending.indexOf("银蛇之友") >= 0) { a.ending = "银蛇之友" } if (v.ending.indexOf("生命") >= 0) { a.scoreType = "生命" } else if (v.ending.indexOf("攻击") >= 0) { a.scoreType = "攻击" } else if (v.ending.indexOf("防御") >= 0) { a.scoreType = "防御" } a.green = v.verify === -1 a.score = v.score a.totalTime = v.totalTime a.timestamp = core.formatDate(new Date(v.timestamp * 1000)) return a }).filter(v => v && v.character) } // 生成模拟数据 function generateMockLeaderboardData(map, difficulty, character, level, ending, scoreType) { const maps = ['星趴·梦想号', '御魂庆典', '水乡古镇', '魔法学院', '龙宫游乐园']; const difficulties = ['普通', '困难', '噩梦', '疯狂']; const characters = ["勇者", "米米", "帕露南", "芬妮", "阿兰娜", "小町", "派德曼", "帕帕拉", "恋", "Z3000", "潘大猛", "璐璐", "枫", "蓝海晴", "美咲", "娜蒂斯", "茉莉", "星魅琉华", "南希露", "梅加斯", "姬梦朝", "阿尔", "凛", "照"]; const levels = ["0级", "1级", "2级", "3级", "4级", "5级", "6级"]; const endings = ['星趴高手', '星趴大神', '锦鲤之友', '银蛇之友']; const scoreTypes = ['生命', '攻击', '防御']; // 根据筛选条件生成数据 const data = []; const count = Math.floor(Math.random() * 15) + 5; // 5-20条随机数据 for (let i = 0; i < count; i++) { const selectedMap = map === 'all' ? maps[Math.floor(Math.random() * maps.length)] : map; const selectedDifficulty = difficulty === 'all' ? difficulties[Math.floor(Math.random() * difficulties.length)] : difficulty; const selectedCharacter = character === 'all' ? characters[Math.floor(Math.random() * characters.length)] : character; const selectedLevel = level === 'all' ? levels[Math.floor(Math.random() * levels.length)] : level; const selectedEnding = ending === 'all' ? endings[Math.floor(Math.random() * endings.length)] : ending; const selectedScoreType = scoreType; data.push({ map: selectedMap, difficulty: selectedDifficulty, character: selectedCharacter, level: selectedLevel, ending: selectedEnding, scoreType: selectedScoreType, score: Math.floor(Math.random() * 10000) + 5000, totalTime: `${Math.floor(Math.random() * 10)}:${Math.floor(Math.random() * 60).toString().padStart(2, '0')}:${Math.floor(Math.random() * 60).toString().padStart(2, '0')}`, timestamp: core.formatDate(new Date(new Date().getTime() - Math.floor(Math.random() * 100000000))) }); } return data; } // 渲染排行榜 function renderLeaderboard(data) { if (data.length === 0) { leaderboardContent.innerHTML = '
    没有找到匹配的记录
    '; return; } const table = document.createElement('table'); table.id = 'leaderboardTable'; // 创建表头 const thead = document.createElement('thead'); const headerRow = document.createElement('tr'); headerRow.innerHTML = ` 排名 地图 难度 角色 等级 结局 计分模式 分数 用户名 游戏时间 提交时间 `; thead.appendChild(headerRow); table.appendChild(thead); // 创建表格内容 const tbody = document.createElement('tbody'); data.forEach((item, i) => { const row = document.createElement('tr'); row.innerHTML = ` ${i+1} ${item.map} ${item.difficulty} ${item.character} ${item.level} ${item.ending} ${item.scoreType} ${item.score} ${item.name} ${item.totalTime} ${item.timestamp} `; tbody.appendChild(row); }); table.appendChild(tbody); // 清空内容区域并添加表格 leaderboardContent.innerHTML = ''; leaderboardContent.appendChild(table); } // 检查方向并调整样式 function checkLeaderboardOrientation() { let leaderboardBtn = document.getElementById('leaderboardBtn') leaderboardBtn.style.fontSize = `${parseFloat(core.dom.gameGroup.style.width)*0.03}px` if (typeof core !== 'undefined' && core.domStyle && core.domStyle.isVertical !== undefined) { if (core.domStyle.isVertical) { document.body.classList.add('isVertical'); leaderboardBtn.style.top = `3%`; leaderboardBtn.style.left = `72%`; leaderboardBtn.style.width = `20%`; leaderboardBtn.style.height = `6%`; leaderboardBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.03}px` leaderboardBtn.style.border = `${4*core.domStyle.scale}px solid black` } else { document.body.classList.remove('isVertical'); leaderboardBtn.style.top = `3%`; leaderboardBtn.style.left = `83%`; leaderboardBtn.style.width = `15%`; leaderboardBtn.style.height = `10%`; leaderboardBtn.style.borderRadius = `${parseFloat(core.dom.gameGroup.style.height)*0.05}px` leaderboardBtn.style.border = `${4*core.domStyle.scale}px solid black` } } } // 鼠标拖拽滚动支持 let isLeaderboardMouseDown = false; let leaderboardStartY = 0; let leaderboardScrollTop = 0; leaderboardContent.addEventListener('mousedown', function (e) { isLeaderboardMouseDown = true; leaderboardStartY = e.pageY - leaderboardContent.offsetTop; leaderboardScrollTop = leaderboardContent.scrollTop; leaderboardContent.style.cursor = 'grabbing'; }); leaderboardContent.addEventListener('mouseleave', function () { isLeaderboardMouseDown = false; leaderboardContent.style.cursor = 'default'; }); leaderboardContent.addEventListener('mouseup', function () { isLeaderboardMouseDown = false; leaderboardContent.style.cursor = 'default'; }); leaderboardContent.addEventListener('mousemove', function (e) { if (!isLeaderboardMouseDown) return; e.preventDefault(); const y = e.pageY - leaderboardContent.offsetTop; const walk = (y - leaderboardStartY) * 2; leaderboardContent.scrollTop = leaderboardScrollTop - walk; }); } }