var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = 
{
    "init": function () {
	// 简单修改：超过8位才转换
	var oldFormatBigNumber = core.utils.formatBigNumber;
	core.formatBigNumber = core.utils.formatBigNumber = function (x, digits) {
		if (!digits || digits === true || (typeof digits === 'number' && digits < 8)) digits = 8;
		return oldFormatBigNumber.call(core.utils, x, digits);
	};

	// =========================================================================
	// I. 核心与随机数 (Core & Random)
	// =========================================================================
	function br() {
		core.stopAnimate();
		core.ui.closePanel();
		if (core.isReplaying()) {
			setTimeout(function () {
				core.replay();
			}, core.control.__replay_getTimeout() / 2);
		}
	}
	// 统一使用 core.rand2，并将其赋值给一个简短的常量
	function rand(n) {
		if (core.isReplaying() && core.status.replay.toReplay[0] === 'retreat') {
			core.status.replay.toReplay.shift();
			core.status.route.push("retreat");
			core.status.event.data = null;
			br();
			core.battle_exit();
			return -1; // [MODIFIED] 返回-1作为信号
		} else return core.rand2(n);
	}

	// =========================================================================
	// II. UI与事件监听模块 (UI & Event Listeners)
	// =========================================================================

	const uiModule = {
		/**
		 * 初始化UI相关的DOM事件监听
		 */
		init: function () {

			core.dom.statusCanvas.onclick = function (e) {
				try {
					e.preventDefault();
					core.onStatusBarClick(e);
				} catch (err) {
					main.log(err);
				}
			};

			core.dom.toolBar.onclick = function (e) {
				try {
					e.preventDefault();
					core.onToolBarClick(e);
				} catch (err) {
					main.log(err);
				}
			};

			core.registerResize("showStatusBar", function () {
				core.dom.statusCanvas.style.display = "block";
				if (!core.domStyle.isVertical) core.dom.toolBar.style.display = "none";
				else core.dom.toolBar.style.display = "block";
			});
		},

		sys_onStatusBarClick: function (px, py, vertical) {
			if (core.actions.actionsdata.onStatusBarClick)
				return core.actions.actionsdata.onStatusBarClick(px, py, vertical);
		},

		sys_onToolBarClick: function (px, py, vertical) {
			if (core.actions.actionsdata.onStatusBarClick)
				return core.actions.actionsdata.onToolBarClick(px, py, vertical);
		},

		onToolBarClick: function (e) {
			if (!core.isPlaying()) return false;
			var left = core.dom.gameGroup.offsetLeft + 3;
			var top = core.dom.gameGroup.offsetTop + core.dom.statusBar.offsetHeight + core.dom.gameDraw.offsetHeight + 3;
			var px = parseInt((e.clientX - left) / core.domStyle.scale),
				py = parseInt((e.clientY - top) / core.domStyle.scale);
			return core.sys_onToolBarClick(Math.max(px, 0), Math.max(py, 0), core.domStyle.isVertical);
		}
	};

	// =========================================================================
	// III. 核心钩子与重载 (Core Hooks & Overrides)
	// =========================================================================

	const hooksModule = {
		_afterLoadResources: function () {
			// 本函数将在所有资源加载完毕后，游戏开启前被执行
			// var arr = core.splitImage("assets.png", 32, 32);
			// for (var i = 0; i < arr.length; i++) {core.material.images.images["asset"+i+".png"] = arr[i];}
		},

		afterLoadData: function () {
			// 修正装备名称显示
			core.material.items.proof_brave.name = core.material.items.proof_brave.text;
			core.material.items.proof_tyrant.name = core.material.items.proof_tyrant.text;
			core.material.items.proof_sage.name = core.material.items.proof_sage.text;
			if (core.hasFlag('proof')) {
				core.material.items.proof_brave.name = '勇者之证';
				core.material.items.proof_tyrant.name = '霸者之证';
				core.material.items.proof_sage.name = '贤者之证';
			}
		},

		updateFg: function () {
			// 此处为空，但保留结构
		},

		/**
		 * 覆盖核心音频加载
		 */
		override_loadMusic: function () {
			core.loader._loadMusic = function () {
				core.bgms.forEach(function (t) {
					core.loader.loadOneMusic(t);
				});

				if (main.useCompress && core.musicStatus.audioContext) {
					core.unzip('project/sounds/sounds.zip?v=' + main.version, function (data) {
						for (var name in data) {
							if (core.sounds.indexOf(name) >= 0) {
								core.loader._loadOneSound_decodeData(name, data[name]);
							}
						}
					});
				} else {
					core.sounds.forEach(function (t) {
						core.loader.loadOneSound(t);
					});
				}
				// 直接开始播放
				core.playBgm("bgm.mp3");
			};
		},
	};

	// =========================================================================
	// IV. 物品逻辑模块 (Item Logic)
	// =========================================================================

	const itemModule = {
		useProof: function (itemId) {
			if (itemId == 'proof_brave') {
				core.status.hero.cri += 5;
				core.setFlag('proof_brave', 1);
				core.setHeroIcon('hero1.png');
			}
			if (itemId === 'proof_sage') {
				core.status.hero.agi += 5;
				core.setFlag('proof_sage', 1);
				core.setHeroIcon('hero2.png');
			}
			if (itemId == 'proof_tyrant') {
				core.status.hero.atk += 15;
				core.status.hero.def += 15;
				core.setFlag('proof_tyrant', 1);
				core.setHeroIcon('hero3.png');
			}
			core.setFlag('proof', true);
			core.playSound('获得道具');
			core.afterLoadData();
			core.drawTip("已装备" + core.material.items[itemId].name);
			return;
		},

		/**
		 * 装备逻辑 (私有)
		 */
		_useEquipment: function (itemId, name, type) {
			if (itemId.indexOf(name) == 0) {
				var now = core.getFlag(name, name + "0");

				if (typeof core.values[now] == 'number') {
					core.status.hero[type] -= core.values[now];
				} else {
					core.status.hero.atk -= core.values[now].atk || 0;
					core.status.hero.def -= core.values[now].def || 0;
					core.status.hero.mdef -= core.values[now].mdef || 0;
				}

				if (typeof core.values[itemId] == 'number') {
					core.status.hero[type] += core.values[itemId];
				} else {
					core.status.hero.atk += core.values[itemId].atk || 0;
					core.status.hero.def += core.values[itemId].def || 0;
					core.status.hero.mdef += core.values[itemId].mdef || 0;
				}

				core.setItem(now, 1);
				core.setItem(itemId, 0);
				core.setFlag(name, itemId);
				core.drawTip("已装备" + core.material.items[itemId].name);
			}
		},

		/**
		 * 装备逻辑 (公共)
		 */
		useEquipment: function (itemId) {
			itemModule._useEquipment(itemId, "sword", "atk");
			itemModule._useEquipment(itemId, "shield", "def");
		},
	};

	// =========================================================================
	// V. 战斗逻辑模块 (Battle Logic)
	// =========================================================================

	const battleModule = {
		/**
		 * 战斗UI配置
		 */
		battleInfo: {
			width: core.__PIXELS__ - 16,
			height: 128,
			left: 8,
			top: 96
		},

		getBattleInterval: function (baseInterval) {
			if (flags.battleSpeed === 9) return Math.max(5, Math.round(baseInterval * 0.35));
			return Math.max(10, Math.round(baseInterval * 2.0));
		},

		/**
		 * 注册战斗相关的钩子和覆盖
		 */
		init_battle_hooks: function () {
			// 注册战斗相关的按键
			core.registerAction('onkeyUp', '_battle_onkeyUp_lockControl', function (e) {
				let keyCode = e.keyCode; // 使用标准的 keyCode 属性
				if (!core?.status?.hero?.battleStatus) return;
				if (keyCode == 13 && core.status.hero.battleStatus === "afterBattle" && core.status.hero.battleAnimationCompleted) {
					core.status.hero.battleStatus = "end";
					return true;
				} else if (core.status.hero.battleStatus === "fighting") {
					if (keyCode == 81) {
						core.status.route.push("retreat");
						// 撤退时血量不能超过战前
						if (core.status.hero.hp > core.status.battle_heroHp) {
							core.status.hero.hp = core.status.battle_heroHp;
						}
						br();
						core.battle_exit();
						return true;
					}
					if (keyCode == 80) {
						if (flags.battleSpeed != 9) flags.battleSpeed = 9;
						else flags.battleSpeed = 20;
						return true;
					}
				}
			}, 51);

			core.registerAction('onclick', '_battle_onclick_lockControl', function (x, y) {
				if (!core?.status?.hero?.battleStatus) return;
				if (core.status.hero.battleStatus === "afterBattle" && core.status.hero.battleAnimationCompleted) {
					core.status.hero.battleStatus = "end";
					//return true;
				} else if (core.status.hero.battleStatus === "fighting") {
					if (x >= 10 && x <= 12 && y == 6) {
						core.status.route.push("retreat");
						// 撤退时血量不能超过战前
						if (core.status.hero.hp > core.status.battle_heroHp) {
							core.status.hero.hp = core.status.battle_heroHp;
						}
						br();
						core.battle_exit();
					}
					if (x >= 5 && x <= 7 && y == 6) {
						if (flags.battleSpeed != 9) flags.battleSpeed = 9;
						else flags.battleSpeed = 20;
					}
					//return true;
				}
			}, 51);

			// 解锁
			core.control.unlockControl = function () {
				if (!((core.status.hero || {}).battleStatus === "fighting" || (core.status.hero || {}).battleStatus == "afterBattle")) core.status.lockControl = false;
			};

			/**
			 * 覆盖核心战斗事件
			 * !!! 注意：此处替换了 core.rand()
			 */
			core.events.battle = function (id, x, y, force, callback) {
				core.saveAndStopAutomaticRoute();
				id = id || core.getBlockId(x, y);
				if (!id) return core.clearContinueAutomaticRoute(callback);
				// 自动存档
				if (!core.status.event.id) core.autosaveFromSnapshot();
				// 战前事件
				if (!core.beforeBattle(id, x, y))
					return core.clearContinueAutomaticRoute(flags.__callback__);
				if (flags.useBattleAnimate) {

					// 拷贝怪物数据
					var enemy = core.clone(core.material.enemys[id]);
					// 仿攻
					if (core.hasSpecial(enemy.special, 35)) enemy.atk = Math.max(enemy.atk, core.getRealStatus("atk"));
					// 坚固
					if (core.hasSpecial(enemy.special, 31)) enemy.def = Math.max(enemy.def, core.getRealStatus("atk") - 1);
					core.status.hero.battle_enemy = enemy;

					core.battle_drawUI();

					core.status.hero.battleStatus = "fighting";
					// 回合数
					core.status.hero.battle_heroTurn = 0;
					core.status.hero.battle_enemyTurn = 0;

					// 计算先攻权
					var first = 0;
					if (core.hasSpecial(enemy, 29)) first = 1;

					flags.__callback__ = callback;
					flags.__loc__ = [x, y];

					core.status.battle_heroHp = core.status.hero.hp;
					var hp = core.status.hero.hp;
					// 事件流跑战斗过程
					var enemyEvent = [{
						"type": "function",
						"function": "function(){core.battle_enemyAttack();}"
					}];
					var heroEvent = [{
						"type": "function",
						"function": "function(){core.battle_heroAttack()}"
					}];
					var battleEvent = heroEvent.concat(enemyEvent);
					if (first) battleEvent = enemyEvent.concat(heroEvent);
					core.insertAction([{
						"type": "while",
						"condition": "core.status.hero.battleStatus === \"fighting\"",
						"data": battleEvent,
					}]);
					core.doAction();
				} else {
					// 非强制战斗
					if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) {
						core.drawTip("你打不过此怪物！", null, true);
						return core.clearContinueAutomaticRoute(callback);
					}
					// 战后事件
					core.afterBattle(id, x, y, callback);
				}
			};
		},

		battle_drawUI: function () {
			var info = core.plugin.battleInfo;
			var width = info.width,
				height = info.height,
				left = info.left,
				top = info.top;
			var ctx = core.getContextByName("__battle__") || core.createCanvas("__battle__", left, top, width, height, 66);

			core.setAlpha(ctx, 1);
			ctx.canvas.style.backgroundColor = "gray";
			ctx.canvas.style.backgroundImage = "url(project/images/ground.png)";
			ctx.canvas.style.fontFamily = "布丁体";
			//core.fillRect(ctx, left, top, width, height, core.material.groundPattern);

			// 背景框的线宽,颜色
			var lineWidth = 4,
				strokeStyle = "#0099CC";
			core.plugin.battleInfo.lineWidth = lineWidth;
			core.strokeRect(ctx, lineWidth / 2, lineWidth / 2, width - lineWidth, height - lineWidth, strokeStyle, lineWidth);


			// 战斗角色名字的字体大小
			var fontSize = 15;
			// 字体是否加粗
			var isBold = false;
			// 字体是否是斜体
			var isItalic = false;
			// 颜色
			var ncolor = "white";
			// 勇士跟怪物的box
			// 勇士：

			// 坐标 box的尺寸
			var bx = 42,
				by = 20 + fontSize * 3 / 4,
				size = 28;
			core.plugin.battleInfo.bx = bx;
			core.plugin.battleInfo.by = by;
			core.plugin.battleInfo.size = size;

			var bx2 = width - bx - size;
			var hero = core.material.images.hero;
			var h_width = core.material.icons.hero.width || 32,
				h_height = core.material.icons.hero.height;
			// 怪物每隔xms动一次
			var speed = battleModule.getBattleInterval(core.values.animateSpeed - 50);

			// 勇士4帧 这里只用第2 4帧 全用上很鬼畜……
			// 新新里的勇士不会动所以关掉
			var heroAnimate = false;
			// 先绘制上第一帧 防止由于定时器导致box闪一下才出来
			core.drawImage(ctx, hero, 0, 0, h_width, h_height, bx2, by, size, size);
			core.strokeRect(ctx, bx2, by, size, size, strokeStyle, 2);

			var enemy = core.status.hero.battle_enemy;
			//enemy = core.enemys.getenemyAttackOnce(enemy, hero, enemy.x, enemy.y)
			var enemyName = "敌人";
			enemyName = core.material.enemys[enemy.id].name;
			var font = (isItalic ? "italic " : "") + (isBold ? "bold " : "") + fontSize + "px 布丁体";
			core.setTextAlign(ctx, "center");
			core.fillText(ctx, enemyName, bx + size / 2, by - fontSize * 3 / 4, ncolor, font);
			core.fillText(ctx, "勇者", bx2 + size / 2, by - fontSize * 3 / 4, ncolor, font);

			// VS标志
			core.fillText(ctx, "vs", width / 2, by - fontSize * 3 / 4, ncolor, '25px 布丁体');
			core.setTextAlign(ctx, "left");

			if (heroAnimate) {
				var animate = 4,
					a = 4;
				if (!(core.isReplaying() || main.replayChecking)) {
					var box_hero = window.setInterval(function () {
						core.clearMap(ctx, bx2 - 2, by - 2, size + 4, size + 4);
						if (core.status.hero.battleStatus !== "fighting") clearInterval(box_hero);
						core.drawImage(ctx, hero, (a - 1) * h_width, 0, h_width, h_height, bx2, by, 32, h_height);
						core.strokeRect(ctx, bx2, by, size, size, strokeStyle, 2);
						a += 2;
						if (a > animate) a = 2;
					}, speed);
				}
			}

			var block = core.getBlockInfo(enemy.id || "greenSlime");
			var b_width = 32,
				b_height = block.height;
			var animate = block.animate,
				aa = 1;
			var enemyType = "enemys";
			if (b_height == 48) enemyType = "enemy48";
			var b_pos = block.posY;
			var enemyImage = core.material.images[enemyType];

			// 绘制第一帧 理由同上
			core.drawImage(ctx, enemyImage, (aa - 1) * b_width, b_pos * b_height, b_width, b_height, bx, by, size, size);
			core.strokeRect(ctx, bx, by, size, size, strokeStyle, 2);
			aa++;

			if (!(core.isReplaying() || main.replayChecking)) {
				var box_enemy = window.setInterval(function () {
					core.clearMap(ctx, bx - 2, by - 2, size + 4, size + 4);
					if (core.status.hero.battleStatus !== "fighting") {
						clearInterval(box_enemy);
					}
					core.drawImage(ctx, enemyImage, (aa - 1) * b_width, b_pos * b_height, b_width, b_height, bx, by, size, size);
					core.strokeRect(ctx, bx, by, size, size, strokeStyle, 2);
					aa++;
					if (aa > animate) aa = 1;
				}, speed);
			}
			core.battle_battling_draw(null, enemy);
		},

		battle_battling_draw: function (hero, enemy) {
			if (typeof enemy == "string") enemy = core.material.enemys[enemy];
			if (!hero) hero = core.status.hero;

			// 定义战斗结束函数，避免重复代码
			var battleEnd = function () {
				var damage = core.status.hero.hp - core.status.battle_heroHp;

				core.status.hero.statistics.battleDamage += damage;
				core.status.hero.statistics.battle++;

				//先毒再衰
				if (core.status.hero.battle_triggerPoison) core.triggerDebuff('get', 'poison');
				if (core.status.hero.battle_triggerWeak) core.triggerDebuff('get', 'weak');

				var x = flags.__loc__[0];
				var y = flags.__loc__[1];
				// 战后事件
				core.afterBattle(enemy.id, x, y, flags.__callback__);
			};

			if (main.replayChecking) {
				if (core.status.hero.hp > 0 && core.status.hero.battle_enemy.hp <= 0) {
					br();

					// 录像直接跳过
					if (core.isReplaying()) {
						var enemy = core.status.hero.battle_enemy;
						if (enemy == null) return;
						battleEnd();
						return core.battle_exit();
					}
				}
			}

			var ctx = core.getContextByName("__battle__");
			if (!ctx) return;
			core.setAlpha(ctx, 1);

			var info = core.plugin.battleInfo;
			var width = info.width,
				height = info.height,
				left = info.left,
				top = info.top,
				bx = info.bx,
				by = info.by,
				size = info.size,
				lineWidth = info.lineWidth;
			core.setTextAlign(ctx, "left");
			var bx2 = width - bx - size;
			ctx.clearRect(bx + size + 2, by - 4, bx2 - (bx + size + 2) - 1, height - by);

			// 字体设置 同上
			var textFontSize = 14;
			var isBold = false;
			var isItalic = false;
			var textFont = (isItalic ? "italic " : "") + (isBold ? "bold " : "") + textFontSize + "px 布丁体";

			// 绘制的能力项 可以仿照增加 格式为"勇士属性英文": "勇士属性名"
			var toDraw = {
				"hp": "体力",
				"atk": "攻击力",
				"def": "防御力",
				"agi": "敏捷"
			};

			// 绘制行距
			var lineHeight = 10;


			var text = "";
			var offsetY = 0;
			var tx = bx + size + 8,
				ty = by + textFontSize * 3 / 4;
			for (var status in toDraw) {
				core.fillText(ctx, toDraw[status] + ": " + (enemy[status] || 0), tx, ty + offsetY, "white", textFont);
				offsetY += textFontSize + lineHeight;
			}
			offsetY = 0;
			core.setTextAlign(ctx, "right");
			for (var status in toDraw) {
				core.fillText(ctx, (core.getRealStatusOrDefault(hero, status) || 0) + " :" + toDraw[status], width - tx, ty + offsetY, "white", textFont);
				offsetY += textFontSize + lineHeight;
			}
			offsetY -= textFontSize + lineHeight;
			core.setTextAlign(ctx, "center");
			var text2 = flags.battleSpeed == 9 ? "[减速(P)]" : "[加速(P)]";
			core.fillText(ctx, text2, width / 2, ty + offsetY, "white", "16px 布丁体");
			core.setTextAlign(ctx, "left");
			var bx2 = width - bx - size;
			// 撤退(Q)
			var hp = (core.status.hero.battle_enemy || {}).hp == null ? 1 : (core.status.hero.battle_enemy || {}).hp;

			if (core.status.hero.battleStatus !== "fighting" && hp > 0) {
				core.fillText(ctx, " 撤退(Q)", bx2, ty + offsetY, "#ffff00", "14px WenDingGuangGaoTi"); //yzm：原为"italic bold 16px myFont1" //翻新：不管颜色跟字体都不对啊！
			} else if (core.status.hero.hp > 0 && core.status.hero.battle_enemy.hp <= 0) {
				// 退出时显示下拉框和enter退出
				var w = core.calWidth(ctx, " 撤退(Q)", "bold 16px WenDingGuangGaoTi");
				core.clearMap(ctx, bx2 - 1, ty + offsetY - 16, w + 3, 20);
				//core.fillText(ctx, "-Enter-", bx2, ty + offsetY, "white", "16px myFont1");
				br();

				// 录像直接跳过
				if (core.isReplaying()) {
					var enemy = core.status.hero.battle_enemy;
					if (enemy == null) return;
					battleEnd();
					return core.battle_exit();
				}
				core.status.hero.battleStatus = "afterBattle";
				var ch = 0;
				var canvas = ctx.canvas;
				var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
				core.lockControl();
				core.playSound("slide.mp3");
				// 战斗结束后的下拉框
				if (!core.isReplaying()) {
					let ch = 0;
					let frame = 0;
					let lastTime = 0;
					const animationDuration = flags.battleSpeed <= 9 ? 100 : 500; // 下拉动画持续时间(ms)
					const blinkInterval = 1000; // 完整的闪烁周期(ms)
					const targetHeight = 32; // 目标高度

					core.status.hero.battleAnimationCompleted = false;

					// 优化：缓存下拉框内容，避免每帧重复计算文字宽度
					let _cachedEnterWidth = null;

					function animateBattleEnd(currentTime) {
						if (!lastTime) lastTime = currentTime;

						const deltaTime = currentTime - lastTime;

						// 下拉框动画
						if (ch <= targetHeight) {
							const progress = Math.min(deltaTime / animationDuration, 1);
							ch += 32 * progress; // 平滑过渡到目标高度

							core.resizeCanvas("__battle__", width, height + ch);
							ctx.putImageData(data, 0, 0);

							// 绘制下拉框内容
							core.fillBoldText(ctx, "胜利!!", 10, height + 23 + lineWidth / 2, "white", ["white", 0.5], "bold 28px WenDingGuangGaoTi");
							core.fillBoldText(ctx, "经验值：" + enemy.exp, 105, height + 20 + lineWidth / 2, "white", ["white", 0.5], "bold 17px WenDingGuangGaoTi");
							core.fillBoldText(ctx, "金币：" + enemy.money, 255, height + 20 + lineWidth / 2, "white", ["white", 0.5], "bold 17px WenDingGuangGaoTi");

							// 绘制边框
							core.drawLine(ctx, lineWidth / 2, height + ch, width - lineWidth / 2, height + ch, "#0099CC", lineWidth);
							core.drawLine(ctx, lineWidth / 2, height, lineWidth / 2, height + ch, "#0099CC", lineWidth);
							core.drawLine(ctx, width - lineWidth / 2, height, width - lineWidth / 2, height + ch, "#0099CC", lineWidth);
						}

						// 渐变闪烁文字动画（当下拉框完全展开后）
						if (ch >= targetHeight) {
							// 设置动画完成标志
							if (!core.status.hero.battleAnimationCompleted) {
								core.status.hero.battleAnimationCompleted = true;
							}

							// 使用正弦函数创建平滑的透明度渐变
							const elapsedTime = currentTime % blinkInterval;
							const progress = elapsedTime / blinkInterval;

							// 正弦波：从0到1再到0的平滑变化
							const alpha = 0.3 + 0.7 * Math.sin(progress * Math.PI);

							core.setAlpha(ctx, alpha);

							// 缓存文字宽度，避免每帧重新计算
							if (_cachedEnterWidth == null) {
								_cachedEnterWidth = core.calWidth(ctx, "-Enter-", "16px 布丁体");
							}
							const w = _cachedEnterWidth;
							core.clearMap(ctx, bx2 - 1, ty + offsetY - 14, w + 3, 16);
							core.fillText(ctx, "-Enter-", bx2, ty + offsetY, "white", "16px 布丁体");
							core.setAlpha(ctx, 1);

							// 检查战斗结束状态
							if (core.status.hero.battleStatus == "end") {
								battleEnd();
								core.battle_exit();
								return; // 停止动画
							}
						}

						lastTime = currentTime;

						// 继续动画
						if (core.status.hero.battleStatus != "end") {
							requestAnimationFrame(animateBattleEnd);
						}
					}

					// 初始绘制闪烁文字
					core.fillText(ctx, "-Enter-", bx2, ty + offsetY, "white", "16px 布丁体");

					// 开始动画
					requestAnimationFrame(animateBattleEnd);
				}
			}
		},

		battle_exit: function () {
			core.status.hero.battle_enemy = null;
			core.status.hero.battleStatus = null;
			core.status.hero.battle_triggerWeak = null;
			core.status.hero.battle_triggerPoison = null;
			core.status.hero.battle_randNum = null;
			core.clearMap("__battle__");
			core.deleteCanvas("__battle__");
			core.unlockControl();
			delete flags.__callback__;
			delete flags.__loc__;
		},

		battle_missAnimate: function (name) {
			core.playSound("miss.mp3");
			if (flags.inMiss) return;
			if (core.isReplaying()) return;

			var info = core.plugin.battleInfo;
			var width = info.width,
				height = info.height,
				left = info.left,
				top = info.top,
				bx = info.bx,
				by = info.by,
				lineWidth = info.lineWidth,
				size = info.size;

			var ctx = core.createCanvas("__battleAnimate__", left, top, width, height, 70);
			ctx.textBaseline = "bottom";

			var x, textAlign;
			if (name == "hero") {
				x = width - 3;
				textAlign = "right";
			} else {
				x = lineWidth + 3;
				textAlign = "left";
			}

			// 初始绘制位置（对应 XML 中 centerPoint3DY ≈ 13.95 的位置）
			var baseY = by + size + lineWidth;

			// 从 XML 提取的动画参数
			var totalFrames = 6; // 帧 1~6
			var interval = battleModule.getBattleInterval(10); // 单帧约 20ms
			var startAlpha = 1.0;
			var endAlpha = 0.30078; // 最后一帧的 alphaMultiplier
			var floatDistance = 6; // 向上移动 6 像素（ty: 0 → -6）

			var currentFrame = 0;
			flags.inMiss = true;

			var animate = setInterval(function () {
				if (currentFrame > totalFrames + 2) {
					// 多停留 2 帧后清除
					clearInterval(animate);
					delete core.animateFrame.asyncId[animate];
					flags.inMiss = false;
					core.clearMap("__battleAnimate__");
					core.deleteCanvas("__battleAnimate__");
					return;
				}

				core.clearMap(ctx);

				var progress = Math.min(currentFrame / totalFrames, 1.0);
				// 线性插值（XML 中 alpha 和位移基本是均匀变化）
				var alpha = startAlpha + (endAlpha - startAlpha) * progress;
				var offsetY = floatDistance * progress;

				// 绘制位置：向上偏移
				var drawY = baseY - offsetY;

				// 设置透明度后绘制红色文字
				ctx.globalAlpha = alpha;
				ctx.textAlign = textAlign;
				core.fillBoldText(ctx, "MISS!!", x, drawY, "rgb(228,0,0)", "rgb(228,0,0)", "30px WenDingGuangGaoTi");
				ctx.globalAlpha = 1.0; // 恢复，避免影响其他绘制

				currentFrame++;
			}, interval);

			core.animateFrame.asyncId[animate] = true;
		},



		// 重构后的伤害动画函数
		battle_damageAnimate: function (name, damage) {
			// ========================
			// 从 Symbol 909.xml 提取的数据（帧 0 ~ 18）
			// 基准：第 0 帧的 (tx, ty) = (-24, 3) 作为零点
			// 故实际偏移 = (当前帧 tx - (-24), 当前帧 ty - 3)
			// ========================
			const DAMAGE_ANIM_DATA = [
				// 帧 0
				{ dx: 0.0, dy: 0.0, scale: 1.0000 },
				// 帧 1
				{ dx: 3.75, dy: -14.1, scale: 1.0725 },
				// 帧 2
				{ dx: 10.55, dy: -18.65, scale: 1.1392 },
				// 帧 3
				{ dx: 11.2, dy: -6.4, scale: 1.2001 },
				// 帧 4
				{ dx: 11.15, dy: 4.7, scale: 1.2552 },
				// 帧 5
				{ dx: 12.9, dy: 3.8, scale: 1.3045 },
				// 帧 6
				{ dx: 16.6, dy: 10.7, scale: 1.3480 },
				// 帧 7
				{ dx: 16.65, dy: 15.75, scale: 1.3857 },
				// 帧 8
				{ dx: 20.1, dy: 11.45, scale: 1.4176 },
				// 帧 9
				{ dx: 20.75, dy: 16.7, scale: 1.4437 },
				// 帧 10
				{ dx: 20.9, dy: 20.75, scale: 1.4640 },
				// 帧 11
				{ dx: 21.0, dy: 23.65, scale: 1.4785 },
				// 帧 12
				{ dx: 21.0, dy: 25.4, scale: 1.4872 },
				// 帧 13 ~ 18（数据相同）
				{ dx: 21.0, dy: 26.0, scale: 1.4901 },
				{ dx: 21.0, dy: 26.0, scale: 1.4901 },
				{ dx: 21.0, dy: 26.0, scale: 1.4901 },
				{ dx: 21.0, dy: 26.0, scale: 1.4901 },
				{ dx: 21.0, dy: 26.0, scale: 1.4901 },
				{ dx: 21.0, dy: 26.0, scale: 1.4901 } // 帧 18
			];

			const TOTAL_FRAMES = DAMAGE_ANIM_DATA.length; // = 19
			damage = parseInt(damage);
			if (core.isReplaying()) return;

			var info = core.plugin.battleInfo;
			var width = info.width,
				height = info.height,
				left = info.left,
				top = info.top,
				bx = info.bx,
				by = info.by,
				size = info.size;

			// 确定数字的基准绘制点（与原始逻辑一致）
			if (name != "hero") bx = width - bx - size;
			var baseX = bx + 5,
				baseY = by + size - 10;

			var ctx = core.createCanvas("__battleDamage__", left, top, width, height, 71);
			ctx.textBaseline = "middle";
			ctx.textAlign = "center";

			var isHeal = damage < 0;
			var valueText = (isHeal ? "+" : "") + Math.abs(damage);
			var baseFontSize = 18; // 初始字号（px）

			var currentFrame = 0;
			var interval = battleModule.getBattleInterval(10);

			var animate = setInterval(function () {
				if (currentFrame < TOTAL_FRAMES) {
					// 获取当前帧数据
					var data = DAMAGE_ANIM_DATA[currentFrame];
					// 计算实际绘制位置（基准点 + XML 偏移）
					var x = baseX + data.dx;
					var y = baseY + data.dy;
					// 计算字体大小（原始字号 × 缩放系数）
					var fontSize = Math.round(baseFontSize * data.scale);
					// 计算颜色：按帧进度从白渐变到纯色（伤害红，治疗绿）
					var t = currentFrame / (TOTAL_FRAMES - 1); // 0 → 1
					var r, g, b;
					if (isHeal) {
						// 治疗：白 → 绿 (0,255,0)
						r = Math.round(255 * (1 - t));
						g = 255;
						b = Math.round(255 * (1 - t));
					} else {
						// 伤害：白 → 红 (255,0,0)
						r = 255;
						g = Math.round(255 * (1 - t));
						b = Math.round(255 * (1 - t));
					}
					var color = "rgb(" + r + "," + g + "," + b + ")";
					var font = "bold " + fontSize + "px myFont";

					// 清空画布并绘制新数字
					core.clearMap(ctx);
					core.fillText(ctx, valueText, x, y, color, font);

					currentFrame++;
				} else {
					clearInterval(animate);
					delete core.animateFrame.asyncId[animate];
					core.clearMap("__battleDamage__");
				}
			}, interval);

			core.animateFrame.asyncId[animate] = true;
		},


		/**
		 * !!! 注意：此处替换了 core.rand()
		 * [MODIFIED] 添加了对rand() == -1的检查
		 * [MODIFIED_V2] 添加了对战斗状态的检查
		 */
		battle_enemyAttackOnce: function (hero, enemy) {
			if (core.status.hero.battleStatus !== "fighting") {
				return { "retreated": true }; // [MODIFIED_V2] 战斗已结束，返回停止信号
			}
			if (!hero) hero = core.status.hero;
			// 回合前动画处理

			var animate = enemy.animate || "hand";
			var originAnimate = animate;

			var hero_def = core.getRealStatusOrDefault(hero, "def");
			var hero_atk = core.getRealStatusOrDefault(hero, "atk");
			var hero_agi = core.getRealStatusOrDefault(hero, "agi");
			var hero_natk = core.getRealStatusOrDefault(hero, "atkCritical");
			var hero_ndef = core.getRealStatusOrDefault(hero, "defCritical");

			// 伤害计算
			var mon_hp = enemy.hp,
				mon_atk = enemy.atk,
				mon_def = enemy.def,
				mon_cri = enemy.cri,
				mon_agi = enemy.agi,
				mon_special = enemy.special;

			var sageLv = core.getFlag('proof_sage', 0);
			var tyrantLv = core.getFlag('proof_tyrant', 0);
			var braveLv = core.getFlag('proof_brave', 0);

			// 是否闪避成功
			var dodge = false;
			var isCri = false;
			var isRecovery = 0;

			// 使用数组记录动画
			var animations = [];

			var per_damage = 0;
			var recovery = 0;
			var reflectDamage = 0;

			var r = rand(100); // [MODIFIED] 存储rand结果
			if (r === -1) return { "retreated": true }; // [MODIFIED] 检查撤退信号
			if (r <= hero_agi) { // <--- 已替换
				// 闪避成功
				per_damage = 0;
				if (sageLv > 0) {
					var r = rand(100);
					if (r === -1) return { "retreated": true };
					if (r <= [60, 80, 99][sageLv - 1]) {
						isRecovery = true;
						recovery += Math.round(enemy.atk / 5);
						if (core.status.hero.hp < 300) recovery += sageLv * 100;
						//animations.push("贤者回复");
					} else {
						dodge = true;
					}
				} else {
					dodge = true;
				}
			} else if (core.hasSpecial(mon_special, 30)) {
				per_damage = mon_atk;
				var r = rand(100);
				if (r === -1) return { "retreated": true };
				if (sageLv === 3 && r < 21) {
					// 魔法集能
					let rhp = rand(20) + enemy.atk;
					if (rhp === -1) return { "retreated": true };
					flags.sagePower = (flags.sagePower || 0) + rhp;
					flags.sageEnergy = (flags.sageEnergy || 0) + 1;
					per_damage -= 100;
					animations.push("贤者集能");
				}
				var r = rand(100);
				if (r === -1) return { "retreated": true };
				if (r <= mon_cri) {
					per_damage *= 2;
					isCri = true;
				}
			} else {
				// 先尝试霸者反弹触发（不成功时继续走后续正常伤害计算）
				var tyrantReflected = false;
				if (tyrantLv > 0) {
					var r = rand(100);
					if (r === -1) return { "retreated": true };
					if (r <= [5, 10, 15][tyrantLv - 1]) {
						if (tyrantLv === 3 && core.hasSpecial(mon_special, 32)) {
							// BOSS Ⅰ型：双方不受伤
							per_damage = 0;
							reflectDamage = 0;
							animations.push("霸者反弹");
							tyrantReflected = true;
						} else {
							// 以基础伤害计算反弹量
							var baseDamage = 0;
							if (mon_atk >= hero_def - hero_ndef) baseDamage = Math.max(mon_atk - hero_def, 1);
							reflectDamage = [Math.round(baseDamage / 2), baseDamage, Math.round(enemy.atk / 3)][tyrantLv - 1];
							if (reflectDamage >= enemy.hp) {
								// 直接杀死怪物：英雄不受伤
								per_damage = 0;
								animations.push("霸者反弹");
								tyrantReflected = true;
							} else {
								// 英雄承受缩减后的伤害
								per_damage = [baseDamage, Math.round(baseDamage / 2), Math.round(baseDamage / 3)][tyrantLv - 1];
								animations.push("霸者反弹");
								tyrantReflected = true;
							}
						}
					}
				}

				// 若未发生反弹，则继续正常伤害流程（包含霸体/贤者集能/暴击/秒杀）
				if (!tyrantReflected) {
					if (tyrantLv >= 2) {
						var r = rand(100);
						if (r === -1) return { "retreated": true };
						if (r <= (tyrantLv === 2 ? 10 : 15)) {
							hero_def *= 2;
							animations.push("霸者霸体");
						}
					}
					if (mon_atk >= hero_def - hero_ndef) {
						per_damage = Math.max(mon_atk - hero_def, 1);
						var r = rand(100);
						if (r === -1) return { "retreated": true };
						var chance = 0;
						if (sageLv === 2) chance = 11;
						else if (sageLv === 3) chance = 31;
						if (chance > 0 && r < chance) {
							let rhp = rand(30);
							if (rhp === -1) return { "retreated": true };
							flags.sagePower = (flags.sagePower || 0) + rhp;
							flags.sageEnergy = (flags.sageEnergy || 0) + 1;
							per_damage -= 50;
							animations.push("贤者集能");
						}
						var r = rand(100);
						if (r === -1) return { "retreated": true };
						if (r <= mon_cri) {
							per_damage *= 2;
							isCri = true;
						}
					} else {
						if (!dodge && !core.hasSpecial(mon_special, 30) && !tyrantReflected && mon_atk + hero_ndef < hero_def && hero_atk > mon_def && !animations.includes("霸者霸体")) {
							var r = rand(100);
							if (r === -1) return { "retreated": true };
							if (r <= 15) {
								animate = '秒杀';

								return {
									"per_damage": 0,
									"recovery": 0,
									"reflectDamage": 0,
									"animate": animate,
									"animations": [] // 返回动画数组
								};
							}
						}
					}
				}
			}
			if (isRecovery) animate = "贤者回复";
			else if (dodge) animate = "miss";
			else if (per_damage == 0)
				animate = "block";
			else animate = animate + (isCri ? "C" : "N");

			return {
				"per_damage": per_damage,
				"recovery": recovery,
				"reflectDamage": reflectDamage,
				"animate": animate,
				"animations": animations // 返回动画数组
			};
		},

		battle_enemyAttack: function () {
			function enemyAttackTimes(enemy) {
				mon_special = enemy.special;
				// 二连击
				if (core.hasSpecial(mon_special, 4)) return 2;
				// 三连击
				if (core.hasSpecial(mon_special, 5)) return 3;
				// n连击
				if (core.hasSpecial(mon_special, 6)) return enemy.n || 4;
				return 1;
			}

			if (core.status.hero.battleStatus !== "fighting") {
				return; // [MODIFIED_V2] 战斗已结束(或即将结束)，立刻返回
			}
			if (core.status.hero.battleAnimate_enemy == null) core.status.hero.battleAnimate_enemy = "hand";
			var info = core.plugin.battleInfo;
			var width = info.width,
				height = info.height,
				left = info.left,
				top = info.top,
				bx = info.bx,
				by = info.by,
				size = info.size;
			var bx2 = width - bx - size;

			// 回合开始
			var enemy = core.status.hero.battle_enemy;
			core.status.hero.battle_enemyTurn++;

			var times = enemyAttackTimes(enemy);
			var attackTimes = 0;

			// 回合结束 执行该回合战斗
			var heroBox_x = (bx2 + left + size / 2) / 32 - 0.5,
				heroBox_y = (by + top + size / 2 - 4) / 32 - 0.5;
			var enemyBox_x = (bx + left + size / 2) / 32 - 0.5,
				enemyBox_y = (by + top + size / 2 - 4) / 32 - 0.5;
			// 一回合攻击times次
			while (times > 0) {
				var todo = [];
				attackTimes++;
				var info = core.battle_enemyAttackOnce(null, enemy);
				if (info.retreated) return; // [MODIFIED] 检查撤退信号

				var per_damage = info.per_damage,
					recovery = info.recovery,
					reflectDamage = info.reflectDamage,
					animate = info.animate;
				animations = info.animations;
				if (animate === '秒杀') {
					// 原有秒杀动画
					todo.push({ "type": "animate", "name": '秒杀动画1', "loc": [heroBox_x, heroBox_y] });
					todo.push({ "type": "animate", "name": '秒杀动画2', "loc": [heroBox_x, heroBox_y] });
					if (core.getFlag("proof_brave", 0) > 0) {
						todo.push({ "type": "animate", "name": '勇者秒杀', "loc": [enemyBox_x, enemyBox_y] });
					} else if (core.getFlag("proof_sage", 0) > 0) {
						todo.push({ "type": "animate", "name": '贤者秒杀', "loc": [enemyBox_x, enemyBox_y] });
					} else if (core.getFlag("proof_tyrant", 0) > 0) {
						todo.push({ "type": "animate", "name": '霸者秒杀1', "loc": [heroBox_x, heroBox_y] });
						todo.push({ "type": "animate", "name": '霸者秒杀2', "loc": [enemyBox_x, enemyBox_y] });
					} else {
						todo.push({ "type": "animate", "name": '无章秒杀1', "loc": [enemyBox_x, enemyBox_y] });
						todo.push({ "type": "animate", "name": '无章秒杀2', "loc": [enemyBox_x, enemyBox_y] });
					}

					// 等待所有动画完成
					todo.push({ "type": "waitAsync" });
					todo.push({
						"type": "function",
						"function": "function(){core.battle_damageAnimate('hero', " + core.status.hero.battle_enemy.hp + ")}"
					});
					todo.push({ "type": "waitAsync" });

					// 立即杀死敌人并刷新界面
					todo.push({
						"type": "function",
						"function": "function(){ core.status.hero.battle_enemy.hp = 0; core.battle_battling_draw(null, core.status.hero.battle_enemy); }"
					});

					core.insertAction(todo);
					return; // 跳出本次攻击，不再执行后续通用伤害逻辑
				} else {
					if (animate === '贤者回复') {
						todo.push({
							"type": "function",
							"function": "function(){core.battle_damageAnimate( null," + -recovery + ")}"

						});
						core.status.hero.hp += recovery;
						todo.push({ "type": "animate", "name": animate, "async": true, "loc": [heroBox_x, heroBox_y] });
					} else if (animate == "miss") {
						todo.push({
							"type": "function",
							"function": "function(){\ncore.battle_missAnimate(\"hero\");\n}"
						});
					} else if (animate == "block") {
						var sageLv = core.getFlag('proof_sage', 0);
						var tyrantLv = core.getFlag('proof_tyrant', 0);
						var braveLv = core.getFlag('proof_brave', 0);
						let name;
						if (sageLv > 0) name = '贤者受击无伤';
						else if (tyrantLv > 0) name = '霸者受击无伤';
						else if (braveLv > 0) name = '勇者受击无伤';
						else name = '无章受击无伤';
						todo.push({
							"type": "animate",
							"name": name,
							"async": true,
							"loc": [heroBox_x, heroBox_y]
						});
					} else {
						todo.push({
							"type": "animate",
							"name": animate,
							"async": true,
							"loc": [heroBox_x, heroBox_y]
						});
					}

					// 三个章 贤者sage 勇者brave 霸者tyrant
					// 啊这 地狱般的ifelse 我自己也看不懂了 // yzm：我更看不懂了，修你猫猫头，不修了
					// 翻新：都没了，不用修了！
					animations.forEach((animate) => {
						if (animate === '霸者反弹') {
							todo.push({ "type": "animate", "name": 'mega', "async": true, "loc": [heroBox_x, heroBox_y] });
							todo.push({
								"type": "function",
								"function": "function(){core.battle_damageAnimate('hero', " + reflectDamage + ")}"
							});
							enemy.hp -= reflectDamage;
							todo.push({ "type": "animate", "name": animate, "async": true, "loc": [enemyBox_x, enemyBox_y] });
							todo.push({ "type": "waitAsync" });
						} else
							todo.push({ "type": "animate", "name": animate, "async": true, "loc": [heroBox_x, heroBox_y] });
					});
				}


				var funcBody = `
            core.status.hero.hp -= ${per_damage};
            core.battle_battling_draw(null, core.status.hero.battle_enemy);
            if (core.status.hero.hp <= 0) {
              if (core.hasItem("cross")){
                core.insertAction({ "type": "animate", "name": '复活', "async": true, "loc": [${heroBox_x}, ${heroBox_y}] });
                core.status.hero.hp = 2000;
                core.addItem("cross", -1);
              }
            }
          `;

				if (animate !== "miss" && animate !== '贤者回复') {
					funcBody += "core.battle_damageAnimate(null, " + per_damage + ");";
				}

				todo.push({
					"type": "function",
					"function": `function(){${funcBody}}`
				});

				times--;
				// 毒衰(随机)
				if (enemy.poisonPercent && !core.status.hero.battle_triggerPoison) {
					var r = rand(100); // [MODIFIED]
					if (r === -1) return; // [MODIFIED]
					if (r < enemy.poisonPercent) { // <--- 已替换
						core.status.hero.battle_triggerPoison = true;
					}
				}
				if (enemy.weakPercent && !core.status.hero.battle_triggerWeak) {
					var r = rand(100); // [MODIFIED]
					if (r === -1) return; // [MODIFIED]
					if (r < enemy.weakPercent) { // <--- 已替换
						core.status.hero.battle_triggerWeak = true;
					}
				}
				if (todo.length > 0) {
					todo.push({
						"type": "waitAsync"
					}, {
						"type": "function",
						"function": `function() {
                    core.battle_battling_draw(null, core.status.hero.battle_enemy);
                    if (core.status.hero.hp <= 0) {
                        core.status.hero.hp = 0;
									core.lose();
                    }
                }`
					});
				}
				core.insertAction(todo);
			}
		},

		/**
		 * !!! 注意：此处替换了 core.rand()
		 * [MODIFIED] 添加了对rand() == -1的检查
		 * [MODIFIED_V2] 添加了对战斗状态的检查
		 */
		battle_heroAttackOnce: function (hero, enemy) {
			if (core.status.hero.battleStatus !== "fighting") {
				return { "retreated": true }; // [MODIFIED_V2] 战斗已结束，返回停止信号
			}
			if (!hero) hero = core.status.hero;

			// 使用数组记录动画
			var animations = [];

			// 动画
			var animate = core.status.hero.battleAnimate;
			var originAnimate = animate;
			// 勇士属性
			var hero_agi = core.getRealStatusOrDefault(hero, "agi"),
				hero_cri = core.getRealStatusOrDefault(hero, "cri"),
				hero_atk = core.getRealStatusOrDefault(hero, "atk"),
				hero_def = core.getRealStatusOrDefault(hero, "def"),
				hero_natk = core.getRealStatusOrDefault(hero, "atkCritical");
			hero_ndef = core.getRealStatusOrDefault(hero, "defCritical");

			// 读取章等级
			var sageLv = core.getFlag('proof_sage', 0);
			var tyrantLv = core.getFlag('proof_tyrant', 0);
			var braveLv = core.getFlag('proof_brave', 0);

			var mon_atk = enemy.atk,
				mon_def = enemy.def,
				mon_cri = enemy.cri,
				mon_agi = enemy.agi,
				mon_special = enemy.special;

			var hero_per_damage = 0;
			var extraDamage = 0;

			// 魔攻
			if (core.hasSpecial(mon_special, 30) && core.hasItem('magic_amulet') && !core.hasSpecial(mon_special, 37))
				mon_def -= Math.max(mon_def, Math.round(hero_def / 3));

			// 是否闪避成功
			var dodge = false;
			var crit = false;
			var extra = false;
			var breakArmor = false;

			var r = rand(100); // [MODIFIED] 存储rand结果
			if (r === -1) return { "retreated": true }; // [MODIFIED] 检查撤退信号
			if (r <= mon_agi) { // <--- 已替换
				hero_per_damage = 0;
				dodge = true;
			} else {
				if (hero_atk + hero_natk >= mon_def) {
					hero_per_damage = Math.max(hero_atk - mon_def, 1);
				}

				// --- 修改开始：霸者Lv3 的“破防” ---
				// BOSS Ⅱ 型（special 33）免疫破防
				if (tyrantLv === 3 && !core.hasSpecial(mon_special, 33)) {
					var r_break = rand(100);
					if (r_break === -1) return;
					if (r_break < 11) {
						mon_def -= Math.round(mon_def / 1.3);
						hero_per_damage = Math.max(hero_atk - mon_def, 1); // 重新计算伤害
						animations.push("霸者破防");
					}
				}

				// 勇者Lv3的附加伤害
				if (braveLv === 3 && hero_per_damage > 0 && hero_def < mon_atk && !core.hasSpecial(mon_special, 31)) {
					var r_extra = rand(100);
					if (r_extra === -1) return { "retreated": true };
					if (r_extra < 16) { // 16% 概率
						extraDamage = Math.round(hero_def / 4); // 主角防御力/4
						hero_per_damage += extraDamage;
						animations.push("勇者攻击附伤");
					}
				}
				r = rand(100);
				if (r === -1) return { "retreated": true };
				if (r <= hero_cri) {
					hero_per_damage *= 2;
					crit = true;
				}
			}

			if (dodge) animate = "miss";
			else if (hero_per_damage == 0) animate = "block";
			else animate = animate + (crit ? "C" : "N");

			return {
				"mon_atk": mon_atk,
				"extraDamage": extraDamage,
				"hero_per_damage": hero_per_damage,
				"animate": animate,
				"animations": animations
			};
		},


		/**
		 * [MODIFIED] 添加了对battle_heroAttackOnce返回值的检查
		 * [MODIFIED_V2] 添加了对战斗状态的检查
		 */
		battle_heroAttack: function () {
			if (core.status.hero.battleStatus !== "fighting") {
				return; // [MODIFIED_V2] 战斗已结束(或即将结束)，立刻返回
			}
			core.status.hero.battleAnimate = core.getFlag('battleAnimate', 'hand');
			// 回合开始 设置战斗动画
			var enemy = core.status.hero.battle_enemy;
			var info = core.plugin.battleInfo;
			var width = info.width,
				height = info.height,
				left = info.left,
				top = info.top,
				bx = info.bx,
				by = info.by,
				size = info.size;
			var bx2 = width - bx - size;
			core.status.hero.battle_heroTurn++;

			var hero = core.status.hero;
			var braveLv = core.getFlag('proof_brave', 0);
			var times = core.getRealStatusOrDefault(hero, 'atkTimes');

			if (braveLv >= 2) times = 2;
			var attackTimes = 0;
			var heroBox_x = (bx2 + left + size / 2) / 32 - 0.5,
				heroBox_y = (by + top + size / 2 - 4) / 32 - 0.5;
			var enemyBox_x = (bx + left + size / 2) / 32 - 0.5,
				enemyBox_y = (by + top + size / 2 - 4) / 32 - 0.5;
			// 回合结束 执行该回合战斗
			while (times > 0) {
				var todo = [];
				attackTimes++;
				var info = core.battle_heroAttackOnce(null, enemy);
				if (info.retreated) return; // [MODIFIED] 检查撤退信号

				var hero_per_damage = info.hero_per_damage,
					animate = info.animate;
				if (animate == "miss") {
					todo.push({
						"type": "function",
						"function": "function(){core.battle_missAnimate();}"
					});
				} else {
					todo.push({
						"type": "animate",
						"name": animate,
						"async": true,
						"loc": [enemyBox_x, enemyBox_y]
					});
				}

				info.animations.forEach((animate) => {
					if (animate === '勇者攻击附伤')
						todo.push({ "type": "animate", "name": animate, "async": true, "loc": [heroBox_x, heroBox_y] });
					else
						todo.push({ "type": "animate", "name": animate, "async": true, "loc": [enemyBox_x, enemyBox_y] });
				});

				var funcBody = "core.status.hero.battle_enemy.hp = Math.max(0, core.status.hero.battle_enemy.hp - " + hero_per_damage + ");core.battle_battling_draw(null, core.status.hero.battle_enemy);";
				if (animate != "miss") {
					funcBody += "core.battle_damageAnimate('hero', " + hero_per_damage + ");";
				}

				todo.push({
					"type": "function",
					"function": "function(){" + funcBody + "}"
				});

				times--;
				if (todo.length > 0) {
					// 使用 Object.keys 长度检查替代 core.same 递归比较
					todo.push({
						"type": "while",
						"condition": "Object.keys(core.animateFrame.asyncId).length>0",
						"data": [{
							"type": "sleep",
							"time": battleModule.getBattleInterval(10)
						}, ],
					}, {
						"type": "waitAsync"
					}, {
						"type": "function",
						"function": "function(){core.battle_battling_draw(null, core.status.hero.battle_enemy);}"
					});
					core.insertAction(todo);
				}
			}
		}
	};

	// =========================================================================
	// VI. 注册与执行 (Registration & Execution)
	// =========================================================================

	// 1. 挂载核心钩子 (MotaJS 会自动查找)
	this._afterLoadResources = hooksModule._afterLoadResources;
	this.afterLoadData = hooksModule.afterLoadData;
	this.updateFg = hooksModule.updateFg;

	// 2. 挂载UI处理
	uiModule.init(); // 注册DOM事件
	this.sys_onStatusBarClick = uiModule.sys_onStatusBarClick;
	this.sys_onToolBarClick = uiModule.sys_onToolBarClick;
	this.onToolBarClick = uiModule.onToolBarClick;

	// 3. 挂载物品逻辑 (MotaJS 会自动查找 useXXX)
	this.useEquipment = itemModule.useEquipment;
	this._useEquipment = itemModule._useEquipment; // 辅助函数
	this.useProof = itemModule.useProof; // 重构后的
	this.useCross = itemModule.useCross; // 战斗中调用

	// 4. 挂载战斗逻辑 (MotaJS 会自动查找 battle_XXX)
	core.plugin.battleInfo = battleModule.battleInfo; // 挂载战斗配置
	this.battle_drawUI = battleModule.battle_drawUI;
	this.battle_battling_draw = battleModule.battle_battling_draw;
	this.battle_enemyAttackOnce = battleModule.battle_enemyAttackOnce;
	this.battle_heroAttackOnce = battleModule.battle_heroAttackOnce;
	this.battle_exit = battleModule.battle_exit;
	this.battle_missAnimate = battleModule.battle_missAnimate;
	this.battle_damageAnimate = battleModule.battle_damageAnimate;
	this.battle_heroAttack = battleModule.battle_heroAttack;
	this.battle_enemyAttack = battleModule.battle_enemyAttack;

	// 5. 执行核心覆盖
	battleModule.init_battle_hooks(); // 注册战斗钩子和覆盖
	hooksModule.override_loadMusic();
},
    "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": "function", "function": "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', false) }" },
						],
						"false": [
							// 不能访问的情况下：检测能否预览
							{
								"type": "if",
								"condition": shop.disablePreview,
								"true": [
									// 不可预览，提示并退出
									{ "type": "playSound", "name": "操作失败" },
									"当前无法访问该商店！",
									{ "type": "break" },
								],
								"false": [
									// 可以预览：将商店全部内容进行替换
									{ "type": "tip", "text": "当前处于预览模式，不可购买" },
									{ "type": "function", "function": "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', true) }" },
								]
							}
						]
					}
				]
			},
			{ "type": "function", "function": "function() {core.addFlag('@temp@shop', -1);}" }
		];
	}

	this._convertShop_replaceChoices = function (shopId, previewMode) {
		var shop = core.status.shops[shopId];
		var choices = (shop.choices || []).filter(function (choice) {
			if (choice.condition == null || choice.condition == '') return true;
			try { return core.calValue(choice.condition); } catch (e) { return true; }
		}).map(function (choice) {
			var ableToBuy = core.calValue(choice.need);
			return {
				"text": choice.text,
				"icon": choice.icon,
				"color": ableToBuy && !previewMode ? choice.color : [153, 153, 153, 1],
				"action": ableToBuy && !previewMode ? [{ "type": "playSound", "name": "商店" }].concat(choice.action) : [
					{ "type": "playSound", "name": "操作失败" },
					{ "type": "tip", "text": previewMode ? "预览模式下不可购买" : "购买条件不足" }
				]
			};
		}).concat({ "text": "离开", "action": [{ "type": "playSound", "name": "取消" }, { "type": "break" }] });
		core.insertAction({ "type": "choices", "text": 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;
	}

	/**
	 * 判定当前是否可以使用快捷商店
	 * @param {string} id - 商店ID（可选）
	 * @returns {string|null} - 返回错误信息字符串，如果可以开启则返回 null
	 */
	this.canUseQuickShop = function (id) {
		// 1. 检查当前楼层是否显式禁用了快捷商店
		if (core.status.thisMap.canUseQuickShop === false) {
			return '当前楼层不能使用快捷商店。';
		}

		// 2. 检查黄金色羽根的使用限制
		if (!core.status.maps[core.status.floorId].canFlyFrom) {
			return '当前楼层不能使用黄金色羽根。';
		}

		// 3. 检查全局瞬移限制
		if (!core.maps._canMoveDirectly_checkGlobal()) {
			return '当前不能瞬移。';
		}

		// 4. 检查异常状态
		if (core.hasFlag('poison')) {
			return '你中毒了，请先解毒。';
		}

		// 5. 检查是否持有必要道具
		if (!core.hasItem('fly')) {
			return '你没有黄金色羽根。';
		}

		var floorId = core.status.floorId;
		var map = core.status.maps[floorId];

		/**
		 * 辅助内部函数：检查某个坐标的四个邻格是否有玩家可瞬移到达的
		 */
		var isAnyNeighborReachable = function (sx, sy) {
			if (sx == null || sy == null) return false;
			for (var direction in core.utils.scan) {
				var delta = core.utils.scan[direction];
				var tx = sx + delta.x;
				var ty = sy + delta.y;
				// 只要有一个邻格能瞬移到达，即视为此点可达
				if (core.canMoveDirectly(tx, ty) >= 0) return true;
			}
			return false;
		};

		var canReachUp = false;
		var canReachDown = false;

		// --- 逻辑 A: 判定上楼点 ---
		// 如果地图属性定义了 upFloor，则只检查这个点的邻域
		if (map.upFloor) {
			canReachUp = isAnyNeighborReachable(map.upFloor[0], map.upFloor[1]);
		} else {
			// 如果没有定义 upFloor 点，则遍历全图寻找 'upFloor' ID 的图块
			var upBlock = (map.blocks || []).find(function (b) {
				return !b.disable && b.event && b.event.id === 'upFloor';
			});
			if (upBlock) canReachUp = isAnyNeighborReachable(upBlock.x, upBlock.y);
		}

		// --- 逻辑 B: 判定下楼点 ---
		// 如果地图属性定义了 downFloor，则只检查这个点的邻域
		if (map.downFloor) {
			canReachDown = isAnyNeighborReachable(map.downFloor[0], map.downFloor[1]);
		} else {
			// 如果没有定义 downFloor 点，则遍历全图寻找 'downFloor' ID 的图块
			var downBlock = (map.blocks || []).find(function (b) {
				return !b.disable && b.event && b.event.id === 'downFloor';
			});
			if (downBlock) canReachDown = isAnyNeighborReachable(downBlock.x, downBlock.y);
		}


		// 只要能到达【上、下】三者中的任意一个邻域，即可使用快捷商店
		if (canReachUp || canReachDown) {
			return null;
		}

		// 如果所有路径都检查失败
		return '无法到达主楼梯，无法使用快捷商店。';
	};

	var _shouldProcessKeyUp = true;

	/**
	 * 核心修改：将执行选项逻辑提取到 core 中，方便 actions.prototype 访问
	 * 这样就不需要模拟点击像素坐标了
	 */
	this._executeChoiceByIndex = function (index, choices) {
		if (index < 0 || index >= choices.length) return false;
		var choice = choices[index];

		// 检查购买条件（如果有 need 字段）
		if (choice.need != null && choice.need != '' && !core.calValue(choice.need)) {
			core.playSound('操作失败');
			core.drawTip("无法选择此项");
			return true;
		}

		// 清除超时计时器
		clearTimeout(core.status.event.interval);
		var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0;
		delete core.status.event.timeout;
		core.setFlag('timeout', timeout);

		// 记录录像与全局商店特殊处理 (如果是全局商店，最后项 index 存为 -1)
		var routeIndex = index;
		if (index == choices.length - 1 && core.hasFlag('@temp@shop')) {
			routeIndex = -1;
		}
		core.status.route.push("choices:" + (100 * timeout + routeIndex));

		// 插入并执行动作
		core.insertAction(choice.action);
		core.doAction();
		return true;
	};

	// --- 商店插件中的快捷键拦截 ---
	var _shouldProcessKeyUp = false;

	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;
		}

		// ========== 支持快捷商店 ==========
		var isShop = core.hasFlag("@temp@shop");
		var isQuickShop = core.status.event.id === "selectShop";
		if ((!isShop && !isQuickShop) || core.status.event.data.type != 'choices') return false;

		var choices = core.status.event.data.current.choices;
		if (keycode == 88 || keycode == 27) { // X, ESC
			return core.plugin._executeChoiceByIndex(choices.length - 1, choices);
		}
		return false;
	}, 60);

	core.registerAction('keyDown', 'shops', function (keycode) {
		// ========== 支持快捷商店 ==========
		var isShop = core.hasFlag("@temp@shop");
		var isQuickShop = core.status.event.id === "selectShop";
		if (!core.status.lockControl || (!isShop && !isQuickShop) || core.status.event.id != 'action') return false;

		if (core.status.event.data.type != 'choices') return false;

		if (keycode == 13 || keycode == 32) { // Space, Enter
			var executed = core.plugin._executeChoiceByIndex(core.status.event.selection, core.status.event.data.current.choices);
			_shouldProcessKeyUp = false;
			return executed;
		}
		return false;
	}, 60);
},
    "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.__PIXELS__, 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();
	}
},
    "函数覆写": function () {
	control.prototype.resize = function () {
		if (main.mode == 'editor') return;
		if (this._resizeDebounce) clearTimeout(this._resizeDebounce);
		this._resizeDebounce = setTimeout(() => {
			var clientWidth = main.dom.body.clientWidth,
				clientHeight = main.dom.body.clientHeight;
			var CANVAS_WIDTH = core.__PIXELS__,
				BAR_WIDTH = Math.round(core.__PIXELS__ * 0.31);
			var BORDER = 3;
			var extendToolbar = core.flags.extendToolbar;
			var isVertical = clientHeight > clientWidth;
			core.domStyle.isVertical = isVertical;

			var maxRatio;
			if (isVertical) {
				// 去掉 Math.min(1, ...)，允许竖屏缩放超过1
				maxRatio = (clientWidth - 2 * BORDER) / CANVAS_WIDTH;
			} else {
				var horizontalMaxRatioByWidth = (clientWidth - 3 * BORDER) / (CANVAS_WIDTH + BAR_WIDTH);
				var horizontalMaxRatioByHeight = (clientHeight - 2 * BORDER - (extendToolbar ? BORDER : 0)) / (CANVAS_WIDTH + (extendToolbar ? 38 : 0));
				maxRatio = Math.min(horizontalMaxRatioByWidth, horizontalMaxRatioByHeight);
			}

			var savedScale = null;
			var storageKey = isVertical ? 'scale_vertical' : 'scale_horizontal';
			var rawSaved = core.getLocalStorage(storageKey);
			if (rawSaved != null) {
				var parsed = parseFloat(rawSaved);
				if (!isNaN(parsed) && parsed > 0 && parsed <= maxRatio) savedScale = parsed;
			}
			if (savedScale == null && !isNaN(core.domStyle.scale) && core.domStyle.scale > 0 && core.domStyle.scale <= maxRatio) {
				savedScale = core.domStyle.scale;
			}

			var scaleOptions = [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5];

			// 竖屏下也用和横屏一样的档位逻辑
			core.domStyle.availableScale = scaleOptions.filter(function (v) {
				return v <= maxRatio;
			});

			if (savedScale != null) {
				core.domStyle.scale = savedScale;
				if (core.domStyle.availableScale.indexOf(savedScale) < 0) {
					core.domStyle.availableScale.push(savedScale);
					core.domStyle.availableScale.sort(function (a, b) { return a - b; });
				}
			} else if (core.domStyle.availableScale.length > 0) {
				// 自适应选择最大可用档位
				core.domStyle.scale = core.domStyle.availableScale[core.domStyle.availableScale.length - 1];
			} else {
				core.domStyle.scale = Math.min(1, maxRatio);
			}

			// 竖屏下仍然不使用扩展工具栏
			if (isVertical) {
				extendToolbar = false;
			}

			var statusDisplayArr = this._shouldDisplayStatus(),
				count = statusDisplayArr.length;
			var statusCanvas = core.flags.statusCanvas,
				statusCanvasRows = core.values.statusCanvasRowsOnMobile || 3;
			var col = statusCanvas ? statusCanvasRows : Math.ceil(count / 3);
			if (col > 5) {
				if (statusCanvas) alert("自绘状态栏的在竖屏下的行数应不超过5！");
				else alert("当前状态栏数目(" + count + ")大于15，请调整到不超过15以避免手机端出现显示问题。");
			}
			var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute;

			var obj = {
				clientWidth: clientWidth,
				clientHeight: clientHeight,
				CANVAS_WIDTH: CANVAS_WIDTH,
				BORDER: BORDER,
				BAR_WIDTH: BAR_WIDTH,
				TOOLBAR_HEIGHT: 38,
				outerSize: CANVAS_WIDTH * core.domStyle.scale + 2 * BORDER,
				globalAttribute: globalAttribute,
				border: '3px ' + core.arrayToRGBA(globalAttribute.borderColor) + ' solid',
				statusDisplayArr: statusDisplayArr,
				count: count,
				col: col,
				statusBarHeightInVertical: core.domStyle.isVertical ? (32 * col + 6) * core.domStyle.scale + 2 * BORDER : 0,
				toolbarHeightInVertical: core.domStyle.isVertical ? 38 * core.domStyle.scale + 2 * BORDER : 0,
				extendToolbar: extendToolbar,
				is15x15: core.__SIZE__ == 15
			};

			this._doResize(obj);
			this.setToolbarButton();
			core.updateStatusBar();
		}, 100);
	};

	control.prototype.setDisplayScale = function (delta) {
		var clientWidth = main.dom.body.clientWidth,
			clientHeight = main.dom.body.clientHeight;
		var isVertical = typeof core.domStyle.isVertical === 'undefined' ? clientHeight > clientWidth : core.domStyle.isVertical;
		var storageKey = isVertical ? 'scale_vertical' : 'scale_horizontal';
		var CANVAS_WIDTH = core.__PIXELS__,
			BAR_WIDTH = Math.round(core.__PIXELS__ * 0.31);
		var BORDER = 3;
		var extendToolbar = core.flags.extendToolbar;
		var maxRatio = isVertical ?
			(clientWidth - 2 * BORDER) / CANVAS_WIDTH // 去除 Math.min(1, ...)
			:
			Math.min(
				(clientWidth - 3 * BORDER) / (CANVAS_WIDTH + BAR_WIDTH),
				(clientHeight - 2 * BORDER - (extendToolbar ? BORDER : 0)) / (CANVAS_WIDTH + (extendToolbar ? 38 : 0))
			);

		var scaleOptions = [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5];
		core.domStyle.availableScale = scaleOptions.filter(function (v) {
			return v <= maxRatio;
		});

		if (!core.domStyle.availableScale || core.domStyle.availableScale.length === 0) {
			core.domStyle.scale = Math.min(1, maxRatio);
			core.setLocalStorage(storageKey, core.domStyle.scale);
			core.setLocalStorage('scale', core.domStyle.scale);
			core.resize();
			return;
		}

		var index = core.domStyle.availableScale.indexOf(core.domStyle.scale);
		if (index < 0) index = 0;
		index = (index + delta + core.domStyle.availableScale.length) % core.domStyle.availableScale.length;
		core.domStyle.scale = core.domStyle.availableScale[index];
		core.setLocalStorage(storageKey, core.domStyle.scale);
		core.setLocalStorage('scale', core.domStyle.scale);
		core.resize();
	};

	control.prototype._replay_SL = function () {
		if (!core.isPlaying() || !core.isReplaying()) return;
		if (!core.status.replay.pausing) {
			core.playSound('操作失败');
			return core.drawTip("请先暂停录像");
		}
		if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
			core.playSound('操作失败');
			return core.drawTip("请等待当前事件的处理结束");
		}
		if (core.hasFlag('__forbidSave__')) {
			core.playSound('操作失败');
			return core.drawTip('当前禁止存档');
		}
		if (this._replay_hideProgress)
			this._replay_hideProgress();

		core.lockControl();
		core.status.event.id = 'save';
		var saveIndex = core.saves.saveIndex;
		var page = parseInt((saveIndex - 1) / 5),
			offset = saveIndex - 5 * page;

		core.ui._drawSLPanel(10 * page + offset);
	}
	core.control.registerReplayAction("shop", core.control._replayAction_shop);



	ui.prototype.createCanvas = function (name, x, y, width, height, z) {
		// 如果画布已存在则直接调用
		if (core.dymCanvas[name]) {
			this.relocateCanvas(name, x, y);
			this.resizeCanvas(name, width, height);
			core.dymCanvas[name].canvas.style.zIndex = z;
			return core.dymCanvas[name];
		}
		var newCanvas = document.createElement("canvas");
		newCanvas.id = name;
		newCanvas.style.display = 'block';
		newCanvas.setAttribute("_left", x);
		newCanvas.setAttribute("_top", y);
		newCanvas.style.width = width * core.domStyle.scale + 'px';
		newCanvas.style.height = height * core.domStyle.scale + 'px';
		newCanvas.style.left = x * core.domStyle.scale + 'px';
		newCanvas.style.top = y * core.domStyle.scale + 'px';
		newCanvas.style.zIndex = z;
		newCanvas.style.position = 'absolute';
		newCanvas.style.pointerEvents = 'none';
		core.dymCanvas[name] = newCanvas.getContext('2d');
		core.maps._setHDCanvasSize(core.dymCanvas[name], width, height);
		core.dom.gameDraw.appendChild(newCanvas);
		return core.dymCanvas[name];
	}

	maps.prototype._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);
		config.ctx = toDrawCtx;

		core.strokeRect(toDrawCtx, 30, 30, 356, 356, "#0099CC", 3);
		core.fillText(toDrawCtx, core.floors[floorId].title, 166, 20, "white", "18px 布丁体");
	}


	ui.prototype._buildFont = function (fontSize, bold, italic, font) {
		var textAttribute = core.status.textAttribute || core.initStatus.textAttribute,
			globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute;
		//if (bold == null) bold = textAttribute.bold;
		bold = false;
		return (bold ? "bold " : "") + (italic ? "italic " : "") + (fontSize || textAttribute.textfont) + "px " + (font || globalAttribute.font);
	}
	control.prototype.checkBlock = function () {
		var x = core.getHeroLoc('x'),
			y = core.getHeroLoc('y'),
			loc = x + "," + y;
		var damage = core.status.checkBlock.damage[loc];
		if (damage) {
			core.status.hero.hp -= damage;
			var text = (Object.keys(core.status.checkBlock.type[loc] || {}).join("，")) || "伤害";
			core.drawTip("受到" + text + damage + "点");
			core.drawHeroAnimate("zone");
			this._checkBlock_disableQuickShop();
			core.status.hero.statistics.extraDamage += damage;
			if (core.status.hero.hp <= 0) {
				if (core.hasItem('cross')) {
					core.addItem('cross', -1);
					core.status.hero.hp = 2000;
					core.insertAction([
						{ "type": "stopAnimate" },
						{ "type": "animate", "name": "revival", "loc": "hero" }
					]);
				} else {
					core.status.hero.hp = 0;
					core.updateStatusBar();
					core.events.lose();
					return;
				}
			} else {
				core.updateStatusBar();
			}
		}
		this._checkBlock_ambush(core.status.checkBlock.ambush[loc]);
		this._checkBlock_repulse(core.status.checkBlock.repulse[loc]);
	}
	////// 能否获胜 //////
	enemys.prototype.canBattle = function (enemy, x, y, floorId) {
		if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
		var damage = this.getDamage(enemy, x, y, floorId);
		return damage != null && damage < core.status.hero.hp + core.itemCount('cross') * 2000;
	}
	////// 系统菜单栏界面时的点击操作 //////
	actions.prototype._clickSettings = function (x, y) {
		if (x < this.CHOICES_LEFT || x > this.CHOICES_RIGHT) return;
		var choices = core.status.event.ui.choices;
		var topIndex = this._getChoicesTopIndex(choices.length);
		if (y >= topIndex && y < topIndex + choices.length) {
			var selection = y - topIndex;
			core.status.event.selection = selection;
			switch (selection) {
			case 0:
				core.plugin.gameIntroduceInGame()
				break;
			case 1:
				core.insertCommonEvent('自动');
				break;
			case 2:
				core.status.event.selection = 0;
				core.playSound('确定');
				core.ui._drawSwitchs();
				break;
			case 3:
				// core.playSound('确定');
				core.ui._drawKeyBoard();
				break;
			case 4:
				// core.playSound('确定');
				core.clearUI();
				core.ui._drawViewMaps();
				break;
			case 5:
				core.status.event.selection = 0;
				core.playSound('确定');
				core.ui._drawNotes();
				break;
			case 6:
				core.status.event.selection = 0;
				core.playSound('确定');
				core.ui._drawSyncSave();
				break;
			case 7:
				core.status.event.selection = 0;
				core.playSound('确定');
				core.ui._drawGameInfo();
				break;
			case 8:
				return core.confirmRestart();
			case 9:
				core.playSound('取消');
				core.ui.closePanel();
				break;
			}
		}
		return;
	}
	ui.prototype._drawSettings = function () {
		core.status.event.id = 'settings';
		this.drawChoices(null, [
			'帮助文档', "便捷功能", "系统设置", "虚拟键盘", "浏览地图", "存档笔记", "同步存档", "游戏信息", "返回标题", "返回游戏"
		]);
	}
	events.prototype._changeFloor_playSound = function () {
		// 播放换层音效
		if (core.hasFlag('__fromLoad__')) // 是否是读档造成的切换
			core.playSound('读档');
		else if (core.hasFlag('__isFlying__')) // 是否是楼传造成的切换
			core.playSound('飞行器');
		/*
		else
			core.playSound('上下楼');
		*/
	}
	events.prototype._sys_getItem = function (data, callback) {
		core.saveAndStopAutomaticRoute();
		this.getItem(data.event.id, 1, data.x, data.y, false, callback);
	}
	/*
	control.prototype.turnHero = function (direction) {
		core.drawTip('此塔没有原地转向功能！')
		return;
		if (direction) {
			core.setHeroLoc('direction', direction);
			core.drawHero();
			core.status.route.push("turn:" + direction);
			return;
		}
		core.setHeroLoc('direction', core.turnDirection(':right'));
		core.drawHero();
		core.status.route.push("turn");
		core.checkRouteFolding();
	}*/
	control.prototype.setToolbarButton = function (useButton) {
		return;
		if (!core.domStyle.showStatusBar) {
			// 隐藏状态栏时检查竖屏
			if (!core.domStyle.isVertical && !core.flags.extendToolbar) {
				for (var i = 0; i < core.dom.tools.length; ++i)
					core.dom.tools[i].style.display = 'none';
				return;
			}
			if (!core.hasFlag('showToolbox')) return;
			else core.dom.tools.hard.style.display = 'block';
		}

		if (useButton == null) useButton = core.domStyle.toolbarBtn;
		if ((!core.domStyle.isVertical && !core.flags.extendToolbar) || core.isReplaying()) useButton = false;
		core.domStyle.toolbarBtn = useButton;

		if (useButton) {
			["book", "fly", "toolbox", "keyboard", "shop", "save", "load", "settings"].forEach(function (t) {
				core.statusBar.image[t].style.display = 'none';
			});
			["btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8"].forEach(function (t) {
				core.statusBar.image[t].style.display = 'block';
			})
			main.statusBar.image.btn8.style.filter = core.getLocalStorage('altKey') ? 'sepia(1) contrast(1.5)' : '';
		} else {
			["btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8"].forEach(function (t) {
				core.statusBar.image[t].style.display = 'none';
			});
			["book", "fly", "toolbox", "save", "load", "settings"].forEach(function (t) {
				core.statusBar.image[t].style.display = 'block';
			});
			core.statusBar.image.keyboard.style.display = core.statusBar.image.shop.style.display = core.domStyle.isVertical || core.flags.extendToolbar ? "block" : "none";
		}
	}
	control.prototype._showStartAnimate_resetDom = function () {
		core.dom.startPanel.style.opacity = 1;
		core.dom.startPanel.style.display = "block";
		core.dom.startTop.style.opacity = 1;
		core.dom.startTop.style.display = "block";
		core.dom.startButtonGroup.style.display = 'none';
		core.dom.startButtons.style.display = 'grid';
		core.dom.levelChooseButtons.style.display = 'none';
		core.status.played = false;
		core.clearStatus();
		core.clearMap('all');
		core.dom.musicBtn.style.display = 'block';
		core.dom.enlargeBtn.style.display = 'block';
		core.setMusicBtn();
		// 重置音量
		core.events.setVolume(1, 0);
		core.updateStatusBar();
	}

	control.prototype.showStatusBar = function () {
		if (main.mode == 'editor') return;
		if (core.domStyle.showStatusBar) return;
		var statusItems = core.dom.status;
		core.domStyle.showStatusBar = true;
		core.removeFlag('hideStatusBar');
		// 显示
		for (var i = 0; i < statusItems.length; ++i)
			statusItems[i].style.opacity = 1;
		this.setToolbarButton(false);
		core.dom.tools.hard.style.display = 'block';
		core.dom.toolBar.style.display = 'none';
	}
},
    "HD": function () {
	maps.prototype._setHDCanvasSize = function (ctx, width, height) {
		if (main.replayChecking) return;
		ctx.setTransform(1, 0, 0, 1, 0, 0);
		var ratio = core.domStyle.ratio;
		//ratio *= devicePixelRatio;
		if (width != null) ctx.canvas.width = width * ratio;
		if (height != null) ctx.canvas.height = height * ratio;
		ctx.scale(ratio, ratio);
		ctx.canvas.setAttribute('isHD', 1);
	}
	ui.prototype.drawTextContent = function (ctx, content, config) {
		if (main.replayChecking) return;
		ctx = core.getContextByName(ctx);
		// 设置默认配置项
		var textAttribute = core.status.textAttribute || core.initStatus.textAttribute;
		var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute;
		config = core.clone(config || {});
		config.left = config.left || 0;
		config.right = config.left + (config.maxWidth == null ? core._PX_ : config.maxWidth);
		config.top = config.top || 0;
		config.color = core.arrayToRGBA(config.color || textAttribute.text);
		if (config.bold == null) config.bold = textAttribute.bold;
		config.italic = config.italic || false;
		config.align = config.align || textAttribute.align || "left";
		config.fontSize = config.fontSize || textAttribute.textfont;
		config.lineHeight = config.lineHeight || (config.fontSize * 1.3);
		config.defaultFont = config.font = config.font || globalAttribute.font;
		config.time = config.time || 0;
		config.letterSpacing = config.letterSpacing == null ? (textAttribute.letterSpacing || 0) : config.letterSpacing;

		config.index = 0;
		config.currcolor = config.color;
		config.currfont = config.fontSize;
		config.lineMargin = Math.max(Math.round(config.fontSize / 4), config.lineHeight - config.fontSize);
		config.topMargin = parseInt(config.lineMargin / 2);
		config.lineMaxHeight = config.lineMargin + config.fontSize;
		config.offsetX = 0;
		config.offsetY = 0;
		config.line = 0;
		config.blocks = [];
		config.isHD = ctx == null || ctx.canvas.hasAttribute('isHD');

		// 创建一个新的临时画布
		var tempCtx = document.createElement('canvas').getContext('2d');
		if (config.isHD && ctx) {
			core.maps._setHDCanvasSize(
				tempCtx,
				ctx.canvas.width,
				ctx.canvas.height
			);
		} else {
			tempCtx.canvas.width = ctx == null ? 1 : ctx.canvas.width;
			tempCtx.canvas.height = ctx == null ? 1 : ctx.canvas.height;
		}
		tempCtx.textBaseline = 'top';
		tempCtx.font = this._buildFont(config.fontSize, config.bold, config.italic, config.font);
		tempCtx.fillStyle = config.color;
		config = this._drawTextContent_draw(ctx, tempCtx, content, config);
		return config;
	}
	// —— 初始化 tempCanvas，只需一次 —— 
	core.bigmap.tempCanvas = (function () {
		if (main.replayChecking) return;
		const canvas = document.createElement('canvas');
		const ctx = canvas.getContext('2d');
		return ctx;
	})();

	// —— 重写 drawThumbnail 绘制到临时小画布 —— 
	core.maps._drawThumbnail_drawTempCanvas = function (floorId, blocks, options) {
		if (main.replayChecking) return;
		let width = core.floors[floorId].width,
			height = core.floors[floorId].height;
		const tempCanvas = core.bigmap.tempCanvas;
		const dpr = core.domStyle.ratio;

		if (main.mode == 'editor') {

			// 如果是大地图模式？
			if (options.all) {
				// 计算比例
				var scale = Math.max(core.__SIZE__ / width, core.__SIZE__ / height);
				tempCanvas.canvas.width = width * 32 * scale;
				tempCanvas.canvas.height = height * 32 * scale;
				tempCanvas.scale(scale, scale);
			} else if (width * height > core.bigmap.threshold) {
				options.v2 = true;
				tempCanvas.canvas.width = core.__PIXELS__;
				tempCanvas.canvas.height = core.__PIXELS__;
				var centerX = options.centerX,
					centerY = options.centerY;
				if (centerX == null) centerX = Math.floor(width / 2);
				if (centerY == null) centerY = Math.floor(height / 2);
				var offsetX = core.clamp(centerX - core.__HALF_SIZE__, 0, width - core.__SIZE__),
					offsetY = core.clamp(centerY - core.__HALF_SIZE__, 0, height - core.__SIZE__);
				tempCanvas.translate(-32 * offsetX, -32 * offsetY);
			} else {
				options.v2 = false;
				tempCanvas.canvas.width = width * 32;
				tempCanvas.canvas.height = height * 32;
			}
			options.ctx = tempCanvas;

			// 地图过大的缩略图不绘制显伤
			if (width * height > core.bigmap.threshold)
				options.damage = false;

			// --- 暂存 flags
			var hasHero = core.status.hero != null,
				flags = null;
			if (options.flags) {
				if (!hasHero) core.status.hero = {};
				flags = core.status.hero.flags;
				core.status.hero.flags = options.flags;
			}

			this._drawThumbnail_realDrawTempCanvas(floorId, blocks, options);

			// --- 恢复 flags
			if (!hasHero) delete core.status.hero;
			else if (flags != null) core.status.hero.flags = flags;
			tempCanvas.setTransform(1, 0, 0, 1, 0, 0);
			return;
		}

		width = 13;
		height = 13;

		// 设置临时画布的物理尺寸
		tempCanvas.canvas.width = width * 32 * dpr;
		tempCanvas.canvas.height = height * 32 * dpr;
		tempCanvas.canvas.style.width = width * 32 + "px";
		tempCanvas.canvas.style.height = height * 32 + "px";

		// 设置高清缩放比例
		tempCanvas.setTransform(dpr, 0, 0, dpr, 0, 0);
		//tempCanvas.imageSmoothingEnabled = false;

		options.v2 = false;
		options.ctx = tempCanvas;



		var hasHero = core.status.hero != null,
			flags = null;
		if (options.flags) {
			if (!hasHero) core.status.hero = {};
			flags = core.status.hero.flags;
			core.status.hero.flags = options.flags;
		}

		core.maps._drawThumbnail_realDrawTempCanvas(floorId, blocks, options);

		if (!hasHero) delete core.status.hero;
		else if (flags != null) core.status.hero.flags = flags;

		tempCanvas.restore();
	}

	// —— 重写 drawThumbnail 绘制到目标 ctx —— 
	core.maps._drawThumbnail_drawToTarget = function (floorId, options) {
		if (main.replayChecking) return;
		const ctx = core.getContextByName(options.ctx);
		if (!ctx) return;

		const tempCanvas = core.bigmap.tempCanvas;
		if (main.mode == 'editor') {

			if (ctx == null) return;
			var x = options.x || 0,
				y = options.y || 0,
				size = options.size || core.__PIXELS__;
			var width = core.floors[floorId].width,
				height = core.floors[floorId].height;
			var centerX = options.centerX,
				centerY = options.centerY;
			if (centerX == null) centerX = Math.floor(width / 2);
			if (centerY == null) centerY = Math.floor(height / 2);


			if (options.all) {
				var tempWidth = tempCanvas.canvas.width,
					tempHeight = tempCanvas.canvas.height;
				// 绘制全景图
				if (tempWidth <= tempHeight) {
					var realHeight = size,
						realWidth = realHeight * tempWidth / tempHeight;
					var side = (size - realWidth) / 2;
					core.fillRect(ctx, x, y, side, realHeight, '#000000');
					core.fillRect(ctx, x + size - side, y, side, realHeight);
					core.drawImage(ctx, tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x + side, y, realWidth, realHeight);
				} else {
					var realWidth = size,
						realHeight = realWidth * tempHeight / tempWidth;
					var side = (size - realHeight) / 2;
					core.fillRect(ctx, x, y, realWidth, side, '#000000');
					core.fillRect(ctx, x, y + size - side, realWidth, side);
					core.drawImage(ctx, tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x, y + side, realWidth, realHeight);
				}
			} else {
				// 只绘制可见窗口
				if (options.v2) {
					core.drawImage(ctx, tempCanvas.canvas, 0, 0, core.__PIXELS__, core.__PIXELS__, x, y, size, size);
				} else {
					var offsetX = core.clamp(centerX - core.__HALF_SIZE__, 0, width - core.__SIZE__),
						offsetY = core.clamp(centerY - core.__HALF_SIZE__, 0, height - core.__SIZE__);
					core.drawImage(ctx, tempCanvas.canvas, offsetX * 32, offsetY * 32, core.__PIXELS__, core.__PIXELS__, x, y, size, size);
				}

			}
			return;
		}


		const dpr = core.domStyle.ratio;

		const srcW = tempCanvas.canvas.width;
		const srcH = tempCanvas.canvas.height;

		const dstX = options.x || 0;
		const dstY = options.y || 0;
		const dstW = options.width || options.size || core.__PIXELS__;
		const dstH = options.height || options.size || core.__PIXELS__;

		ctx.imageSmoothingEnabled = false;

		ctx.drawImage(
			tempCanvas.canvas,
			0, 0, srcW, srcH, // 源画布
			dstX, dstY, dstW, dstH // 目标画布
		);
	};


	ui.prototype._drawTextContent_draw = function (ctx, tempCtx, content, config) {
		if (main.replayChecking) return;
		// Step 1: 绘制到tempCtx上，并记录下图块信息
		while (this._drawTextContent_next(tempCtx, content, config));

		if (ctx == null) return config;

		// Step 2: 从tempCtx绘制到画布上
		config.index = 0;
		var _drawNext = function () {
			if (config.index >= config.blocks.length) return false;
			var block = config.blocks[config.index++];
			if (block != null) {
				const scale = config.isHD ? core.domStyle.ratio : 1;
				core.drawImage(
					ctx,
					tempCtx.canvas,
					block.left * scale,
					block.top * scale,
					block.width * scale,
					block.height * scale,
					config.left + block.left + block.marginLeft,
					config.top + block.top + block.marginTop,
					block.width,
					block.height
				);
			}
			return true;
		}
		if (config.time == 0) {
			while (_drawNext());
		} else {
			clearInterval(core.status.event.interval);
			core.status.event.interval = setInterval(function () {
				if (!_drawNext()) {
					clearInterval(core.status.event.interval);
					core.status.event.interval = null;
				}
			}, config.time);
		}

		return config;
	}


	ui.prototype.drawScrollText = function (content, time, lineHeight, callback) {
		content = core.replaceText(content || "");
		lineHeight = lineHeight || 1.4;
		time = time || 5000;
		this.clearUI();
		var offset = core.status.textAttribute.offset || 15;
		lineHeight *= core.status.textAttribute.textfont;
		var ctx = this._createTextCanvas(content, lineHeight);
		var obj = { align: core.status.textAttribute.align, lineHeight: lineHeight };
		if (obj.align == 'right') obj.left = this.PIXEL - offset;
		else if (obj.align != 'center') obj.left = offset;
		this.drawTextContent(ctx, content, obj);
		this._drawScrollText_animate(ctx, time, callback);
	}

	ui.prototype._createTextCanvas = function (content, lineHeight) {
		var width = this.PIXEL,
			height = 30 + this.getTextContentHeight(content, { lineHeight: lineHeight });

		// 使用 core.createCanvas 创建画布
		var ctx = core.createCanvas('TextCanvas', 0, 0, width, height, 0);
		return ctx;
	}
	// 假设 'ui.prototype' 已经定义
	ui.prototype._drawScrollText_animate = function (ctx, time, callback) {
		// ctx.canvas 是 TextCanvas，它已经被高清缩放
		var physicalHeight = ctx.canvas.height;
		var physicalWidth = ctx.canvas.width;

		// 1. 获取用于缩放的 ratio
		// 假设 core.domStyle.ratio 和 devicePixelRatio 是全局可访问的，
		// 并且它们是 _setHDCanvasSize 中使用的相同的值。
		var ratio = core.domStyle.ratio || 1;

		// 2. 计算 TextCanvas 的“逻辑”尺寸
		// 这是它在屏幕上应该占据的尺寸
		var logicalHeight = physicalHeight / ratio;
		var logicalWidth = physicalWidth / ratio;

		// 3. 获取目标画布 (ui) 的上下文
		// 你的 core.drawImage('ui', ...) 内部可能就是这么做的
		var ui_ctx = core.getContextByName('ui');
		if (!ui_ctx) {
			console.error("无法获取 'ui' 画布的上下文！");
			if (callback) callback();
			return;
		}

		time /= Math.max(core.status.replay.speed, 1);
		var per_pixel = 1; // 每次滚动 1 "逻辑"像素

		// 4. 使用“逻辑”高度来计算总时间
		var per_time = time * per_pixel / (this.PIXEL + logicalHeight);
		var currH = this.PIXEL; // 初始“逻辑”Y坐标

		// 5. 初始绘制
		// 使用 5 参数的 drawImage
		core.clearMap('ui'); // 先清除
		ui_ctx.drawImage(ctx.canvas, 0, currH, logicalWidth, logicalHeight);

		var animate = setInterval(function () {
			core.clearMap('ui');
			currH -= per_pixel;

			// 6. 使用“逻辑”高度进行结束比较
			if (currH < -logicalHeight) {
				delete core.animateFrame.asyncId[animate];
				clearInterval(animate);
				if (callback) callback();
				return;
			}

			// 7. 在循环中同样使用 5 参数的 drawImage
			// (ctx.canvas是源图像, 0, currH是目标逻辑坐标, logicalWidth, logicalHeight是目标逻辑尺寸)
			ui_ctx.drawImage(ctx.canvas, 0, currH, logicalWidth, logicalHeight);

		}, per_time);

		core.animateFrame.lastAsyncId = animate;
		core.animateFrame.asyncId[animate] = callback;
	}
	ui.prototype.drawImage = function (name, image, x, y, w, h, x1, y1, w1, h1, angle, reverse) {
		if (main.replayChecking) return;
		// 检测文件名以 :x, :y, :o 结尾，表示左右翻转，上下翻转和中心翻转
		var ctx = this.getContextByName(name);

		ctx.imageSmoothingEnabled = false;
		ctx.mozImageSmoothingEnabled = false;
		ctx.webkitImageSmoothingEnabled = false;
		ctx.msImageSmoothingEnabled = false;

		if (!ctx) return;
		// var reverse = null;
		if (typeof image == 'string') {
			if (image.endsWith(':x') || image.endsWith(':y') || image.endsWith(':o')) {
				reverse = image.charAt(image.length - 1);
				image = image.substring(0, image.length - 2);
			}
			image = core.getMappedName(image);
			image = core.material.images.images[image];
			if (!image) return;
		}

		var scale = {
			'x': [-1, 1],
			'y': [1, -1],
			'o': [-1, -1]
		};

		// 只能接受2, 4, 8个参数
		if (x != null && y != null) {
			if (w == null || h == null) {
				// 两个参数变成四个参数
				w = image.width;
				h = image.height;
			}
			if (x1 != null && y1 != null && w1 != null && h1 != null) {
				if (!reverse && !angle) {
					ctx.drawImage(image, x, y, w, h, x1, y1, w1, h1);
				} else {
					ctx.save();
					ctx.translate(x1 + w1 / 2, y1 + h1 / 2);
					if (reverse) ctx.scale(scale[reverse][0], scale[reverse][1]);
					if (angle) ctx.rotate(angle);
					ctx.drawImage(image, x, y, w, h, -w1 / 2, -h1 / 2, w1, h1);
					ctx.restore();
				}
				return;
			}
			if (!reverse && !angle) {
				ctx.drawImage(image, x, y, w, h);
			} else {
				ctx.save();
				ctx.translate(x + w / 2, y + h / 2);
				if (reverse) ctx.scale(scale[reverse][0], scale[reverse][1]);
				if (angle) ctx.rotate(angle);
				ctx.drawImage(image, -w / 2, -h / 2, w, h);
				ctx.restore();
			}
			return;
		}
	}
},
    "物品显伤": function () {

	// ===== getDamageString 缓存：避免对同一怪物重复做 DP 计算 =====
	// 核心思路：DP 计算结果只依赖 hero 的核心属性，不依赖 HP
	// 颜色依赖 HP，但颜色计算是 O(1)，DP 计算是 O(K*H)，差距巨大
	// 所以我们缓存每个怪物 ID 的 getDamage 数值，只在 hero 核心属性或地图状态变化时重算
	var _perEnemyDamageCache = {};
	var _heroDamageSnapshot = null; // 合并两个冗余变量为一个
	const ignore = ['superPotion']

	function _getDamageHeroSnapshot() {
		var h = core.status.hero;
		var floorId = core.status.floorId;
		var map = core.status.maps[floorId];

		// ===== 修复1：增加地图状态检测 =====
		// 统计当前楼层的有效怪物和物品数量，用于检测地图变化
		var enemyCount = 0,
			itemCount = 0;
		if (map && map.blocks) {
			map.blocks.forEach(function (block) {
				if (block.disable) return;
				if (block.event.cls && block.event.cls.indexOf('enemy') === 0) enemyCount++;
				if (block.event.cls === 'items' && !ignore.includes(block.event.id)) itemCount++;
			});
		}

		return [
			h.atk, h.def, h.mdef, h.agi, h.cri,
			h.atkCritical, h.defCritical,
			h.flags && h.flags.proof_tyrant,
			h.flags && h.flags.proof_brave,
			h.flags && h.flags.proof_sage,
			h.flags && h.flags.poison,
			h.flags && h.flags.weak,
			core.hasItem('magic_amulet'), core.hasItem('cross'), // 新增：关键道具状态
		].join('|');
	}

	// 检查并清空伤害缓存（移到最外层，确保每次updateDamage都会调用）
	function _checkClearDamageCache() {
		var currentSnapshot = _getDamageHeroSnapshot();
		if (currentSnapshot !== _heroDamageSnapshot) {
			_perEnemyDamageCache = {};
			_heroDamageSnapshot = currentSnapshot;
			// 关键修复：属性或地图变化时重置防抖计时器，强制立即更新显伤
			core.control._lastUpdateDamageTime = 0;
		}
	}

	// 缓存 nextCriticals 的结果（同样只依赖 hero 核心属性和怪物ID）
	// 本塔无光环/支援，同 ID 怪物属性完全一致，按怪物 ID 缓存即可
	core.enemys.nextCriticals = function (enemy, number, x, y, floorId) {
		if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
		if (!enemy) return [];
		var cacheKey = 'nc_' + enemy.id + '_' + number;
		if (_perEnemyDamageCache[cacheKey]) {
			return _perEnemyDamageCache[cacheKey];
		}
		// 直接调用原型上的最新方法，this 指向 core.enemys
		var result = enemys.prototype.nextCriticals.call(this, enemy, number, x, y, floorId);
		_perEnemyDamageCache[cacheKey] = result;
		return result;
	};

	core.enemys.getDamage = function (enemy, x, y, floorId) {
		if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
		if (!enemy) return null;
		var cacheKey = 'gd_' + enemy.id;
		if (_perEnemyDamageCache[cacheKey] !== undefined) {
			return _perEnemyDamageCache[cacheKey];
		}
		var result = enemys.prototype.getDamage.call(this, enemy, x, y, floorId);
		_perEnemyDamageCache[cacheKey] = result;
		return result;
	};

	core.control.updateDamage = function (floorId, ctx) {
		floorId = floorId || core.status.floorId;
		if (!floorId || core.status.gameOver || main.mode != 'play') return;

		// 关键修复1：在防抖之前先检查缓存，确保属性或地图变化能被及时检测到
		_checkClearDamageCache();

		// 执行原有的更新逻辑
		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);
	};

	core.control._updateDamage_damage = function (floorId, onMap) {
		core.status.damage.data = [];
		if (!core.flags.displayEnemyDamage && !core.flags.displayExtraDamage) return;

		core.extractBlocks(floorId);
		core.status.maps[floorId].blocks.forEach(function (block) {
			var x = block.x,
				y = block.y;

			if (onMap && core.bigmap.v2) {
				if (x < core.bigmap.posX - core.bigmap.extend || x > core.bigmap.posX + core.__SIZE__ + core.bigmap.extend ||
					y < core.bigmap.posY - core.bigmap.extend || y > core.bigmap.posY + core.__SIZE__ + core.bigmap.extend) {
					return;
				}
			}

			if (!block.disable && block.event.cls.indexOf('enemy') == 0 && block.event.displayDamage !== false) {
				var enemyId = block.event.id;

				if (core.flags.displayEnemyDamage) {
					var damageValue = core.enemys.getDamage(enemyId, x, y, floorId);
					var color, damageString;

					if (damageValue == null) {
						damageString = "???";
						color = '#FF2222';
					} else {
						if (damageValue <= 0) color = '#11FF11';
						else if (damageValue < core.status.hero.hp / 3) color = '#FFFFFF';
						else if (damageValue < core.status.hero.hp * 2 / 3) color = '#FFFF00';
						else if (damageValue < core.status.hero.hp) color = '#FF9933';
						else color = '#FF2222';

						damageString = core.formatBigNumber(damageValue, true);
						var enemy = core.material.enemys[enemyId];
						if (core.enemys.hasSpecial(enemy, 19)) damageString += "+";
						if (core.enemys.hasSpecial(enemy, 21)) damageString += "-";
						if (core.enemys.hasSpecial(enemy, 11)) damageString += "^";
					}

					core.status.damage.data.push({ text: damageString, px: 32 * x + 1, py: 32 * (y + 1) - 1, color: color });
				}
				if (core.flags.displayCritical) {
					var critical = core.enemys.nextCriticals(block.event.id, 1, x, y, floorId);
					critical = core.formatBigNumber((critical[0] || [])[0], true);
					if (critical == '???') critical = '?';
					core.status.damage.data.push({ text: critical, px: 32 * x + 1, py: 32 * (y + 1) - 11, color: '#FFFFFF' });
				}
			}
		});
	}

	// 获取宝石信息 并绘制（带缓存优化，已包含之前的拾取修复）
	var _itemDetailCache = {
		floorId: null,
		heroSnapshot: null,
		itemBlocksSnapshot: null,
		data: null
	};

	this.getItemDetail = function (floorId) {
		floorId = floorId != null ? floorId : (core.status.thisMap && core.status.thisMap.floorId);
		if (!core.status.thisMap || !core.status.maps[floorId]) return;

		// ===== 修复3：强制提取最新的地图数据 =====
		// 确保物品显伤总是使用最新的blocks数组，避免使用旧数据
		core.extractBlocks(floorId);

		const beforeRatio = core.status.thisMap.ratio;
		core.status.thisMap.ratio = core.status.maps[floorId].ratio;

		const originalHero = core.status.hero;
		var blocks = core.status.maps[floorId].blocks;

		// 物品显伤的快照也使用统一的伤害快照
		var heroSnapshot = _getDamageHeroSnapshot();

		// 生成物品状态快照（记录所有物品的x、y、disable状态）
		var itemBlocksSnapshot = '';
		blocks.forEach(function (block) {
			var event = block.event;
			if (!event || event.cls !== 'items' || ignore.includes(event.id)) return;
			itemBlocksSnapshot += block.x + ',' + block.y + ',' + (block.disable ? '1' : '0') + '|';
		});

		// 缓存命中条件
		if (_itemDetailCache.floorId === floorId &&
			_itemDetailCache.heroSnapshot === heroSnapshot &&
			_itemDetailCache.itemBlocksSnapshot === itemBlocksSnapshot &&
			_itemDetailCache.data) {
			_itemDetailCache.data.forEach(function (item) {
				core.status.damage.data.push(item);
			});
			core.status.thisMap.ratio = beforeRatio;
			return;
		}

		var cachedData = [];
		var itemBlocks = [];
		blocks.forEach(function (block) {
			var x = block.x,
				y = block.y,
				event = block.event,
				disable = block.disable;
			if (!event || event.cls !== 'items' || ignore.includes(event.id) || disable) return;

			if (core.bigmap.v2 &&
				(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;
			}

			itemBlocks.push({ block: block, x: x, y: y, event: event, itemId: event.id });
		});

		itemBlocks.forEach(function (item) {
			var itemId = item.itemId;
			var x = item.x,
				y = item.y;
			var itemData = core.material.items[itemId];
			if (!itemData) return;

			if (itemData.cls === 'equips') {
				var equipValue = (itemData.equip && itemData.equip.value) || {};
				var percentage = (itemData.equip && itemData.equip.percentage) || {};
				var equipDiff = {};

				for (var key in equipValue) {
					equipDiff[key] = equipValue[key];
				}
				for (var key in percentage) {
					equipDiff[key + 'per'] = percentage[key] + '%';
				}

				var detailItems = _buildItemDetailData(equipDiff, x, y);
				cachedData = cachedData.concat(detailItems);
				return;
			}

			var diff = _calcItemDiff(itemData, originalHero);
			var detailItems = _buildItemDetailData(diff, x, y);
			cachedData = cachedData.concat(detailItems);
		});

		cachedData.forEach(function (item) {
			core.status.damage.data.push(item);
		});

		_itemDetailCache.floorId = floorId;
		_itemDetailCache.heroSnapshot = heroSnapshot;
		_itemDetailCache.itemBlocksSnapshot = itemBlocksSnapshot;
		_itemDetailCache.data = cachedData;

		core.status.thisMap.ratio = beforeRatio;
		window.hero = originalHero;
		window.flags = originalHero.flags;
	};


	// 手动计算道具属性差异（替代 Proxy + eval）
	function _calcItemDiff(itemData, hero) {
		var diff = {};
		var effect = itemData.itemEffect;
		if (!effect) return diff;

		// 用简单正则匹配常见的属性赋值模式
		// 匹配 core.status.hero.xxx += value 或 core.status.hero.hp += value 等
		var patterns = [
			{ regex: /core\.status\.hero\.(\w+)\s*\+=\s*([^;]+)/g, op: '+' },
			{ regex: /core\.status\.hero\.(\w+)\s*-=\s*([^;]+)/g, op: '-' },
		];

		for (var p = 0; p < patterns.length; p++) {
			var pat = patterns[p];
			var match;
			while ((match = pat.regex.exec(effect)) !== null) {
				var attr = match[1];
				var expr = match[2];
				try {
					// 安全地计算表达式值
					var val = _safeEval(expr, hero);
					if (val !== null && !isNaN(val)) {
						if (pat.op === '-') val = -val;
						if (diff[attr] == null) diff[attr] = 0;
						diff[attr] += val;
					}
				} catch (e) {
					// 表达式无法安全计算，跳过
				}
			}
		}

		// 如果正则没匹配到任何内容，回退到 Proxy 方式（但只对单个道具做，不批量）
		if (Object.keys(diff).length === 0) {
			return _calcItemDiffByProxy(itemData, hero);
		}

		return diff;
	}

	// 安全地计算简单表达式
	function _safeEval(expr, hero) {
		// 替换常见的属性引用
		var sanitized = expr
			.replace(/core\.values\.(\w+)/g, function (m, key) { return core.values[key] || 0; })
			.replace(/core\.status\.hero\.(\w+)/g, function (m, key) { return hero[key] || 0; })
			.replace(/core\.status\.thisMap\.ratio/g, function () { return core.status.thisMap.ratio || 1; })
			.replace(/core\.getRealStatus\(['"](\w+)['"]\)/g, function (m, key) { return core.getRealStatus(key) || 0; });

		// 只允许数字和基本运算符
		if (/^[\d\s+\-*/().]+$/.test(sanitized)) {
			return Function('"use strict"; return (' + sanitized + ')')();
		}
		return null;
	}

	// Proxy 回退方式（仅在正则匹配失败时使用）
	function _calcItemDiffByProxy(itemData, hero) {
		var heroClone = core.clone(hero);
		var diff = {};
		var handler = {
			set: function (target, key, val) {
				var oldVal = target[key] || 0;
				var delta = val - oldVal;
				if (delta !== 0) {
					if (diff[key] == null) diff[key] = 0;
					diff[key] += delta;
				}
				target[key] = val;
				return true;
			}
		};
		var proxyHero = new Proxy(heroClone, handler);
		core.status.hero = proxyHero;
		try {
			core.setFlag('__statistics__', true);
			eval(itemData.itemEffect);
		} catch (e) {
			console.warn("物品效果执行失败：" + itemData.id, e);
		}
		core.status.hero = hero;
		return diff;
	}

	// 从 diff 数据构建绘制数据（不直接绘制，返回数据数组）
	function _buildItemDetailData(diff, x, y) {
		var px = 32 * x + 2,
			py = 32 * y + 30;
		var result = [];
		var i = 0;
		for (var name in diff) {
			if (!diff[name]) continue;
			var color = '#fff';
			var content;
			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;
			case 'money':
				color = '#ffff00';
				break;
			default:
				continue;
			}
			result.push({
				text: content,
				px: px,
				py: py - 10 * i,
				color: color
			});
			i++;
		}
		return result;
	}

	// 保留强制刷新接口，供其他插件调用（可选）
	this.forceClearDamageCache = function () {
		_perEnemyDamageCache = {};
		_itemDetailCache.data = null;
		_heroDamageSnapshot = null;
		if (core.control && core.control._lastUpdateDamageTime) {
			core.control._lastUpdateDamageTime = 0;
		}
		if (core.control && core.control.updateDamage) {
			core.control.updateDamage();
		}
	}
},
    "对话框效果": function () {
	ui.prototype.drawTextBox = function (content, config) {
		config = config || {};

		let mainTextFont = config.font,
			fontSize = config.fontSize;

		this.clearUI();
		content = core.replaceText(content);

		var ctx = config.ctx || null;
		if (ctx && main.mode == 'play') {
			core.createCanvas(ctx, 0, 0, core.__PIXELS__, core.__PIXELS__, 141);
			ctx = core.getContextByName(ctx);
		}

		// Step 1: 获得标题信息和位置信息
		var textAttribute = core.status.textAttribute;
		var titleInfo = this._getTitleAndIcon(content);
		var posInfo = this._getPosition(titleInfo.content);
		if (titleInfo.title) {
			var textAttribute = core.status.textAttribute || core.initStatus.textAttribute;
			posInfo.pos = [80, 64, 8 * 32];
			posInfo.hasIcon = true; // ← 加这行
			mainTextFont = 'DFPZongYiW9-P';
			core.status.textAttribute.title = [255, 255, 255, 1];
			core.status.textAttribute.titlefont = 20;
			core.status.textAttribute.textfont = 14;
			core.status.textAttribute.lineHeight = 18;
		}

		if (posInfo.position != 'up' && posInfo.position != 'down') posInfo.px = posInfo.py = null;
		if (!posInfo.position) posInfo.position = textAttribute.position;
		content = this._drawTextBox_drawImages(posInfo.content, config.ctx);
		if (config.pos) {
			delete posInfo.px;
			delete posInfo.py;
			posInfo.pos = config.pos;
		}
		posInfo.ctx = ctx;

		// Step 2: 计算对话框的矩形位置
		var hPos = this._drawTextBox_getHorizontalPosition(content, titleInfo, posInfo);
		var vPos = this._drawTextBox_getVerticalPosition(content, titleInfo, posInfo, hPos.validWidth);
		posInfo.xoffset = hPos.xoffset;
		posInfo.yoffset = vPos.yoffset - 4;

		if (ctx && main.mode == 'play') {
			ctx.canvas.setAttribute('_text_left', hPos.left);
			ctx.canvas.setAttribute('_text_top', vPos.top);
		}

		// Step 3: 绘制背景图
		var isWindowSkin = this.drawBackground(hPos.left, vPos.top, hPos.right, vPos.bottom, posInfo);
		var alpha = isWindowSkin ? this._drawWindowSkin_getOpacity() : textAttribute.background[3];

		// Step 4: 绘制标题、头像、动画
		var content_top = this._drawTextBox_drawTitleAndIcon(titleInfo, hPos, vPos, alpha, config.ctx);

		// Step 5: 绘制正文
		var config = this.drawTextContent(config.ctx || 'ui', content, {
			left: hPos.content_left,
			top: content_top,
			maxWidth: hPos.validWidth,
			lineHeight: vPos.lineHeight,
			time: (config.showAll || config.async || textAttribute.time <= 0 || core.status.event.id != 'action') ? 0 : textAttribute.time,
			font: mainTextFont
		});

		// Step 6: 绘制光标
		if (main.mode == 'play') {
			main.dom.next.style.display = 'block';
			main.dom.next.style.borderRightColor = main.dom.next.style.borderBottomColor = core.arrayToRGB(textAttribute.text);
			main.dom.next.style.top = (vPos.bottom - 20) * core.domStyle.scale + "px";
			var left = (hPos.left + hPos.right) / 2;
			if (posInfo.position == 'up' && !posInfo.noPeak && posInfo.px != null && Math.abs(posInfo.px * 32 + 16 - left) < 50)
				left = hPos.right - 64;
			main.dom.next.style.left = left * core.domStyle.scale + "px";
		}
		return config;
	}

	ui.prototype.drawBackground = function (left, top, right, bottom, posInfo) {
		posInfo = posInfo || {};
		var px = posInfo.px == null || posInfo.noPeak ? null : posInfo.px * 32 - core.bigmap.offsetX;
		var py = posInfo.py == null || posInfo.noPeak ? null : posInfo.py * 32 - core.bigmap.offsetY;
		var xoffset = posInfo.xoffset || 0,
			yoffset = posInfo.yoffset || 0;
		var background = core.status.textAttribute.background;

		// --- 如果是 winSkin 背景 ---
		if (this._drawBackground_drawWindowSkin(background, left, top, right, bottom, posInfo.position, px, py, posInfo.ctx))
			return true;

		// --- 默认颜色背景 ---
		if (typeof background == 'string') background = core.initStatus.textAttribute.background;
		this._drawBackground_drawColor(background, left, top, right, bottom, posInfo.position, px, py, xoffset, yoffset, posInfo.ctx, posInfo.hasIcon);
		return false;
	};


	ui.prototype._drawBackground_drawColor = function (background, left, top, right, bottom, position, px, py, xoffset, yoffset, ctx, hasIcon) {
		ctx = ctx || 'ui';
		ctx = core.getContextByName(ctx);

		// 保存原始图像平滑设置
		var originalSmoothingSettings = null;
		if (hasIcon) {
			originalSmoothingSettings = {
				imageSmoothingEnabled: ctx.imageSmoothingEnabled,
				mozImageSmoothingEnabled: ctx.mozImageSmoothingEnabled,
				webkitImageSmoothingEnabled: ctx.webkitImageSmoothingEnabled,
				msImageSmoothingEnabled: ctx.msImageSmoothingEnabled
			};

			// --- 图标对话框模式 ---
			ctx.imageSmoothingEnabled = false;
			ctx.mozImageSmoothingEnabled = false;
			ctx.webkitImageSmoothingEnabled = false;
			ctx.msImageSmoothingEnabled = false;

			var groundImage = core.material.images.images['ground.png'];
			var imageElement = groundImage && (groundImage.canvas || groundImage);
			if (imageElement) ctx.fillStyle = ctx.createPattern(imageElement, 'repeat');

			ctx.lineWidth = 3;
			ctx.strokeStyle = '#0099CC';

		} else {
			// --- 普通模式 ---
			var alpha = background[3];
			core.setAlpha(ctx, alpha);
			core.setFillStyle(ctx, core.arrayToRGB(background));
			core.setStrokeStyle(ctx, core.arrayToRGBA(core.status.globalAttribute.borderColor));
			ctx.lineWidth = 2;
		}

		ctx.beginPath();
		ctx.moveTo(left, top);
		if (position == 'down' && px != null && py != null) {
			ctx.lineTo(px + xoffset, top);
			ctx.lineTo(px + 16, top - yoffset);
			ctx.lineTo(px + 32 - xoffset, top);
		}
		ctx.lineTo(right, top);
		ctx.lineTo(right, bottom);
		if (position == 'up' && px != null && py != null) {
			ctx.lineTo(px + 32 - xoffset, bottom);
			ctx.lineTo(px + 16, bottom + yoffset);
			ctx.lineTo(px + xoffset, bottom);
		}
		ctx.lineTo(left, bottom);
		ctx.closePath();

		ctx.fill();
		ctx.stroke();

		core.setAlpha(ctx, 1);

		// 恢复原始图像平滑设置
		if (originalSmoothingSettings) {
			ctx.imageSmoothingEnabled = originalSmoothingSettings.imageSmoothingEnabled;
			ctx.mozImageSmoothingEnabled = originalSmoothingSettings.mozImageSmoothingEnabled;
			ctx.webkitImageSmoothingEnabled = originalSmoothingSettings.webkitImageSmoothingEnabled;
			ctx.msImageSmoothingEnabled = originalSmoothingSettings.msImageSmoothingEnabled;
		}
	};
	ui.prototype._drawTextBox_drawTitleAndIcon = function (titleInfo, hPos, vPos, alpha, ctx) {
		ctx = ctx || 'ui';
		ctx = core.getContextByName(ctx);

		core.setTextAlign(ctx, 'left');
		var textAttribute = core.status.textAttribute;
		var content_top = vPos.top + 7;
		var image_top = vPos.top + 15;
		if (titleInfo.title != null) {
			var titlefont = textAttribute.titlefont;
			content_top += titlefont + 5;
			image_top = vPos.top + 40;
			core.setFillStyle(ctx, core.arrayToRGB(textAttribute.title));
			core.setStrokeStyle(ctx, core.arrayToRGB(textAttribute.title));

			// 修改这里：标题完全居中
			var title_font = this._buildFont(titlefont, false, false, 'ChaoZiSheZengYuBoShouShuJianFan');
			var title_width = core.calWidth(ctx, titleInfo.title, title_font);
			var title_left = hPos.left + (hPos.width - title_width) / 2; // 水平居中

			// 垂直位置也调整到更合适的位置
			var title_top = vPos.top + titlefont + 4; // 距离顶部适当距离

			core.fillText(ctx, titleInfo.title, title_left, title_top, null, title_font);
		}
		if (titleInfo.icon != null) {
			core.setAlpha(ctx, alpha);

			// 修改这里：头像框向上移动
			var icon_top = vPos.top + 14; // 从原来的 image_top 改为更靠上的位置

			core.strokeRect(ctx, hPos.left + 15 - 1, icon_top - 1, 34, titleInfo.height + 2, '#0099CC', 4);
			core.setAlpha(ctx, 1);
			core.status.boxAnimateObjs = [];

			if (titleInfo.image == core.material.images.hero) {
				if (core.status.hero.animate) {
					var direction = core.getHeroLoc('direction');
					if (direction == 'up') direction = 'down';
					core.status.boxAnimateObjs.push({
						'bgx': hPos.left + 15,
						'bgy': icon_top, // 使用新的位置
						'bgWidth': 32,
						'bgHeight': titleInfo.height,
						'x': hPos.left + 15,
						'y': icon_top, // 使用新的位置
						'height': titleInfo.height,
						'animate': 4,
						'image': titleInfo.image,
						'pos': core.material.icons.hero[direction].loc * titleInfo.height,
						ctx: ctx,
					})
				} else {
					core.clearMap(ctx, hPos.left + 15, icon_top, 32, titleInfo.height);
					core.fillRect(ctx, hPos.left + 15, icon_top, 32, titleInfo.height, core.material.groundPattern);
					core.drawImage(ctx, titleInfo.image, 0, 0, core.material.icons.hero.width || 32, core.material.icons.hero.height,
						hPos.left + 15, icon_top, 32, titleInfo.height);
				}
			} else {
				if (titleInfo.bigImage) {
					core.status.boxAnimateObjs.push({
						bigImage: titleInfo.bigImage,
						face: titleInfo.face,
						centerX: hPos.left + 15 + 16,
						centerY: icon_top + titleInfo.height / 2, // 使用新的位置
						max_width: 50,
						ctx: ctx
					});
				} else {
					core.status.boxAnimateObjs.push({
						'bgx': hPos.left + 15,
						'bgy': icon_top, // 使用新的位置
						'bgWidth': 32,
						'bgHeight': titleInfo.height,
						'x': hPos.left + 15,
						'y': icon_top, // 使用新的位置
						'height': titleInfo.height,
						'animate': titleInfo.animate,
						'image': titleInfo.image,
						'pos': titleInfo.icon * titleInfo.height,
						ctx: ctx,
					});
				}
			}
			core.drawBoxAnimate();
		}
		if (titleInfo.image != null && titleInfo.icon == null) { // 头像图
			core.drawImage(ctx, titleInfo.image, 0, 0, titleInfo.image.width, titleInfo.image.height,
				hPos.left + 10, vPos.top + 10, 70, 70);
		}

		return content_top;
	}

	ui.prototype.drawChoices = function (content, choices, width, ctx) {
		choices = core.clone(choices || []);
		core.status.event.ui = { "text": content, "choices": choices, "width": width };

		this.clearUI();
		content = core.replaceText(content || "");
		var titleInfo = this._getTitleAndIcon(content);

		let hasTiltle = false;

		if (titleInfo.title) {
			core.status.globalAttribute.font = 'ChaoZiSheZengYuBoShouShuJianFan';
			core.status.textAttribute.textfont = 15;
			core.status.textAttribute.titlefont = 21;
			core.status.textAttribute.choicefont = 22;
			hasTiltle = true;
		} else {
			core.status.globalAttribute.font = 'DFPZongYiW9-P';
			core.status.textAttribute.choicefont = 17;
		}

		titleInfo.content = this._drawTextBox_drawImages(titleInfo.content, ctx);
		var hPos = this._drawChoices_getHorizontalPosition(titleInfo, choices, width, ctx);
		var vPos = this._drawChoices_getVerticalPosition(titleInfo, choices, hPos);
		core.status.event.ui.offset = vPos.offset;

		var isWindowSkin = this.drawBackground(hPos.left, vPos.top, hPos.right, vPos.bottom, { ctx: ctx, hasIcon: hasTiltle });
		this._drawChoices_drawTitle(titleInfo, hPos, vPos, ctx);
		this._drawChoices_drawChoices(choices, isWindowSkin, hPos, vPos, ctx, hasTiltle);
	}
	ui.prototype._drawChoices_getHorizontalPosition = function (titleInfo, choices, width, ctx) {
		ctx = ctx || 'ui';
		// 设置最大宽度限制
		var MAX_WIDTH = 8 * 32; // 你可以调整这个值来控制最大宽度

		core.setFont(ctx, this._buildFont(17, true));
		var width = this._calTextBoxWidth(ctx, titleInfo.content || "", width || 246, this.PIXEL - 20);
		for (var i = 0; i < choices.length; i++) {
			if (typeof choices[i] === 'string')
				choices[i] = { "text": choices[i] };
			choices[i].text = core.replaceText(choices[i].text);
			choices[i].width = core.calWidth(ctx, core.replaceText(choices[i].text));
			if (choices[i].icon != null) choices[i].width += 28;
			width = Math.max(width, choices[i].width + 30);
		}

		// 添加宽度限制
		width = Math.min(width, MAX_WIDTH);

		var left = (this.PIXEL - width) / 2,
			right = left + width;
		var content_left = left + (titleInfo.icon == null ? 15 : 60),
			validWidth = right - content_left - 10;

		return { left: left, right: right, width: width, content_left: content_left, validWidth: validWidth };
	}
	ui.prototype._drawTextBox_drawImages = function (content, ctx) {
		ctx = ctx || 'ui';
		return content.replace(/(\f|\\f)\[(.*?)]/g, function (text, sympol, str) {
			var ss = str.split(",");
			// 绘制
			if (ss.length == 3)
				core.drawImage(ctx, ss[0], parseFloat(ss[1]), parseFloat(ss[2]));
			else if (ss.length == 5)
				core.drawImage(ctx, ss[0], parseFloat(ss[1]), parseFloat(ss[2]), parseFloat(ss[3]), parseFloat(ss[4]));
			else if (ss.length >= 9) {
				if (ss.length >= 10) core.setAlpha(ctx, parseFloat(ss[9]));
				var angle = (parseFloat(ss[10]) || 0) * Math.PI / 180;
				core.drawImage(ctx, ss[0], parseFloat(ss[1]), parseFloat(ss[2]), parseFloat(ss[3]), parseFloat(ss[4]),
					parseFloat(ss[5]), parseFloat(ss[6]), parseFloat(ss[7]), parseFloat(ss[8]), angle);
				core.setAlpha(ctx, 1);
			}
			return "";
		});
	}
	ui.prototype._drawChoices_drawTitle = function (titleInfo, hPos, vPos, ctx) {
		if (!titleInfo.content) return;
		ctx = ctx || 'ui';
		var content_top = vPos.top + 21;
		if (titleInfo.title != null) {
			core.setTextAlign(ctx, 'center');

			content_top = vPos.top + 41;
			var title_offset = hPos.left + hPos.width / 2;
			// 动画

			if (titleInfo.icon != null) {
				title_offset += 12;
				core.strokeRect(ctx, hPos.left + 32 - 1, vPos.top + 32 - 1, 34, titleInfo.height + 2, '#0099CC', 4);
				core.status.boxAnimateObjs = [];
				if (titleInfo.bigImage) {
					core.status.boxAnimateObjs.push({
						bigImage: titleInfo.bigImage,
						face: titleInfo.face,
						centerX: hPos.left + 32 + 16,
						centerY: vPos.top + 32 + titleInfo.height / 2,
						max_width: 50,
						ctx: ctx
					});
				} else {
					core.status.boxAnimateObjs.push({
						'bgx': hPos.left + 32,
						'bgy': vPos.top + 32,
						'bgWidth': 32,
						'bgHeight': titleInfo.height,
						'x': hPos.left + 32,
						'y': vPos.top + 32,
						'height': titleInfo.height,
						'animate': titleInfo.animate,
						'image': titleInfo.image,
						'pos': titleInfo.icon * titleInfo.height,
						ctx: ctx
					});
				}
				core.drawBoxAnimate();
			};

			core.fillText(ctx, titleInfo.title, title_offset, vPos.top + 32, '#FFFFFF', this._buildFont(core.status.textAttribute.titlefont, false));
		}

		core.setTextAlign(ctx, 'left');
		this.drawTextContent(ctx, titleInfo.content, {
			left: hPos.content_left + 14,
			top: content_top - 4,
			maxWidth: hPos.validWidth - 10,
			fontSize: core.status.textAttribute.textfont,
			lineHeight: 20,
		});
	}
	ui.prototype._drawChoices_drawChoices = function (choices, isWindowSkin, hPos, vPos, ctx, hasTiltle) {
		var hasCtx = ctx != null;
		ctx = ctx || 'ui';

		// 字号相关常量提取
		var FONT_SIZE = core.status.textAttribute.choicefont; // 字号
		var LINE_HEIGHT = FONT_SIZE + 15; // 行高
		var ICON_SIZE = 22; // 图标尺寸
		var SELECTOR_HEIGHT = FONT_SIZE + 11; // 选择框高度
		var TEXT_OFFSET = FONT_SIZE; // 文本偏移
		var SELECTOR_OFFSET = FONT_SIZE + 3; // 选择框偏移

		// 绘制分割直线 - 在文章和选项之间
		if (hasTiltle) { // 确保有内容才绘制
			// 统一的垂直偏移量
			core.status.event.ui.offset = 10;

			var lineY = vPos.choice_top - 22;
			core.drawLine(ctx, hPos.left, lineY, hPos.right, lineY, '#0099CC', 3);
		}
		var verticalOffset = core.status.event.ui.offset; // 调整这个值来控制下调距离
		core.status.event.ui.choice_top = vPos.choice_top + verticalOffset - FONT_SIZE;

		// 选项
		core.setTextAlign(ctx, 'center');
		core.setFont(ctx, this._buildFont(FONT_SIZE, false));
		for (var i = 0; i < choices.length; i++) {
			var color = core.arrayToRGBA(choices[i].color || core.status.textAttribute.text);
			if (main.mode == 'play' && choices[i].need != null && choices[i].need != '' && !core.calValue(choices[i].need)) color = '#999999';
			core.setFillStyle(ctx, color);
			var offset = this.HPIXEL;
			if (choices[i].icon) {
				var iconInfo = this._getDrawableIconInfo(choices[i].icon),
					image = iconInfo[0],
					icon = iconInfo[1];
				if (image != null) {
					core.drawImage(ctx, image, 0, 32 * icon, 32, 32,
						this.HPIXEL - choices[i].width / 2, vPos.choice_top + LINE_HEIGHT * i - TEXT_OFFSET + verticalOffset, ICON_SIZE, ICON_SIZE);
					offset += 14;
				}
			}
			core.fillText(ctx, choices[i].text, offset, vPos.choice_top + LINE_HEIGHT * i + verticalOffset, color);
		}

		if (choices.length > 0 && core.status.event.selection != 'none') {
			core.status.event.selection = core.status.event.selection || 0;
			while (core.status.event.selection < 0) core.status.event.selection += choices.length;
			while (core.status.event.selection >= choices.length) core.status.event.selection -= choices.length;
			var len = choices[core.status.event.selection].width;
			if (isWindowSkin) {
				if (hasCtx) {
					this._drawSelector(ctx, core.status.textAttribute.background,
						len + 10, SELECTOR_HEIGHT, this.HPIXEL - len / 2 - 5, vPos.choice_top + LINE_HEIGHT * core.status.event.selection - SELECTOR_OFFSET + verticalOffset);
				} else {
					this._drawWindowSelector(core.status.textAttribute.background,
						this.HPIXEL - len / 2 - 5, vPos.choice_top + LINE_HEIGHT * core.status.event.selection - SELECTOR_OFFSET + verticalOffset, len + 10, SELECTOR_HEIGHT);
				}
			} else {
				var arrowWidth = 12; // 箭头宽度
				var arrowHeight = 15; // 箭头高度
				var stemWidth = 8; // 箭杆宽度
				var stemLength = 3; // 箭杆长度

				// 计算箭头位置（在选项文本左侧）
				var arrowX = this.HPIXEL - len / 2 - 45;
				var arrowY = vPos.choice_top + LINE_HEIGHT * core.status.event.selection - SELECTOR_OFFSET + verticalOffset + SELECTOR_HEIGHT / 2;

				// 定义箭头的七个顶点坐标（形成一个完整的箭头形状）
				var arrowNodes = [
					// 箭杆部分
					[arrowX, arrowY - stemWidth / 2], // 箭杆左端上点
					[arrowX + stemLength, arrowY - stemWidth / 2], // 箭杆右端上点
					// 箭头部分
					[arrowX + stemLength, arrowY - arrowHeight / 2], // 箭头上部左点
					[arrowX + stemLength + arrowWidth, arrowY], // 箭头尖端
					[arrowX + stemLength, arrowY + arrowHeight / 2], // 箭头下部左点
					// 回到箭杆
					[arrowX + stemLength, arrowY + stemWidth / 2], // 箭杆右端下点
					[arrowX, arrowY + stemWidth / 2] // 箭杆左端下点
				];

				// 使用 fillPolygon 绘制实心箭头
				core.fillPolygon(ctx, arrowNodes, '#FFFFFF');
			}
			//core.strokeRoundRect(ctx, this.HPIXEL - len / 2 - 5, vPos.choice_top + LINE_HEIGHT * core.status.event.selection - SELECTOR_OFFSET + verticalOffset, len + 10, SELECTOR_HEIGHT, 6, core.status.globalAttribute.selectColor, 2);
		}
	}

	function _computeChoicesLayout(choices) {
		// 与 drawChoices 中保持一致的字体/行距常量
		var FONT_SIZE = core.status.textAttribute.choicefont;
		var LINE_HEIGHT = FONT_SIZE + 15;
		var ICON_EXTRA = 14; // 有图标时给文本宽度加的补偿
		var PADDING = 10; // 文本左右额外间距（像素）
		var verticalOffset = (core.status.event && core.status.event.ui && core.status.event.ui.offset) ? core.status.event.ui.offset : 10;

		// font 字符串，传给 core.calWidth 使用（使测量与实际绘制一致）
		var fontStr = core.ui._buildFont(FONT_SIZE, false, false, core.status.globalAttribute.font);

		// 计算每项与最大宽度（像素）
		var maxWidth = 0;
		for (var i = 0; i < choices.length; i++) {
			var txt = choices[i].text || "";
			var w = core.calWidth('ui', txt, fontStr) || 0;
			if (choices[i].icon) w += ICON_EXTRA;
			if (w > maxWidth) maxWidth = w;
		}

		// 计算项区域左/右（以绘制用的 this.HPIXEL 为中点）
		var left = core.ui.HPIXEL - (maxWidth / 2) - PADDING;
		var right = core.ui.HPIXEL + (maxWidth / 2) + PADDING;
		var choiceTop = core.status.event.ui.choice_top;

		return {
			left: left,
			right: right,
			top: choiceTop,
			lineHeight: LINE_HEIGHT,
			maxWidth: maxWidth,
			font: fontStr,
			padding: PADDING
		};
	};
	actions.prototype._onMoveChoices = function (x, y, px, py) {
		// px,py 是像素坐标（指针/触摸）
		var choices = core.status.event && core.status.event.ui && core.status.event.ui.choices;
		if (!choices || choices.length === 0) return;

		var layout = _computeChoicesLayout(choices);

		// 如果指针在选项区域外（水平），就不处理
		if (px < layout.left || px > layout.right) return;

		// 计算相对 Y，并得到选中 index（基于像素）
		var relY = py - layout.top;
		if (relY < 0) return;
		var idx = Math.floor(relY / layout.lineHeight);
		if (idx < 0 || idx >= choices.length) return;

		if (idx === core.status.event.selection) return;
		core.status.event.selection = idx;
		core.playSound('光标移动');
		// 重新绘制（保持与原函数 drawChoices 的调用签名一致）
		core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices, core.status.event.ui.width);
	};
	actions.prototype._clickAction = function (x, y, px, py) {
		if (core.status.event.data.type == 'text') {
			return this._clickAction_text();
		}

		if (core.status.event.data.type == 'wait') {
			var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0;
			core.setFlag('type', 1);
			core.setFlag('x', x);
			core.setFlag('y', y);
			core.setFlag('px', px);
			core.setFlag('py', py);
			core.setFlag('timeout', timeout);
			var executed = core.events.__action_wait_afterGet(core.status.event.data.current);
			if (executed || !core.status.event.data.current.forceChild) {
				core.status.route.push("input:" + (1e8 * timeout + 1000000 + 1000 * px + py));
				clearTimeout(core.status.event.interval);
				delete core.status.event.timeout;
				core.doAction();
			}
			return;
		}

		if (core.status.event.data.type == 'choices') {
			// 选项
			var data = core.status.event.data.current;
			var choices = data.choices;
			if (!choices || choices.length == 0) return;

			// 使用像素区域判断点击的是哪一项
			var layout = _computeChoicesLayout(choices);
			if (px >= layout.left && px <= layout.right) {
				var relY = py - layout.top;
				if (relY >= 0 && relY < choices.length * layout.lineHeight) {
					var index = Math.floor(relY / layout.lineHeight);
					var choice = choices[index];
					if (choice.need != null && choice.need != '' && !core.calValue(choice.need)) {
						core.playSound('操作失败');
						core.drawTip("无法选择此项");
						return;
					}
					clearTimeout(core.status.event.interval);
					var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0;
					delete core.status.event.timeout;
					core.setFlag('timeout', timeout);
					// 全局商店特殊处理
					if (index == choices.length - 1 && core.hasFlag('@temp@shop')) {
						index = -1;
					}
					core.status.route.push("choices:" + (100 * timeout + index));
					core.insertAction(choice.action);
					core.doAction();
				}
			}
			return;
		}

		if (core.status.event.data.type == 'confirm') {
			if ((x == this.HSIZE - 2 || x == this.HSIZE - 1) && y == this.HSIZE + 1) {
				clearTimeout(core.status.event.interval);
				var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0;
				delete core.status.event.timeout;
				core.setFlag('timeout', timeout);
				core.status.route.push("choices:" + 100 * timeout);
				core.insertAction(core.status.event.ui.yes);
				core.doAction();
			} else if ((x == this.HSIZE + 2 || x == this.HSIZE + 1) && y == this.HSIZE + 1) {
				clearTimeout(core.status.event.interval);
				var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0;
				delete core.status.event.timeout;
				core.setFlag('timeout', timeout);
				core.status.route.push("choices:" + (100 * timeout + 1));
				core.insertAction(core.status.event.ui.no);
				core.doAction();
			}
			return;
		}
	}
	core.registerAction('onmove', '_sys_onmove_choices', function (x, y, px, py) {
		if (!core.status.lockControl) return false;

		switch (core.status.event.id) {
		case 'action':
			if (core.status.event.data.type == 'choices') {
				this._onMoveChoices(x, y, px, py);
				return true;
			}
			if (core.status.event.data.type == 'confirm') {
				this._onMoveConfirmBox(x, y, px, py);
				return true;
			}
			break;
		case 'selectShop':
		case 'switchs':
		case 'switchs-sounds':
		case 'switchs-display':
		case 'switchs-action':
		case 'notes':
		case 'settings':
		case 'syncSave':
		case 'syncSelect':
		case 'localSaveSelect':
		case 'storageRemove':
		case 'replay':
		case 'gameInfo':
			this._onMoveChoices(x, y, px, py);
			return true;
		case 'confirmBox':
			this._onMoveConfirmBox(x, y, px, py);
			return true;
		default:
			break;
		}
		return false;
	}, 30);

	actions.prototype._keyUpAction = function (keycode) {
		if (core.status.event.data.type == 'text' && (keycode == 13 || keycode == 32 || keycode == 67)) {
			return this._clickAction_text();
		}
		if (core.status.event.data.type == 'wait') {
			var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0;
			core.setFlag('type', 0);
			core.setFlag('keycode', keycode);
			core.setFlag('timeout', timeout);
			var executed = core.events.__action_wait_afterGet(core.status.event.data.current);
			if (executed || !core.status.event.data.current.forceChild) {
				core.status.route.push("input:" + (1e8 * timeout + keycode));
				clearTimeout(core.status.event.interval);
				delete core.status.event.timeout;
				core.doAction();
			}
			return;
		}
		if (core.status.event.data.type == 'choices') {
			var data = core.status.event.data.current;
			var choices = data.choices;
			if (choices && choices.length > 0) {
				// 核心修改：不再通过 _selectChoices 调用 _clickAction
				this._selectChoices(choices.length, keycode);
			}
			return;
		}
		if (core.status.event.data.type == 'confirm' && (keycode == 13 || keycode == 32 || keycode == 67)) {
			var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0;
			delete core.status.event.timeout;
			core.setFlag('timeout', timeout);
			core.status.route.push("choices:" + (100 * timeout + core.status.event.selection));
			if (core.status.event.selection == 0) core.insertAction(core.status.event.ui.yes);
			else core.insertAction(core.status.event.ui.no);
			core.doAction();
			return;
		}
	};

	// 2. 修改 _selectChoices，兼容直接执行与回调两种调用方式
	actions.prototype._selectChoices = function (length, keycode, callback) {
		// 先尝试从 event.data 中获取（对话框），否则从 event.ui 获取（系统菜单等）
		var choices = [];
		if (core.status && core.status.event) {
			if (core.status.event.data && core.status.event.data.current && core.status.event.data.current.choices)
				choices = core.status.event.data.current.choices;
			else if (core.status.event.ui && core.status.event.ui.choices)
				choices = core.status.event.ui.choices;
		}

		var topIndex = this._getChoicesTopIndex(length);

		// 确认当前选择（Enter / Space / C）
		if (keycode == 13 || keycode == 32 || keycode == 67) {
			if (typeof callback === 'function') {
				callback.apply(this, [this.HSIZE, topIndex + (core.status.event.selection || 0)]);
			} else if (core.plugin && core.plugin._executeChoiceByIndex) {
				core.plugin._executeChoiceByIndex(core.status.event ? (core.status.event.selection || 0) : 0, choices);
			}
			return;
		}

		// 取消键：直接选择最后一项（Esc / X）
		if (keycode == 27 || keycode == 88) {
			if (typeof callback === 'function') {
				callback.apply(this, [this.HSIZE, topIndex + (length - 1)]);
			} else if (core.plugin && core.plugin._executeChoiceByIndex) {
				core.plugin._executeChoiceByIndex(length - 1, choices);
			}
			return;
		}

		// 数字键 1~9
		if (keycode >= 49 && keycode <= 57) {
			var index = keycode - 49;
			if (index < length) {
				if (typeof callback === 'function') {
					callback.apply(this, [this.HSIZE, topIndex + index]);
				} else if (core.plugin && core.plugin._executeChoiceByIndex) {
					core.plugin._executeChoiceByIndex(index, choices);
				}
			}
			return;
		}
	};
},
    "图像彻底转变": function () {
	core.removeBlockThoroughly = core.maps.removeBlockThoroughly = function (x, y, floorId) {
		floorId = floorId || core.status.floorId;
		if (!floorId) return false;

		core.extractBlocks(floorId);
		for (var i in core.status.maps[floorId].blocks) {
			var block = core.status.maps[floorId].blocks[i];
			if (block.x == x && block.y == y) {
				core.status.maps[floorId].blocks.splice(i, 1);
				if (core.status.mapBlockObjs[floorId])
					delete core.status.mapBlockObjs[floorId][x + "," + y];
				if (core.floors[floorId].events && core.floors[floorId].events[x + "," + y]) {
					core.setFlag('R' + floorId + "," + x + "," + y, true);
				}
				core.setMapBlockDisabled(floorId, x, y, true);
				core.maps._updateMapArray(floorId, x, y);
				core.maps._removeBlockFromMap(floorId, block);
				return true;
			}
		}
		return false;
	}
	core.setBlockThoroughly = core.maps.setBlockThoroughly = function (number, x, y, floorId) {
		floorId = floorId || core.status.floorId;
		if (!floorId || number == null || x == null || y == null) return;
		if (x < 0 || x >= core.floors[floorId].width || y < 0 || y >= core.floors[floorId].height) return;
		if (typeof number == 'string') {
			if (/^\d+$/.test(number)) number = parseInt(number);
			else number = core.getNumberById(number);
		}

		core.setFlag('R' + floorId + "," + x + "," + y, true);

		var block = core.maps.initBlock(x, y, number, true, core.floors[floorId]);
		if (block.id == 0 && !block.event.trigger) {
			// 转变图块为0且该点无事件，视为删除
			core.maps.removeBlockThoroughly(x, y, floorId);
			return;
		}

		var originBlock = core.getBlock(x, y, floorId, true);
		var originEvent = originBlock == null ? null : originBlock.event;
		if (originBlock == null) {
			core.status.maps[floorId].blocks.push(block);
			if (core.status.mapBlockObjs[floorId])
				core.status.mapBlockObjs[floorId][block.x + "," + block.y] = block;
			core.setMapBlockDisabled(floorId, block.x, block.y, false);
			delete block.disable;
		} else {
			originBlock.id = number;
			originBlock.event = block.event;
			block = originBlock;
			core.setMapBlockDisabled(floorId, block.x, block.y, false);
			delete block.disable;
		}
		core.maps._updateMapArray(floorId, x, y);
		if (floorId == core.status.floorId) {
			// 有任何一个是autotile直接重绘地图
			if ((originEvent != null && originEvent.cls == 'autotile') || block.event.cls == 'autotile') {
				core.redrawMap();
			} else {
				if (originEvent != null) {
					core.maps._removeBlockFromMap(floorId, { x: x, y: y, event: originEvent });
				}
				core.drawBlock(block);
				core.addGlobalAnimate(block);
				core.updateStatusBar();
			}
		}
	}
	maps.prototype.initBlock = function (x, y, id, addInfo, eventFloor) {
		var disable = null;
		var opacity = null;
		var filter = null;
		if (eventFloor != null) {
			disable = this.isMapBlockDisabled(eventFloor.floorId, x, y);
			opacity = this._getBlockOpacityFromFlag(eventFloor.floorId, x, y);
			filter = this._getBlockFilterFromFlag(eventFloor.floorId, x, y);
		}
		var block = { 'x': x, 'y': y, 'id': id };
		if (disable != null) block.disable = disable;
		if (opacity != null) block.opacity = opacity;
		if (filter != null) block.filter = filter;

		if (id == 17) block.event = { "cls": "terrains", "id": "airwall", "cannotIn": ["up", "down", "left", "right"] };
		else if (id in this.blocksInfo) block.event = JSON.parse(JSON.stringify(this.blocksInfo[id]));
		else if (core.icons.getTilesetOffset(id)) block.event = { "cls": "tileset", "id": "X" + id };
		else block.event = { 'cls': 'terrains', 'id': 'none', 'noPass': false };

		if (block.event.noPass == null) {
			if (block.event.canPass == null) {
				block.event.noPass = block.event.cls != 'items';
			} else {
				block.event.noPass = !block.event.canPass;
			}
		}
		delete block.event.canPass;

		// 增加怪物的faceIds
		if (block.event.cls.indexOf("enemy") == 0) {
			var enemy = core.material.enemys[block.event.id];
			if (enemy && enemy.faceIds) {
				block.event.faceIds = enemy.faceIds;
			}
		}

		if (addInfo) this._addInfo(block);
		if (eventFloor) {
			if (core.getFlag('R' + eventFloor.floorId + "," + block.x + "," + block.y, false) !== true) {
				this._addEvent(block, x, y, (eventFloor.events || {})[x + "," + y]);
				var changeFloor = (eventFloor.changeFloor || {})[x + "," + y];
				if (changeFloor) this._addEvent(block, x, y, { "trigger": "changeFloor", "data": changeFloor });
			}
		}
		if (main.mode == 'editor') delete block.disable;
		return block;
	}
	events.prototype._action_hide = function (data, x, y, prefix) {
		data.loc = this.__action_getLoc2D(data.loc, x, y, prefix);
		if (data.time > 0 && data.floorId == core.status.floorId) {
			this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, data.destruct ? "destruct" : data.remove ? 'remove' : 'hide', data.time);
		} else {
			data.loc.forEach(function (t) {
				if (data.destruct) core.removeBlockThoroughly(t[0], t[1], data.floorId);
				else if (data.remove) core.removeBlock(t[0], t[1], data.floorId);
				else core.hideBlock(t[0], t[1], data.floorId);
			});
			core.doAction();
		}
	}

	/**
	 * 动作：设置图块（入口函数）
	 * @param {object} data 事件数据
	 * @param {number} x X坐标
	 * @param {number} y Y坐标
	 * @param {string} prefix 前缀
	 */
	events.prototype._action_setBlock = function (data, x, y, prefix) {
		data.loc = this.__action_getLoc2D(data.loc, x, y, prefix);
		data.time = data.time || 0;
		data.floorId = data.floorId || core.status.floorId;
		data.destruct = data.destruct || false; // 确保 destruct 有默认值

		// 如果有动画时间且在当前楼层，则执行动画
		if (data.time > 0 && data.floorId == core.status.floorId) {
			// 调用唯一的动画处理函数
			this.__action_doAsyncFunc(data.async, core.animateSetBlocks, data.number, data.loc, data.floorId, data.time, data.destruct, core.doAction);
		} else {
			// 否则直接设置
			data.loc.forEach(function (loc) {
				if (data.destruct) {
					core.setBlockThoroughly(data.number, loc[0], loc[1], data.floorId);
				} else {
					core.setBlock(data.number, loc[0], loc[1], data.floorId);
				}
			});
			core.doAction();
		}
	};

	/**
	 * [已合并] 批量播放设置图块的动画，并处理所有逻辑
	 * @param {number|string} number 图块ID或名称
	 * @param {Array} locs 坐标数组
	 * @param {string} floorId 楼层ID
	 * @param {number} time 动画时间
	 * @param {boolean} destruct 是否彻底清除
	 * @param {function} callback 动画全部完成后的回调
	 */
	core.animateSetBlocks = core.maps.animateSetBlocks = function (number, locs, floorId, time, destruct, callback) {
		// --- 1. 初始化和参数检查 ---
		if (!(locs instanceof Array)) {
			if (callback) callback();
			return;
		}
		if (typeof locs[0] == 'number' && typeof locs[1] == 'number') {
			locs = [locs];
		}

		var count = locs.length;
		if (count === 0) {
			if (callback) callback();
			return;
		}

		// 动画计数器，在所有动画结束后执行回调
		var _afterSet = function () {
			count--;
			if (count === 0 && callback) {
				callback();
			}
		};

		var setBlockFn = destruct ? core.setBlockThoroughly : core.setBlock;

		// --- 2. 遍历每个坐标并执行动画 ---
		locs.forEach(function (loc) {
			var x = loc[0],
				y = loc[1];

			var currentNumber = number;
			if (typeof currentNumber == 'string') {
				if (/^\d+$/.test(currentNumber)) currentNumber = parseInt(currentNumber);
				else currentNumber = core.getNumberById(currentNumber);
			}
			var originBlock = core.getBlock(x, y, floorId, true);
			var newBlock = core.maps.initBlock(x, y, currentNumber, true, core.floors[floorId]);

			// --- 3. 根据原始图块状态，应用不同动画策略 ---

			// 策略A：原始图块存在且启用
			if (originBlock != null && !originBlock.disable && originBlock.id !== 0) {
				if (newBlock.id == 0) { // 设置为0（清除图块）
					if (!newBlock.event.trigger) { // 无事件，直接移除
						core.animateBlock([x, y], 'remove', time, function () {
							setBlockFn(0, x, y, floorId);
							_afterSet();
						});
					} else { // 有事件，则隐藏图块但保留事件
						core.animateBlock([x, y], 'hide', time, function () {
							setBlockFn(0, x, y, floorId);
							core.showBlock(x, y, floorId);
							_afterSet();
						});
					}
				} else { // 设置为非0（替换图块）
					core.animateBlock([x, y], 'hide', time / 2, function () {
						setBlockFn(currentNumber, x, y, floorId);
						core.animateBlock([x, y], 'show', time / 2, _afterSet);
					});
				}
			}
			// 策略B：原始图块不存在
			else if (originBlock == null || originBlock.id === 0) {
				if (newBlock.id == 0) { // 设置为0，无操作
					setBlockFn(0, x, y, floorId);
					_afterSet();
				} else { // 设置为非0，淡入显示
					setBlockFn(currentNumber, x, y, floorId);
					core.hideBlock(x, y, floorId);
					core.animateBlock([x, y], 'show', time, _afterSet);
				}
			}
			// 策略C：原始图块被禁用（直接设置，无动画）
			else {
				setBlockFn(currentNumber, x, y, floorId);
				_afterSet();
			}
		});
	};
	maps.prototype.animateBlock = function (loc, type, time, callback) {
		if (core.status.replay.speed == 24) time = 1;
		if (typeof loc[0] == 'number' && typeof loc[1] == 'number')
			loc = [loc];
		if (type != 'show' && type != 'hide' && type != 'remove' && type != 'destruct' && typeof type != 'number') {
			if (callback) callback();
		}
		// --- 检测所有是0的点
		var list = this._animateBlock_getList(loc, type);
		if (list.length == 0) {
			if (callback) callback();
			return;
		}
		this._animateBlock_drawList(list, 0);
		time /= Math.max(core.status.replay.speed, 1)
		this._animateBlock_doAnimate(loc, list, type, time, callback);
	}
	maps.prototype._animateBlock_doAnimate = function (loc, list, type, time, callback) {
		var step = 0,
			steps = Math.max(parseInt(time / 10), 1);
		var cb = function () {
			list.forEach(function (t) {
				if (t.blockInfo)
					core.maps._deleteDetachedBlock(t.canvases);
			});
			loc.forEach(function (t) {
				if (type == 'show') core.showBlock(t[0], t[1]);
				else if (type == 'hide') core.hideBlock(t[0], t[1]);
				else if (type == 'remove') core.removeBlock(t[0], t[1]);
				else if (type == 'destruct') core.removeBlockThoroughly(t[0], t[1]);
				else {
					core.setBlockOpacity(type, t[0], t[1]);
					core.showBlock(t[0], t[1]);
				}
			});
			if (callback) callback();
		}

		var animate = setInterval(function () {
			step++;
			core.maps._animateBlock_drawList(list, step / steps);
			if (step == steps) {
				delete core.animateFrame.asyncId[animate];
				clearInterval(animate);
				cb();
			}
		}, 10);

		core.animateFrame.lastAsyncId = animate;
		core.animateFrame.asyncId[animate] = cb;
	}
	maps.prototype._animateBlock_getList = function (loc, type) {
		var list = [];
		loc.forEach(function (t) {
			var block = core.getBlock(t[0], t[1], null, true);
			if (block == null) return;

			var fromOpacity = block.opacity;
			if (fromOpacity == null) fromOpacity = 1.0;

			var blockInfo = core.maps.getBlockInfo(block);
			if (blockInfo == null) {
				list.push({ 'x': t[0], 'y': t[1] });
				return;
			}
			if (typeof type == 'number' && block.disable) return;
			// 该点是否已经被启用/删除
			if ((type == 'show' && !block.disable) || ((type == 'hide' || type == 'remove' || type == 'destruct') && block.disable)) {
				list.push({ 'x': t[0], 'y': t[1] });
				return;
			}

			var toOpacity = type;
			if (type == 'show') {
				toOpacity = fromOpacity;
				fromOpacity = 0.0;
			} else if (type == 'hide' || type == 'remove' || type == 'destruct') {
				core.hideBlock(t[0], t[1]); // 暂时先隐藏
				toOpacity = 0.0;
			} else {
				core.hideBlock(t[0], t[1]); // 暂时先隐藏
			}

			var canvases = core.maps._initDetachedBlock(blockInfo, t[0], t[1], block.event.displayDamage !== false);

			list.push({
				'x': t[0],
				'y': t[1],
				'blockInfo': blockInfo,
				'canvases': canvases,
				'fromOpacity': fromOpacity,
				'toOpacity': toOpacity,
			});

		});
		return list;
	}
},
    "新增功能": function () {
	core.plugin.showGameOver = function (callback) {
		// 使用 createCanvas 创建专用画布
		const canvasWidth = core.__PIXELS__ || 800;
		const canvasHeight = core.__PIXELS__ || 600;

		let ctx = null;
		try {
			// 创建专用的游戏结束画布，z-index 设置高一些确保在最上层
			ctx = core.ui.createCanvas('gameOverCanvas', 0, 0, canvasWidth, canvasHeight, 9999);
		} catch (e) {
			console.error('创建游戏结束画布失败:', e);
			if (typeof callback === 'function') callback();
			return () => {};
		}

		if (!ctx) {
			console.error('showGameOver: 创建画布上下文失败。');
			if (typeof callback === 'function') callback();
			return () => {};
		}

		// 参数设置 - 调整字体大小
		const fontName = 'WenDingNiuNiuTi';
		const text = 'Game Over';
		const duration = 2000;
		const fadeInEnd = 0.25;
		const holdEnd = 0.60;
		const fontSize = 70; // 调大字体
		const startOffsetY = 20;
		const endOffsetY = -20;

		// 清理函数 - 移除画布
		function clearAnimate() {
			try {
				const canvas = document.getElementById('gameOverCanvas');
				if (canvas && canvas.parentNode) {
					canvas.parentNode.removeChild(canvas);
				}
				delete core.dymCanvas['gameOverCanvas'];
			} catch (e) {
				console.warn('清理画布时出错:', e);
			}
		}

		// 确保字体加载完成
		function ensureFontLoaded() {
			if (document && document.fonts && document.fonts.load) {
				return document.fonts.load(`${fontSize}px "${fontName}"`).catch(() => Promise.resolve());
			}
			return Promise.resolve();
		}

		// 平滑曲线函数
		function easeInOutCubic(t) {
			return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
		}

		let rafId = null;
		let canceled = false;

		function cancel() {
			canceled = true;
			if (rafId) cancelAnimationFrame(rafId);
			rafId = null;
			clearAnimate();
		}

		ensureFontLoaded().then(() => {
			if (canceled) return;

			const start = performance.now();

			function frame(now) {
				if (canceled) return;
				const elapsed = now - start;
				const t = Math.min(elapsed / duration, 1);

				// 计算 opacity 和 y 偏移
				let opacity, offsetY;
				if (t <= fadeInEnd) {
					const p = t / fadeInEnd;
					opacity = p;
					offsetY = startOffsetY * (1 - p);
				} else if (t <= holdEnd) {
					opacity = 1;
					offsetY = 0;
				} else {
					const p = (t - holdEnd) / (1 - holdEnd);
					const e = easeInOutCubic(p);
					opacity = 1 - e;
					offsetY = endOffsetY * e;
				}

				// 清空画布
				ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

				const x = canvasWidth / 2;
				const y = canvasHeight / 2 + offsetY;

				ctx.save();
				ctx.font = `${fontSize}px "${fontName}", sans-serif`;
				ctx.textAlign = 'center';
				ctx.textBaseline = 'middle';
				ctx.globalAlpha = opacity;

				// 渐变效果
				const grad = ctx.createLinearGradient(0, y - fontSize, 0, y + fontSize);
				grad.addColorStop(0, '#ff1515');
				grad.addColorStop(1, '#ffcdcd');
				ctx.fillStyle = grad;

				ctx.fillText(text, x, y);
				ctx.restore();

				if (t < 1) {
					rafId = requestAnimationFrame(frame);
				} else {
					// 动画结束后延迟清理
					setTimeout(() => {
						if (!canceled) {
							clearAnimate();
							if (typeof callback === 'function') callback();
						}
					}, 80);
				}
			}

			rafId = requestAnimationFrame(frame);
		});

		return cancel;
	};
	//地图显伤临界变色
	control.prototype._updateDamage_damage = function (floorId, onMap) {
		core.status.damage.data = [];
		if (!core.flags.displayEnemyDamage && !core.flags.displayExtraDamage) return;

		core.extractBlocks(floorId);
		core.status.maps[floorId].blocks.forEach(function (block) {
			var x = block.x,
				y = block.y;

			// v2优化，只绘制范围内的部分
			if (onMap && core.bigmap.v2) {
				if (x < core.bigmap.posX - core.bigmap.extend || x > core.bigmap.posX + core.__SIZE__ + core.bigmap.extend ||
					y < core.bigmap.posY - core.bigmap.extend || y > core.bigmap.posY + core.__SIZE__ + core.bigmap.extend) {
					return;
				}
			}

			if (!block.disable && block.event.cls.indexOf('enemy') == 0 && block.event.displayDamage !== false) {
				if (core.flags.displayEnemyDamage) {
					var damageString = core.enemys.getDamageString(block.event.id, x, y, floorId);
					core.status.damage.data.push({ text: damageString.damage, px: 32 * x + 1, py: 32 * (y + 1) - 1, color: damageString.color });
				}
				if (core.flags.displayCritical) {
					// 获取临界数据 [攻击增量, 伤害变化量, 破防标记]
					var criticalArr = core.enemys.nextCriticals(block.event.id, 1, x, y, floorId);
					var crit = criticalArr[0] || [];
					var atkUp = crit[0]; // 攻击增量
					var dmgDiff = crit[1]; // 伤害变化量（判断颜色用）
					//    var isBreakDef = crit[2] === 1; // 破防

					// 颜色规则 1:1 匹配书本界面
					var color = '#FFFFFF'; // 默认白色
					//if (isBreakDef) {
					//  color = '#FFD700'; // 破防 → 金色
					//} 
					if (typeof dmgDiff === 'number' && dmgDiff < 0) {
						color = '#FF0000'; // 伤害提高 → 红色
					}

					// 格式化显示
					var criticalText = core.formatBigNumber(atkUp, true);
					if (criticalText === '???') criticalText = '?';

					core.status.damage.data.push({
						text: criticalText,
						px: 32 * x + 1,
						py: 32 * (y + 1) - 11,
						color: color
					});
				}
			}
		});
	};
	// ================================================================
	// 通用背景绘制插件：用于换层、手册、飞轴
	// ================================================================
	core.plugin._drawMTBackground = function (ctx, alpha, isTransition) {
		var width = core.__PIXELS__;
		var height = core.__PIXELS__;

		ctx.save();
		// 换层动画需要整体透明度变化，UI背景则通常是完全不透明
		ctx.globalAlpha = alpha;

		// 1. 绘制底色：改为纯黑色，不透明
		ctx.fillStyle = '#000000';
		if (isTransition) {
			var offset = 31;
			ctx.fillRect(offset, offset, width - offset * 2, height - offset * 2);
			ctx.fillStyle = '#FFFFFF';

			// 2. 绘制装饰文字 "魔塔"
			var fontSize = Math.floor(width * 0.2);
			ctx.font = fontSize + 'px "ChaoZiSheZengYuBoShouShuJianFan", sans-serif';
			ctx.textAlign = 'center';
			ctx.textBaseline = 'middle';
			// 绘制文字
			ctx.fillText('魔', width * 0.4, height * 0.4);
			ctx.fillText('塔', width * 0.6, height * 0.6);

		} else {
			ctx.fillRect(0, 0, width, height);
			ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';

			// 2. 绘制装饰文字 "魔塔"
			var fontSize = Math.floor(width * 0.25);
			ctx.font = fontSize + 'px "ChaoZiSheZengYuBoShouShuJianFan", sans-serif';
			ctx.textAlign = 'center';
			ctx.textBaseline = 'middle';
			// 绘制文字
			ctx.fillText('魔', width * 0.35, height * 0.35);
			ctx.fillText('塔', width * 0.65, height * 0.65);
		}


		ctx.restore();
	};

	// ================================================================
	// 楼层切换动画重构
	// ================================================================

	core.plugin._createFloorTransitionOverlay = function () {
		core.deleteCanvas('floorTransitionOverlay');
		core.createCanvas('floorTransitionOverlay', 0, 0, core.__PIXELS__, core.__PIXELS__, 100);
		return core.getContextByName('floorTransitionOverlay');
	};

	var easeInOut = function (t) {
		return -(Math.cos(Math.PI * t) - 1) / 2;
	};

	core.plugin._showFloorTransition = function (duration, callback) {
		var ctx = core.plugin._createFloorTransitionOverlay();
		if (!ctx) { if (callback) callback(); return; }

		var start = performance.now();

		function step(now) {
			var t = Math.min((now - start) / duration, 1);
			var alpha = easeInOut(t);

			ctx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__);
			// 传入 isTransition = true
			core.plugin._drawMTBackground(ctx, alpha, true);

			if (t < 1) requestAnimationFrame(step);
			else if (callback) callback();
		}
		requestAnimationFrame(step);
	};

	core.plugin._hideFloorTransition = function (duration, callback) {
		var ctx = core.getContextByName('floorTransitionOverlay');
		if (!ctx) { if (callback) callback(); return; }

		var start = performance.now();

		function step(now) {
			var t = Math.min((now - start) / duration, 1);
			var alpha = easeInOut(1 - t);

			ctx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__);
			// 传入 isTransition = true
			core.plugin._drawMTBackground(ctx, alpha, true);

			if (t < 1) requestAnimationFrame(step);
			else {
				core.deleteCanvas('floorTransitionOverlay');
				if (callback) callback();
			}
		}
		requestAnimationFrame(step);
	};
	events.prototype._changeFloor_getInfo = function (floorId, stair, heroLoc, time) {
		floorId = floorId || core.status.floorId;
		if (floorId == ':before') {
			var index = core.floorIds.indexOf(core.status.floorId);
			if (index > 0) floorId = core.floorIds[index - 1];
			else floorId = core.status.floorId;
		} else if (floorId == ':next') {
			var index = core.floorIds.indexOf(core.status.floorId);
			if (index < core.floorIds.length - 1) floorId = core.floorIds[index + 1];
			else floorId = core.status.floorId;
		} else if (floorId == ':now') {
			floorId = core.status.floorId;
		}
		if (!core.status.maps[floorId]) {
			main.log("不存在的楼层：" + floorId);
			return null;
		}

		if (main.mode != 'play' || core.isReplaying()) time = 0;
		if (time == null) time = core.values.floorChangeTime;
		//time /= 20;

		return {
			floorId: floorId,
			time: time,
			heroLoc: core.clone(this._changeFloor_getHeroLoc(floorId, stair, heroLoc))
		};
	}
	events.prototype._changeFloor_beforeChange = function (info, callback) {
		this._changeFloor_playSound();
		var self = this;
		if (info.time == 0) {
			core.events._changeFloor_changing(info, callback);
		} else {
			core.plugin._showFloorTransition(info.time / 2, function () {
				core.events._changeFloor_changing(info, callback);
			});
		}
	};

	events.prototype._changeFloor_changing = function (info, callback) {
		this.changingFloor(info.floorId, info.heroLoc);
		var __lockViewport__ = core.flags.__lockViewport__;
		core.setFlag('__lockViewport__', null);
		core.drawHero();
		core.setFlag('__lockViewport__', __lockViewport__);

		if (info.time == 0) {
			this._changeFloor_afterChange(info, callback);
		} else {
			core.plugin._hideFloorTransition(info.time / 2, function () {
				core.events._changeFloor_afterChange(info, callback);
			});
		}
	};

	// ================================================================
	// UI界面背景适配 (不透明纯黑，无纹理)
	// ================================================================

	// 1. 怪物手册背景
	ui.prototype._drawBook_drawBackground = function () {
		var ctx = core.getContextByName('ui');
		core.clearMap('ui');

		// 直接调用通用背景：不透明(1.0)，全屏(true)，非换层(false)
		core.plugin._drawMTBackground(ctx, 1.0, false);
	};

	// 2. 楼层跳跃界面
	ui.prototype.drawFly = function (page) {
		core.status.event.data = page;
		var floorId = core.floorIds[page];
		var title = core.status.maps[floorId].title;
		var ctx = core.getContextByName('ui');

		core.clearMap('ui');

		// 绘制不透明魔塔背景，无额外纹理
		core.plugin._drawMTBackground(ctx, 1.0, false);

		core.setTextAlign('ui', 'center');
		core.fillText('ui', '楼层跳跃', this.HPIXEL, 60, '#FFFFFF', this._buildFont(28, true));
		core.fillText('ui', '返回游戏', this.HPIXEL, this.PIXEL - 13, null, this._buildFont(15, true));

		core.setTextAlign('ui', 'right');
		core.fillText('ui', '浏览地图时也', this.PIXEL - 10, this.PIXEL - 23, '#aaaaaa', this._buildFont(10, false));
		core.fillText('ui', '可楼层跳跃！', this.PIXEL - 10, this.PIXEL - 11, null, this._buildFont(10, false));

		core.setTextAlign('ui', 'center');
		var middle = this.HPIXEL + 39;

		// 绘制预览图标题
		var lines = core.splitLines('ui', title, 120, this._buildFont(19, true));
		var start_y = middle - (lines.length - 1) * 11;
		for (var i in lines) {
			core.fillText('ui', lines[i], this.PIXEL - 60, start_y, '#FFFFFF');
			start_y += 22;
		}

		// 绘制翻页按钮
		var arrowX = this.PIXEL - 60;
		if (core.actions._getNextFlyFloor(1) != page) {
			core.fillText('ui', '▲', arrowX, middle - 48, null, this._buildFont(20, false));
			core.fillText('ui', '▲', arrowX, middle - 96);
			core.fillText('ui', '▲', arrowX, middle - 103);
		}
		if (core.actions._getNextFlyFloor(-1) != page) {
			core.fillText('ui', '▼', arrowX, middle + 48, null, this._buildFont(20, false));
			core.fillText('ui', '▼', arrowX, middle + 96);
			core.fillText('ui', '▼', arrowX, middle + 103);
		}

		var size = this.PIXEL - 143;
		core.strokeRect('ui', 20, 100, size, size, '#FFFFFF', 2);
		core.drawThumbnail(floorId, null, { ctx: 'ui', x: 20, y: 100, size: size, damage: true });
	};

	actions.prototype._clickFly = function (x, y, px, py) {
		// 使用像素坐标而不是格子坐标
		var arrowX = core.ui.PIXEL - 60; // 箭头中心X坐标
		var middle = core.ui.HPIXEL + 39; // 中间Y坐标

		// 计算各个区域的像素坐标范围
		var upAreaLeft = arrowX - 50; // 向上翻页区域左边界
		var upAreaRight = arrowX + 50; // 向上翻页区域右边界
		var upAreaTop = middle - 130; // 向上翻页区域顶部
		var upAreaBottom = middle - 20; // 向上翻页区域底部

		var downAreaLeft = arrowX - 50; // 向下翻页区域左边界
		var downAreaRight = arrowX + 50; // 向下翻页区域右边界
		var downAreaTop = middle + 20; // 向下翻页区域顶部
		var downAreaBottom = middle + 130; // 向下翻页区域底部

		// 返回按钮区域（"返回游戏"文本）
		var backBtnLeft = core.ui.HPIXEL - 50; // 返回按钮左边界
		var backBtnRight = core.ui.HPIXEL + 50; // 返回按钮右边界
		var backBtnTop = core.ui.PIXEL - 30; // 返回按钮顶部
		var backBtnBottom = core.ui.PIXEL; // 返回按钮底部

		// 楼层跳跃确认区域（缩略图）
		var size = core.ui.PIXEL - 143;
		var confirmLeft = 20; // 确认区域左边界
		var confirmRight = 20 + size; // 确认区域右边界
		var confirmTop = 100; // 确认区域顶部
		var confirmBottom = 100 + size; // 确认区域底部

		// 检查点击位置是否在向上翻页区域内
		if (px >= upAreaLeft && px <= upAreaRight && py >= upAreaTop && py <= upAreaBottom) {
			// 判断是翻1页还是10页（根据Y坐标在区域内的位置）
			if (py <= middle - 90) {
				// 上半部分：翻10页
				core.playSound('光标移动');
				core.ui.drawFly(this._getNextFlyFloor(10));
			} else {
				// 下半部分：翻1页
				core.playSound('光标移动');
				core.ui.drawFly(this._getNextFlyFloor(1));
			}
		}
		// 检查点击位置是否在向下翻页区域内
		else if (px >= downAreaLeft && px <= downAreaRight && py >= downAreaTop && py <= downAreaBottom) {
			// 判断是翻1页还是10页（根据Y坐标在区域内的位置）
			if (py <= middle + 70) {
				// 上半部分：翻1页
				core.playSound('光标移动');
				core.ui.drawFly(this._getNextFlyFloor(-1));
			} else {
				// 下半部分：翻10页
				core.playSound('光标移动');
				core.ui.drawFly(this._getNextFlyFloor(-10));
			}
		}
		// 检查点击位置是否在返回按钮区域内
		else if (px >= backBtnLeft && px <= backBtnRight && py >= backBtnTop && py <= backBtnBottom) {
			core.playSound('取消');
			core.ui.closePanel();
		}
		// 检查点击位置是否在楼层跳跃确认区域内
		else if (px >= confirmLeft && px <= confirmRight && py >= confirmTop && py <= confirmBottom) {
			core.flyTo(core.floorIds[core.status.event.data]);
		}
		return;
	}
	actions.prototype._keyUpFly = function (keycode) {
		if (keycode == 71 || keycode == 27 || keycode == 88) {
			core.playSound('取消');
			core.ui.closePanel();
		}
		if (keycode == 13 || keycode == 32 || keycode == 67)
			core.flyTo(core.floorIds[core.status.event.data]);
		return;
	}
	core.registerAction('longClick', '_sys_longClick_lockControl', function (x, y, px, py) {
		if (!core.status.lockControl) return false;
		if (core.status.event.id == 'text') {
			core.drawText();
			return true;
		}
		if (core.status.event.id == 'action' && core.status.event.data.type == 'text') {
			core.doAction();
			return true;
		}
		// 长按楼传器的箭头可以快速翻页 - 使用像素坐标
		if (core.status.event.id == 'fly') {
			// 计算翻页按钮的像素坐标区域
			var arrowX = core.ui.PIXEL - 60;
			var middle = core.ui.HPIXEL + 39;

			// 计算各个区域的像素坐标范围
			var upAreaLeft = arrowX - 50; // 向上翻页区域左边界
			var upAreaRight = arrowX + 50; // 向上翻页区域右边界
			var upAreaTop = middle - 130; // 向上翻页区域顶部
			var upAreaBottom = middle - 20; // 向上翻页区域底部

			var downAreaLeft = arrowX - 50; // 向下翻页区域左边界
			var downAreaRight = arrowX + 50; // 向下翻页区域右边界
			var downAreaTop = middle + 20; // 向下翻页区域顶部
			var downAreaBottom = middle + 130; // 向下翻页区域底部

			// 检查是否点击在翻页区域
			if ((px >= upAreaLeft && px <= upAreaRight && py >= upAreaTop && py <= upAreaBottom) ||
				(px >= downAreaLeft && px <= downAreaRight && py >= downAreaTop && py <= downAreaBottom)) {
				core.actions._clickFly(x, y, px, py);
				return true;
			}
		}
		// 长按SL上下页快速翻页 - 这里需要根据您的SL界面具体绘制位置来修改
		if (["save", "load", "replayLoad", "replayRemain", "replaySince"].indexOf(core.status.event.id) >= 0) {
			// 需要根据SL界面的实际像素坐标来修改
			// 暂时保留原逻辑，但建议您根据SL界面的实际绘制位置来设置像素坐标区域
			if ([this.HSIZE - 2, this.HSIZE - 3, this.HSIZE + 2, this.HSIZE + 3].indexOf(x) >= 0 && y == this.LAST) {
				this._clickSL(x, y, px, py);
				return true;
			}
		}
		// 长按可以跳过等待事件
		if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' &&
			!core.status.event.data.current.noSkip) {
			if (core.timeout.sleepTimeout && !core.hasAsync()) {
				clearTimeout(core.timeout.sleepTimeout);
				core.timeout.sleepTimeout = null;
				core.doAction();
				return true;
			}
		}
		return false;
	}, 50);

	ui.prototype.fillBoldText = function (name, text, x, y, style, strokeStyle, font, maxWidth) {
		var ctx = this.getContextByName(name);
		if (!ctx) return;
		if (font) ctx.font = font;
		if (!style) style = ctx.fillStyle;
		style = core.arrayToRGBA(style);

		// 解析 strokeStyle 参数
		var strokeColor = '#000000';
		var strokeWidth = 2; // 默认宽度

		if (strokeStyle) {
			if (typeof strokeStyle === 'string') {
				// 字符串格式：只指定颜色
				strokeColor = core.arrayToRGBA(strokeStyle);
			} else if (Array.isArray(strokeStyle)) {
				// 数组格式：[颜色, 宽度]
				strokeColor = core.arrayToRGBA(strokeStyle[0]);
				strokeWidth = strokeStyle[1] || 2;
			} else if (typeof strokeStyle === 'object') {
				// 对象格式：{color: 颜色, width: 宽度}
				strokeColor = core.arrayToRGBA(strokeStyle.color || '#000000');
				strokeWidth = strokeStyle.width || 2;
			}
		} else {
			strokeColor = core.arrayToRGBA('#000000');
		}

		if (maxWidth != null) {
			this.setFontForMaxWidth(ctx, text, maxWidth);
		}

		ctx.strokeStyle = strokeColor;
		ctx.lineWidth = strokeWidth;
		ctx.strokeText(text, x, y);
		ctx.fillStyle = style;
		ctx.fillText(text, x, y);
	}

	////// 播放背景音乐 //////
	control.prototype.playBgm = function (bgm, startTime) {
		bgm = core.getMappedName(bgm);
		if (main.mode != 'play' || !core.material.bgms[bgm]) return;
		if (core.getFlag('禁止BGM', false)) return;
		// 如果不允许播放
		if (!core.musicStatus.bgmStatus) {
			try {
				core.musicStatus.playingBgm = bgm;
				core.musicStatus.lastBgm = bgm;
				core.material.bgms[bgm].pause();
			} catch (e) {
				main.log(e);
			}
			return;
		}
		this.setMusicBtn();

		try {
			this._playBgm_play(bgm, startTime);
		} catch (e) {
			console.log("无法播放BGM " + bgm);
			main.log(e);
			core.musicStatus.playingBgm = null;
		}
	}
	// 新增：移动前快照
	function _snapshotBeforeMove() {
		const loc = core.status.hero.loc;
		core.status.__moveSnapshot__ = {
			x: loc.x,
			y: loc.y,
			direction: loc.direction,
			routeLength: core.status.route.length
		};
	};
	//快照存档
	this.autosaveFromSnapshot = function () {
		const s = core.status.__moveSnapshot__;
		if (!s) return;

		var removeLast = core.status.route.length > s.routeLength;

		const data = {
			heroLoc: { x: s.x, y: s.y, direction: s.direction }
		};

		if (core.hasFlag('__forbidSave__')) return;
		var x = null;
		if (removeLast) {
			x = core.status.route.pop();
			//core.status.route.push("turn:" + core.getHeroLoc('direction'));
		}
		if (core.status.event.id == 'action') // 事件中的自动存档
			core.setFlag("__events__", core.clone(core.status.event.data));
		if (core.saves.autosave.data == null) {
			core.saves.autosave.data = [];
		}
		core.saves.autosave.data.splice(core.saves.autosave.now, 0, core.saveData(data));
		core.saves.autosave.now += 1;
		if (core.saves.autosave.data.length > core.saves.autosave.max) {
			if (core.saves.autosave.now < core.saves.autosave.max / 2)
				core.saves.autosave.data.pop();
			else {
				core.saves.autosave.data.shift();
				core.saves.autosave.now = core.saves.autosave.now - 1;
			}
		}
		core.saves.autosave.updated = true;
		core.saves.ids[0] = true;
		core.removeFlag("__events__");
		if (removeLast) {
			//core.status.route.pop();
			if (x) core.status.route.push(x);
		}

		//delete core.status.__moveSnapshot__;
	};
	core.saveData = function (data) {
		return core.control.saveData(data);
	}
	control.prototype.saveData = function (data) {
		return core.control.controldata.saveData(data);
	}
	// 1. 修改 moveHero 入口
	control.prototype.moveHero = function (direction, callback) {
		if (core.status.heroMoving != 0) return;
		// 统一交给 _moveHero_moving 处理，确保状态一致性
		core._moveHero_moving(direction, callback);
	}

	// 2. 修改移动核心循环（执行单格移动动画与逻辑结算）
	control.prototype._moveAction_moving = function (callback) {
		let nowx = core.getHeroLoc('x'),
			nowy = core.getHeroLoc('y');

		core.setHeroMoveInterval(function () {
			// A. 记录当前朝向并记录路径
			var direction = core.getHeroLoc('direction');
			core.status.route.push(direction);

			// B. 更新位置
			core.setHeroLoc('x', core.nextX(), true);
			core.setHeroLoc('y', core.nextY(), true);

			// C. 执行移动结算（包含碰撞、拾取、触发楼梯等事件）
			core.moveOneStep(nowx, nowy);
			core.checkRouteFolding();

			// D. 处理寻路计数
			// 注意：如果是最后一步，autoHeroMove 已经在 _moveHero_moving 中被置为 false
			// 这里增加判断，防止由于 stopAutomaticRoute 导致的二次处理
			if (core.status.automaticRoute.autoHeroMove) {
				core.control._moveAction_popAutomaticRoute();
			}

			if (callback) callback();
		});
	}

	// 3. 修改寻路阶段切换逻辑（仅负责数据步进和视觉转向）
	control.prototype._moveAction_popAutomaticRoute = function () {
		var automaticRoute = core.status.automaticRoute;

		// 增加已走步数
		automaticRoute.movedStep++;
		automaticRoute.lastDirection = core.getHeroLoc('direction');

		// 检查当前阶段（同方向的一段路）是否走完
		if (automaticRoute.destStep == automaticRoute.movedStep) {
			// 检查是否所有路段都走完了
			if (automaticRoute.autoStep == automaticRoute.autoStepRoutes.length) {
				core.clearContinueAutomaticRoute();
				core.stopAutomaticRoute();
			} else {
				// 进行下一阶段的初始化
				automaticRoute.movedStep = 0;
				var nextRoute = automaticRoute.autoStepRoutes[automaticRoute.autoStep];
				automaticRoute.destStep = nextRoute.step;

				// 执行物理转向（此时 moveOneStep 已经跑完，转向是安全的）
				_snapshotBeforeMove();
				core.setHeroLoc('direction', nextRoute.direction);

				core.status.automaticRoute.autoStep++;
			}
		}
	}

	// 4. 修改寻路主逻辑（递归轮询器，含预判结算逻辑）
	core._moveHero_moving = core.control._moveHero_moving = function (direction, callback) {
		core.status.heroStop = false;
		core.status.automaticRoute.moveDirectly = false;

		// 初始移动前快照（用于回滚到移动前的朝向）
		_snapshotBeforeMove();

		// 改变勇士朝向
		if (core.isset(direction))
			core.setHeroLoc('direction', direction);

		var move = () => {
			if (core.status.heroStop) return;

			// —— 场景 1：Debug+Ctrl 穿墙 ——
			if (core.hasFlag('debug') && core.status.ctrlDown && core.status.heroMoving === 0) {
				var nx = core.nextX(),
					ny = core.nextY();
				if (nx < 1 || nx >= core.bigmap.width - 1 || ny < 1 || ny >= core.bigmap.height - 1) return;
				core.events.eventMoveHero([core.getHeroLoc('direction') + ':1'], core.values.moveSpeed, move);
			}
			// —— 场景 2：自动寻路核心逻辑 ——
			else if (core.status.automaticRoute.autoStepRoutes.length > 0) {
				var automaticRoute = core.status.automaticRoute;

				// 【预判逻辑】检查这是否是路径的绝对最后一格
				var isLastStepOfSegment = (automaticRoute.destStep - automaticRoute.movedStep === 1);
				var isLastSegment = (automaticRoute.autoStep === automaticRoute.autoStepRoutes.length);

				if (isLastStepOfSegment && isLastSegment) {
					// 如果是最后一格，在迈步前“干净地”关闭寻路状态
					// 这样 moveOneStep 触发楼梯时，引擎检查到寻路已结束，不会触发打断保存逻辑
					core.stopAutomaticRoute();
					// 降级为普通移动，并透传最终的回调
					core.moveAction(callback);
					return; // 终止递归
				}

				// 如果不是最后一格，正常执行一步移动
				core.moveAction();

				setTimeout(() => {
					// 递归前的准备：如果还有下一步，记录撤退快照
					if (core.status.automaticRoute.autoStepRoutes.length > 0) {
						_snapshotBeforeMove();
						move();
					}
				}, 50);
			}
			// —— 场景 3：手动按键或单步带回调 ——
			else {
				core.moveAction(callback);
				core.status.heroStop = true;
			}
		};

		move();
	};

	// 5. 移动动画时钟函数
	control.prototype.setHeroMoveInterval = function (callback) {
		if (core.status.heroMoving > 0) return;
		if (core.status.replay.speed == 24) {
			core.status.heroStop = true;
			if (callback) callback();
			return;
		}

		core.status.heroMoving = 1;
		var toAdd = 1;
		if (core.status.replay.speed > 3) toAdd = 2;
		if (core.status.replay.speed > 6) toAdd = 4;
		if (core.status.replay.speed > 12) toAdd = 8;

		core.interval.heroMoveInterval = window.setInterval(function () {
			core.status.heroMoving += toAdd;
			if (core.status.heroMoving >= 8) {
				clearInterval(core.interval.heroMoveInterval);
				core.status.heroMoving = 0;
				if (callback) callback();
			}
		}, core.values.moveSpeed / 8 * toAdd / core.status.replay.speed);
	}
	events.prototype._sys_battle = function (data, callback) {
		// 检查是否需要改变朝向
		/* if (data.x == core.nextX() && data.y == core.nextY()) {
		    var dir = core.turnDirection(":back");
		    var id = data.event.id, toId = (data.event.faceIds || {})[dir];
		    if (toId && id != toId) {
		        var number = core.getNumberById(toId);
		        if (number > 0)
		            core.setBlock(number, data.x, data.y);
		    }
		} */

		// 检查战前事件
		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.autosaveFromSnapshot();
				core.insertAction(beforeBattle, data.x, data.y, callback);
			}
		} else {
			this.battle(data.event.id, data.x, data.y, false, callback);
		}
	}
	events.prototype._openDoor_check = function (block, x, y, needKey) {
		var clearAndReturn = function () {
			core.clearContinueAutomaticRoute();
			return false;
		}

		if (block == null || block.event == null) return clearAndReturn();
		var id = block.event.id;

		// 是否存在门或暗墙
		if (core.material.icons.animates[id] == null && core.material.icons.npc48[id] == null) {
			return clearAndReturn();
		}

		if (id == 'steelDoor' && core.flags.steelDoorWithoutKey)
			needKey = false;
		var doorInfo = block.event.doorInfo;
		if (doorInfo == null) return clearAndReturn();
		// Check all keys
		var keyInfo = doorInfo.keys || {};
		if (needKey) {
			for (var keyName in keyInfo) {
				var keyValue = keyInfo[keyName];
				if (keyName.endsWith(':o')) keyName = keyName.substring(0, keyName.length - 2);

				// --- 如果是一个不存在的道具，则直接认为无法开启
				if (!core.material.items[keyName]) {
					core.stopSound();
					core.playSound('操作失败');
					core.drawTip("无法开启此门");
					return clearAndReturn();
				}
				if (core.itemCount(keyName) < keyValue) {
					core.stopSound();
					core.playSound('操作失败');
					core.drawTip("你的" + ((core.material.items[keyName] || {}).name || "钥匙") + "不足！", null, true);
					return false;
				}
			}
			if (!core.status.event.id) core.autosaveFromSnapshot();
			for (var keyName in keyInfo) {
				if (!keyName.endsWith(':o')) core.removeItem(keyName, keyInfo[keyName]);
			}
		}
		core.playSound(doorInfo.openSound);
		return true;
	}
	//拾取道具前存档
	control.prototype.moveAction = function (callback) {
		if (core.status.heroMoving > 0) return;
		var noPass = core.noPass(core.nextX(), core.nextY()),
			canMove = core.canMoveHero();
		// 下一个点如果不能走
		if (noPass || !canMove) return this._moveAction_noPass(canMove, callback);
		if (core.getBlockCls(core.nextX(), core.nextY()) === 'items') {
			core.autosaveFromSnapshot();
			return this._moveAction_noPass(canMove, callback);
		}
		this._moveAction_moving(callback);
	}
},
    "怪物手册": function () {
	// 在此增加新插件
	ui.prototype.drawBook = function (index) {
		core.status.globalAttribute.font = 'DFPZongYiW9-P';

		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);
		core.clearUI();
		core.clearMap('data');
		// 生成groundPattern
		core.maps.generateGroundPattern(floorId);
		this._drawBook_drawBackground();
		core.setAlpha('ui', 1);

		if (enemys.length == 0) {
			return this._drawBook_drawEmpty();
		}

		index = core.clamp(index, 0, enemys.length - 1);
		core.status.event.data = index;
		var pageinfo = this._drawBook_pageinfo();
		var perpage = pageinfo.per_page,
			page = parseInt(index / perpage) + 1,
			totalPage = Math.ceil(enemys.length / perpage);

		var start = (page - 1) * perpage;
		enemys = enemys.slice(start, page * perpage);

		for (var i = 0; i < enemys.length; i++)
			this._drawBook_drawOne(floorId, i, enemys[i], pageinfo, index == start + i);

		core.drawBoxAnimate();
		this.drawPagination(page, totalPage);
		core.setTextAlign('ui', 'center');
		core.fillText('ui', '返回游戏', this.PIXEL - 46, this.PIXEL - 13, '#DDDDDD', this._buildFont(15, false));
	}

	ui.prototype._drawBookDetail_drawContent = function (enemy, content, pos) {
		// 名称
		core.setTextAlign('data', 'left');
		core.fillText('data', enemy.name, pos.content_left, pos.top + 30, core.status.globalAttribute.selectColor, this._buildFont(22, false));
		var content_top = pos.top + 44;

		this.drawTextContent('data', content.replaceAll('\\d', ''), {
			left: pos.content_left,
			top: content_top,
			maxWidth: pos.validWidth,
			fontSize: 16,
			lineHeight: 24
		});
	}
	/**
	 * 修改后的 _drawBookDetail_turnAndCriticals 函数
	 * 逻辑：遍历临界点，如果减伤 v[1] 为负数（即伤害反而增加），则使用红色显示。
	 */
	ui.prototype._drawBookDetail_turnAndCriticals = function (enemy, floorId, texts) {
		var damageInfo = core.getDamageInfo(enemy.id, null, enemy.x, enemy.y, floorId);
		//texts.push(`\r[#FF6A6A]\\d战斗回合数：\\d\r[]${((damageInfo || {}).turn || 0)}`);

		// 获取临界表数据 [攻击增量, 伤害减量]
		var criticalData = core.enemys.nextCriticals(enemy.id, 8, enemy.x, enemy.y, floorId);

		// 过滤掉无效的 [0, 0] 数据
		var criticals = criticalData
			.filter(function (v) {
				return v[0] !== 0 || v[1] !== 0;
			})
			.map(function (v) {
				var atkText = core.formatBigNumber(v[0]);
				var dmgDiff = v[1];
				var dmgText = core.formatBigNumber(dmgDiff);

				// 如果 dmgDiff < 0，代表伤害增加（破防点）
				if (dmgDiff < 0) {
					return ` \r[#FFD700]${atkText}\r[]: \r[#FF0000]${dmgText}\r[]`;
				} else {
					return `\r[#FFD700]${atkText}\r[]: ${dmgText}`;
				}
			});

		// 如果临界表为空
		var content = criticals.length > 0 ? criticals.join(", ") : "无";
		texts.push(`\r[#FF6A6A]\\d临界表：\\d\r[]【${content}】`);
	};
	ui.prototype._drawBookDetail_drawContent = function (enemy, content, pos) {
		// 名称
		core.setTextAlign('data', 'left');
		core.fillText('data', enemy.name, pos.content_left, pos.top + 30, core.status.globalAttribute.selectColor, this._buildFont(22, false));
		var content_top = pos.top + 44;

		this.drawTextContent('data', content.replaceAll('\\d', ''), {
			left: pos.content_left,
			top: content_top,
			maxWidth: pos.validWidth,
			fontSize: 16,
			lineHeight: 24
		});
	};

	/**
	 * 修改后的 _drawBookDetail_turnAndCriticals
	 * 约定：
	 *  - [atkDiff, dmgDiff]：普通临界点
	 *  - [atkDiff, dmgDiff, 1]：破防点（从 NaN 变成正常伤害）
	 */
	ui.prototype._drawBookDetail_turnAndCriticals = function (enemy, floorId, texts) {
		var damageInfo = core.getDamageInfo(enemy.id, null, enemy.x, enemy.y, floorId);
		//texts.push(`\r[#FF6A6A]\\d战斗回合数：\\d\r[]${((damageInfo || {}).turn || 0)}`);

		// 获取临界表数据 [攻击增量, 伤害减量, 破防标记]
		var criticalData = core.enemys.nextCriticals(enemy.id, 8, enemy.x, enemy.y, floorId);

		// 过滤掉无效的 [0, 0] 数据，但保留破防点
		var criticals = criticalData
			.filter(function (v) {
				return v[2] === 1 || v[0] !== 0 || v[1] !== 0;
			})
			.map(function (v) {
				var atkText = core.formatBigNumber(v[0]);

				// 破防点：特殊显示
				if (v[2] === 1) {
					var dmgText = core.formatBigNumber(v[1]);
					return `\r[#FFD700]${atkText}\r[]: \r[#00E676]${dmgText}\r[]`;
				}

				var dmgDiff = v[1];
				var dmgText = core.formatBigNumber(dmgDiff);

				// 伤害反而增加：红色
				if (dmgDiff < 0) {
					return `\r[#FFD700]${atkText}\r[]: \r[#FF0000]${dmgText}\r[]`;
				} else {
					return `\r[#FFD700]${atkText}\r[]: ${dmgText}`;
				}
			});

		// 如果临界表为空
		var content = criticals.length > 0 ? criticals.join(", ") : "无";
		texts.push(`\r[#FF6A6A]\\d临界表：\\d\r[]【${content}】`);
	};

	/**
	 * 修改后的 nextCriticals
	 * 逻辑：
	 *  1. 先走特殊怪逻辑
	 *  2. 若当前攻击未破防，则先找“破防点”
	 *  3. 破防点会用 [atkDiff, dmgDiff, 1] 返回
	 *  4. 之后再走二分或循环找后续临界点
	 */
	enemys.prototype.nextCriticals = function (enemy, number, x, y, floorId) {
		number = number || 1;

		var specialCriticals = this._nextCriticals_special(enemy, number, x, y, floorId);
		if (specialCriticals != null) return specialCriticals;

		var info = this.getDamageInfo(enemy, null, x, y, floorId);

		// 当前攻击未破防：先找第一个能正常出伤害的点
		if (info == null || Number.isNaN(info.damage)) {
			var overAtk = this._nextCriticals_overAtk(enemy, x, y, floorId);
			if (overAtk == null) return [];

			// 如果已经拿到了“破防点”的信息对象
			if (typeof overAtk[1] == 'number') {
				// 这里直接把这个点标记为破防点
				return [
					[overAtk[0], overAtk[1], 1]
				];
			}

			info = overAtk[1];
			info.__over__ = true;
			info.__overAtk__ = overAtk[0];
		}

		if (typeof info == 'number') return [
			[0, 0]
		];

		// 需要使用 Loop 方式计算的特殊属性
		var loopSpecials = [8, 10, 35];
		var special = core.getEnemyValue(enemy, 'special', x, y, floorId);
		var useLoop = loopSpecials.some(function (s) {
			return core.hasSpecial(special, s);
		});

		// 贤者之证需要使用循环
		useLoop = useLoop || core.getFlag("proof_sage", 0) > 0;

		if (useLoop) {
			return this._nextCriticals_useLoop(enemy, info, number, x, y, floorId);
		} else {
			return this._nextCriticals_useBinarySearch(enemy, info, number, x, y, floorId);
		}
	};

	enemys.prototype._nextCriticals_special = function (enemy, number, x, y, floorId) {
		// 允许坚固、模仿怪物通过二分法计算首个破防点（返回null将触发默认的搜索逻辑）
		return null;
	};

	/**
	 * 寻找破防点：
	 * 返回 [atk增量, damageInfo]
	 */
	enemys.prototype._nextCriticals_overAtk = function (enemy, x, y, floorId) {
		var hero_atk = core.getRealStatusOrDefault(null, 'atk');

		var calNext = function (currAtk, maxAtk) {
			var start = currAtk,
				end = 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 && !Number.isNaN(nextInfo.damage)) end = mid;
				else start = mid + 1;
			}

			var nextInfo = core.enemys.getDamageInfo(enemy, { "atk": start }, x, y, floorId);
			return (nextInfo == null || Number.isNaN(nextInfo.damage)) ? null : [start - hero_atk, nextInfo];
		};

		// 计算搜索上限：怪物防御 + 怪物血量 + 安全余量
		var calculateMaxAtk = function () {
			let info = core.getDamageInfo(enemy, null, x, y, floorId);
			var mon_hp = info.mon_hp,
				mon_def = info.mon_def,
				monster_agi = info.mon_agi || 0;

			var agiFactor = 1 - (monster_agi + 1) / 100;
			var limitAtk = mon_def + mon_hp + 1;
			if (agiFactor > 0) {
				limitAtk = Math.max(limitAtk, mon_def + (mon_hp / agiFactor) + 1);
			}
			return Math.ceil(limitAtk);
		};

		return calNext(hero_atk + 1, calculateMaxAtk());
	};

	/**
	 * 二分查找版临界点
	 * 会把“破防点”放进列表最前面，并打上第三位标记 1
	 */
	enemys.prototype._nextCriticals_useBinarySearch = function (enemy, info, number, x, y, floorId) {
		var mon_hp = info.mon_hp,
			hero_atk = core.getRealStatusOrDefault(null, 'atk'),
			mon_def = info.mon_def,
			monster_agi = info.mon_agi || 0,
			initialDamage = info.damage;

		var list = [];

		// 如果是从“不破防点”跳过来的，先把破防点塞进来
		var currAtk = hero_atk;
		if (info.__over__) {
			currAtk = hero_atk + info.__overAtk__;
			list.push([info.__overAtk__, info.damage, 1]);
		}

		/**
		 * 获取指定攻击力下的伤害值
		 */
		var getDamageByAtk = function (atk) {
			var res = core.enemys.getDamageInfo(enemy, { "atk": atk }, x, y, floorId);
			if (typeof res === 'number') return res;
			return res.damage;
		};

		// 计算搜索上限
		var calculateMaxAtk = function () {
			var agiFactor = 1 - (monster_agi + 1) / 100;
			var limitAtk = mon_def + mon_hp + 1;
			if (agiFactor > 0) {
				limitAtk = Math.max(limitAtk, mon_def + (mon_hp / agiFactor) + 1);
			}
			return Math.ceil(limitAtk);
		};

		var maxSearchAtk = calculateMaxAtk();
		var lastDmg = initialDamage;

		/**
		 * 在 [startAtk + 1, limitAtk] 中找第一个伤害发生变化的位置
		 */
		var findNextChange = function (startAtk, limitAtk, baseDmg) {
			var left = Math.floor(startAtk);
			var right = Math.floor(limitAtk);
			var resultAtk = null;
			var resultDmg = null;

			if (left > right) return null;

			while (left <= right) {
				var mid = Math.floor((left + right) / 2);
				var dmgMid = getDamageByAtk(mid);

				if (dmgMid !== baseDmg) {
					resultAtk = mid;
					resultDmg = dmgMid;
					right = mid - 1;
				} else {
					left = mid + 1;
				}
			}

			return resultAtk !== null ? { atk: resultAtk, damage: resultDmg } : null;
		};

		// 查找后续临界点
		while (list.length < number && currAtk < maxSearchAtk) {
			var next = findNextChange(currAtk + 1, maxSearchAtk, lastDmg);
			if (!next) break;

			var atkDiff = next.atk - hero_atk;
			var damageDiff = initialDamage - next.damage;

			list.push([
				atkDiff,
				damageDiff
			]);

			currAtk = next.atk;
			lastDmg = next.damage;
		}

		if (list.length === 0) {
			return [
				[0, 0]
			];
		}

		return list;
	};

	/**
	 * 循环版临界点
	 * 同样支持破防点特殊标记
	 */
	/*	enemys.prototype._nextCriticals_useLoop = function (enemy, info, number, x, y, floorId) {
			// 计算搜索上限
			var maxAtk = (function () {
				let info = core.getDamageInfo(enemy, null, x, y, floorId);
				var mon_hp = info.mon_hp,
					mon_def = info.mon_def,
					monster_agi = info.mon_agi || 0;

				var agiFactor = 1 - (monster_agi + 1) / 100;
				var limitAtk = mon_def + mon_hp + 1;
				if (agiFactor > 0) {
					limitAtk = Math.max(limitAtk, mon_def + (mon_hp / agiFactor) + 1);
				}
				return Math.ceil(limitAtk);
			})();

			var hero_atk = core.status.hero.atk;
			var pre = info.damage;
			var list = [];
			var start_atk = hero_atk;

			// 先塞入破防点
			if (info.__over__) {
				start_atk += info.__overAtk__;
				list.push([info.__overAtk__, info.damage, 1]);
			}

			for (var atk = start_atk + 1; atk <= maxAtk; atk++) {
				var nextInfo = this.getDamageInfo(enemy, { "atk": atk }, x, y, floorId);
				if (nextInfo == null || typeof nextInfo == 'number') break;

				if (pre !== nextInfo.damage) {
					pre = nextInfo.damage;
					list.push([atk - hero_atk, info.damage - nextInfo.damage]);

					if (list.length >= number) break;
				}
			}

			if (list.length === 0) list.push([0, 0]);
			return list;
		};*/

	enemys.prototype._nextCriticals_useLoop = function (enemy, info, number, x, y, floorId) {
		// 获取搜索上限
		const maxAtk = this._getCriticalSearchMaxAtk(enemy, x, y, floorId);

		let heroAtk = core.status.hero.atk;
		const baseAtk = heroAtk; // 初始攻击
		let currentDamage = info.damage;
		const baseDamage = currentDamage; // 初始伤害
		const results = [];

		// 处理破防点
		if (info.__over__) {
			results.push([info.__overAtk__, currentDamage, 1]);

			heroAtk += info.__overAtk__;
			const newInfo = this.getDamageInfo(enemy, { atk: heroAtk }, x, y, floorId);

			if (newInfo && typeof newInfo !== 'number') {
				currentDamage = newInfo.damage;
			} else {
				return results;
			}
		}

		// damage缓存
		const damageCache = new Map();

		const getDamage = (atk) => {
			if (damageCache.has(atk)) return damageCache.get(atk);

			const dmgInfo = this.getDamageInfo(enemy, { atk: atk }, x, y, floorId);
			const damage = (dmgInfo && typeof dmgInfo !== 'number') ?
				dmgInfo.damage :
				null;

			damageCache.set(atk, damage);
			return damage;
		};

		// 二分寻找下一个临界点
		const findNextCritical = (startAtk, targetDamage, maxAtk) => {
			let low = startAtk + 1;
			let high = maxAtk;
			let firstDiffAtk = null;

			const highDamage = getDamage(high);
			if (highDamage === null || highDamage === targetDamage) return null;

			while (low <= high) {
				const mid = Math.floor((low + high) / 2);
				const midDamage = getDamage(mid);

				if (midDamage === null) return null;

				if (midDamage === targetDamage) {
					low = mid + 1;
				} else {
					firstDiffAtk = mid;
					high = mid - 1;
				}
			}

			return firstDiffAtk;
		};

		// 查找多个临界点
		for (let i = results.length; i < number; i++) {
			const nextAtk = findNextCritical(heroAtk, currentDamage, maxAtk);
			if (nextAtk === null) break;

			const nextDamage = getDamage(nextAtk);
			if (nextDamage === null) break;

			// 改成累计攻击
			const deltaAtk = nextAtk - baseAtk;

			// 改成累计减伤
			const damageDelta = baseDamage - nextDamage;

			results.push([deltaAtk, damageDelta]);

			heroAtk = nextAtk;
			currentDamage = nextDamage;
		}

		return results.length ? results : [
			[0, 0]
		];
	};

	// 提取上限计算辅助函数
	enemys.prototype._getCriticalSearchMaxAtk = function (enemy, x, y, floorId) {
		const info = core.getDamageInfo(enemy, null, x, y, floorId);
		const mon_hp = info.mon_hp,
			mon_def = info.mon_def,
			monster_agi = info.mon_agi || 0;
		const agiFactor = 1 - (monster_agi + 1) / 100;
		let limitAtk = mon_def + mon_hp + 1;
		if (agiFactor > 0) {
			limitAtk = Math.max(limitAtk, mon_def + (mon_hp / agiFactor) + 1);
		}
		return Math.ceil(limitAtk);
	};
	ui.prototype._drawBookDetail_origin = function (enemy, texts) {
		// 怪物数值和原始值不一样时，在详细信息页显示原始数值
		var originEnemy = core.enemys._getCurrentEnemys_getEnemy(enemy.id);
		var content = [];
		let agi = (core.getEnemyValue(enemy, 'agi') + 1);
		let cri = (core.getEnemyValue(enemy, 'cri') + 1);
		texts.push('闪避率' + agi + '%')
		texts.push('暴击率' + cri + '%')
		if (enemy.locs != null && enemy.locs.length >= 0) {
			texts.push("\r[#FF6A6A]\\d怪物坐标：\\d\r[]" + JSON.stringify(enemy.locs));
		}
		["hp", "atk", "def", "point", "money", "exp"].forEach(function (one) {
			if (enemy[one] == null || originEnemy[one] == null) return;
			if (enemy[one] != originEnemy[one]) {
				content.push(core.getStatusLabel(one) + " " + originEnemy[one]);
			}
		});
		if (content.length > 0) {
			texts.push("\r[#FF6A6A]\\d原始数值：\\d\r[]" + content.join("；"));
		}
	}
	// 辅助函数：根据最大宽度获取合适的字体
	ui.prototype._getAdaptiveFont = function (text, maxWidth, baseSize) {
		var size = baseSize;
		var font = this._buildFont(size, false);
		// 循环缩小字号直到文字宽度小于最大宽度，最小不小于 9px
		while (core.calWidth('ui', text, font) > maxWidth && size > 9) {
			size--;
			font = this._buildFont(size, false);
		}
		return font;
	};

	// 辅助函数：绘制带自动缩放的数值
	ui.prototype._drawValueWithAdaptiveFont = function (text, x, y, maxWidth, baseSize) {
		var font = this._getAdaptiveFont(text, maxWidth, baseSize);
		core.fillText('ui', text, x, y, null, font);
	};

	ui.prototype._drawBook_drawContent = function (index, enemy, top, left) {
		var width = this.PIXEL - left; // 9 : 8 : 8 划分三列
		this._drawBook_drawRow1(index, enemy, top, left, width, top + 20);
		this._drawBook_drawRow2(index, enemy, top, left, width, top + 38);
		this._drawBook_drawRow3(index, enemy, top, left, width, top + 56);
	};

	ui.prototype._drawBook_drawRow1 = function (index, enemy, top, left, width, position) {
		core.setTextAlign('ui', 'left');
		var f13 = this._buildFont(13, false);
		var col1 = left,
			col2 = left + width * 9 / 25,
			col3 = left + width * 17 / 25;
		var maxNumWidth = 43; // 定义数值最大宽度限制

		// 第一列：HP
		core.fillText('ui', core.getStatusLabel('hp'), col1, position, '#DDDDDD', f13);
		this._drawValueWithAdaptiveFont(core.formatBigNumber(enemy.hp || 0), col1 + 30, position, maxNumWidth, 13);

		// 第二列：ATK
		core.fillText('ui', core.getStatusLabel('atk'), col2, position, '#DDDDDD', f13);
		this._drawValueWithAdaptiveFont(core.formatBigNumber(enemy.atk || 0), col2 + 30, position, maxNumWidth, 13);

		// 第三列：DEF
		core.fillText('ui', core.getStatusLabel('def'), col3, position, '#DDDDDD', f13);
		this._drawValueWithAdaptiveFont(core.formatBigNumber(enemy.def || 0), col3 + 30, position, maxNumWidth, 13);
	};

	ui.prototype._drawBook_drawRow2 = function (index, enemy, top, left, width, position) {
		core.setTextAlign('ui', 'left');
		var f13 = this._buildFont(13, false);
		var col1 = left,
			col2 = left + width * 9 / 25,
			col3 = left + width * 17 / 25;
		var maxNumWidth = 43;

		var second_line = [];
		second_line.push([core.getStatusLabel('money'), core.formatBigNumber(enemy.money || 0)]);
		//if (core.flags.enableAddPoint) second_line.push([core.getStatusLabel('point'), core.formatBigNumber(enemy.point || 0)]);
		second_line.push([core.getStatusLabel('exp'), core.formatBigNumber(enemy.exp || 0)]);

		var damage_offset = col1 + (this.PIXEL - col1) / 2 - 12;

		if (second_line.length > 0) {
			var one = second_line.shift();
			core.fillText('ui', one[0], col1, position, '#DDDDDD', f13);
			this._drawValueWithAdaptiveFont(one[1], col1 + 30, position, maxNumWidth, 13);
			damage_offset = col2 + (this.PIXEL - col2) / 2 - 12;
		}

		if (second_line.length > 0) {
			var one = second_line.shift();
			core.fillText('ui', one[0], col2, position, '#DDDDDD', f13);
			this._drawValueWithAdaptiveFont(one[1], col2 + 30, position, maxNumWidth, 13);
			damage_offset = col3 + (this.PIXEL - col3) / 2 - 12;
		}

		// 伤害绘制
		this._drawBook_drawDamage(index, enemy, damage_offset, position);
	};

	ui.prototype._drawBook_drawRow3 = function (index, enemy, top, left, width, position) {
		core.setTextAlign('ui', 'left');
		var f13 = this._buildFont(13, false);
		var col1 = left,
			col2 = left + width * 9 / 25,
			col3 = left + width * 17 / 25;
		var maxNumWidth = 43;

		// 临界
		core.fillText('ui', '临界', col1, position, '#DDDDDD', f13);
		this._drawValueWithAdaptiveFont(core.formatBigNumber(enemy.critical || 0), col1 + 30, position, maxNumWidth, 13);

		// 减伤
		core.fillText('ui', '减伤', col2, position, '#DDDDDD', f13);
		this._drawValueWithAdaptiveFont(core.formatBigNumber(enemy.criticalDamage || 0), col2 + 30, position, maxNumWidth, 13);

		// 加防
		core.fillText('ui', '加防', col3, position, '#DDDDDD', f13);
		this._drawValueWithAdaptiveFont(core.formatBigNumber(enemy.defDamage || 0), col3 + 30, position, maxNumWidth, 13);
	};
},
    "函数修复": function () {

	// 修复，现在选择不存在时报错
	events.prototype.__action_choices_replaying = function (data, index) {
		var selection = index;
		if (index != 'none') {
			selection = parseInt(index);
			if (isNaN(selection)) return false;
			if (selection < 0) selection += data.choices.length;
			if (selection < 0) return false;
			if (selection % 100 > 50) selection += data.choices.length;
			if (selection % 100 > data.choices.length) return false;
			var timeout = Math.floor(selection / 100) || 0;
			core.setFlag('timeout', timeout);
			selection %= 100;
		} else core.setFlag('timeout', 0);
		core.status.event.selection = selection;
		setTimeout(function () {
			core.status.route.push("choices:" + index);
			if (selection != 'none') {
				// 检查
				var choice = data.choices[selection];
				if (!choice || (choice.need != null && choice.need != '' && !core.calValue(choice.need))) {
					// 无法选择此项
					core.control._replay_error("无法选择项：" + index);
					return;
				} else {
					core.insertAction(choice.action);
				}
			}
			core.doAction();
		}, core.status.replay.speed == 24 ? 1 : 750 / Math.max(1, core.status.replay.speed));
		return true;
	}
	control.prototype.getRealStatusOrDefault = function (status, name) {
		if (status && name in status)
			return Math.floor(status[name]);
		return Math.floor(this.getStatus(name) * this.getBuff(name));
	}
	// 函数修复，之前当运动回原地时会直接删除怪物数据
	events.prototype.moveEnemyOnPoint = function (fromX, fromY, toX, toY, floorId, norefresh) {
		if (fromX === toX && fromY === toY) return;
		floorId = floorId || core.status.floorId;
		if (((flags.enemyOnPoint || {})[floorId] || {})[fromX + "," + fromY]) {
			flags.enemyOnPoint[floorId][toX + "," + toY] = flags.enemyOnPoint[floorId][fromX + "," + fromY];
			delete flags.enemyOnPoint[floorId][fromX + "," + fromY];
			if (!norefresh) core.updateStatusBar();
		}
	}
	// 函数修复，转变时应该删除全局动画
	maps.prototype.setBgFgBlock = function (name, number, x, y, floorId) {
		floorId = floorId || core.status.floorId;
		if (!floorId || number == null || x == null || y == null) return;
		if (x < 0 || x >= core.floors[floorId].width || y < 0 || y >= core.floors[floorId].height) return;
		if (!name || (!name.startsWith('bg') && !name.startsWith('fg'))) return;

		if (typeof number == 'string') {
			if (/^\d+$/.test(number)) number = parseInt(number);
			else number = core.getNumberById(number);
		}

		var values = core.getFlag('__' + name + 'v__', {});
		values[floorId] = (values[floorId] || []).filter(function (one) { return one[0] != x || one[1] != y });
		values[floorId].push([x, y, number]);
		core.setFlag('__' + name + 'v__', values);

		core.status[name + "maps"][floorId] = null;

		if (floorId == core.status.floorId) {
			core.clearMap(name);
			core.removeGlobalAnimate(x, y, name);
			if (name.startsWith('bg')) core.drawBg(floorId);
			else core.drawFg(floorId);
		}
	}
	// 函数修复，隐藏时应该删除全局动画
	maps.prototype._triggerBgFgMap = function (type, name, loc, floorId, callback) {
		if (type != 'show') type = 'hide';
		if (!name || (!name.startsWith('bg') && !name.startsWith('fg'))) return;
		if (typeof loc[0] == 'number' && typeof loc[1] == 'number')
			loc = [loc];
		floorId = floorId || core.status.floorId;
		if (!floorId) return;

		if (loc.length == 0) return;
		var disabled = core.getFlag('__' + name + 'd__', {});
		disabled[floorId] = disabled[floorId] || [];
		loc.forEach(function (t) {
			if (type == 'hide') {
				disabled[floorId].push([t[0], t[1]]);
				core.removeGlobalAnimate(t[0], t[1], name);
			} else {
				disabled[floorId] = disabled[floorId].filter(function (one) { return one[0] != t[0] || one[1] != t[1] });
			}
		})
		core.setFlag('__' + name + 'd__', disabled);

		core.status[name + "maps"][floorId] = null;

		if (floorId == core.status.floorId) {
			core.redrawMap();
		}
		if (callback) callback();
	}
	// 函数修复，重绘时应该删除全局动画
	maps.prototype.redrawMap = function () {
		core.bigmap.canvas.forEach(function (one) {
			core.clearMap(one);
		});
		core.removeGlobalAnimate();
		this._drawMap_drawAll(null, { redraw: true });
		core.drawDamage();
	}
	//录像中存档
	control.prototype._replay_SL = function () {
		if (!core.isPlaying() || !core.isReplaying()) return;
		if (!core.status.replay.pausing) {
			core.playSound('操作失败');
			return core.drawTip("请先暂停录像");
		}
		if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
			core.playSound('操作失败');
			return core.drawTip("请等待当前事件的处理结束");
		}
		if (core.hasFlag('__forbidSave__')) {
			core.playSound('操作失败');
			return core.drawTip('当前禁止存档');
		}
		if (core.dymCanvas.replay)
			core.dymCanvas.replay.canvas.style.display = 'none';

		core.lockControl();
		core.status.event.id = 'save';
		var saveIndex = core.saves.saveIndex;
		var page = parseInt((saveIndex - 1) / 5),
			offset = saveIndex - 5 * page;

		core.ui._drawSLPanel(10 * page + offset);
	}
	//函数修复，增加选项不存在时的报错
	events.prototype.__action_choices_replaying = function (data, index) {
		var selection = index;
		if (index != 'none') {
			selection = parseInt(index);
			if (isNaN(selection)) return false;
			if (selection < 0) selection += data.choices.length;
			if (selection < 0) return false;
			if (selection % 100 > 50) selection += data.choices.length;
			if (selection % 100 > data.choices.length) return false;
			var timeout = Math.floor(selection / 100) || 0;
			core.setFlag('timeout', timeout);
			selection %= 100;
		} else core.setFlag('timeout', 0);
		core.status.event.selection = selection;
		setTimeout(function () {
			core.status.route.push("choices:" + index);
			if (selection != 'none') {
				// 检查
				var choice = data.choices[selection];
				if (!choice || (choice.need != null && choice.need != '' && !core.calValue(choice.need))) {
					// 无法选择此项
					core.control._replay_error("无法选择项：" + index);
					return;
				} else {
					core.insertAction(choice.action);
				}
			}
			core.doAction();
		}, core.status.replay.speed == 24 ? 1 : 750 / Math.max(1, core.status.replay.speed));
		return true;
	}
	//函数修复，敌人不存在时返回空
	enemys.prototype.getEnemyInfo = function (enemy, hero, x, y, floorId) {
		if (enemy == null) return null;
		if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
		if (!enemy) return null;
		return this.enemydata.getEnemyInfo(enemy, hero, x, y, floorId)
	}
	////// 绘制帮助页面 //////
	ui.prototype._drawHelp = function () {
		core.playSound('打开界面');
		core.clearUI();
		if (core.material.images.keyboard) {
			core.status.event.id = 'help';
			core.lockControl();
			core.setAlpha('ui', 1);
			core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000');
			core.drawImage('ui', core.material.images.keyboard, 0, 0, core.__PIXELS__, core.__PIXELS__);
		} else {
			core.drawText([
				"\t[键盘快捷键列表]" +
				"[CTRL] 跳过对话   [Z] 转向\n" +
				"[X] " + core.material.items['book'].name + "   [G] " + core.material.items['fly'].name + "\n" +
				"[A] 读取自动存档   [W] 撤销读取自动存档\n" +
				"[S/D] 存读档页面   [SPACE] 轻按\n" +
				"[V] 快捷商店   [ESC] 系统菜单\n" +
				"[T] 道具页面   [Q] 装备页面\n" +
				"[B] 数据统计   [H] 帮助页面\n" +
				"[R] 回放录像   [E] 显示光标\n" +
				"[N] 返回标题页面   [P] 游戏主页\n" +
				"[O] 查看工程   [F7] 打开debug穿墙模式\n" +
				"[PgUp/PgDn] 浏览地图\n" +
				"[1~4] 快捷使用破炸飞和其他道具\n" +
				"[Alt+0~9] 快捷换装",
				"\t[鼠标操作]" +
				"点状态栏中图标： 进行对应的操作\n" +
				"点任意块： 寻路并移动\n" +
				"点任意块并拖动： 指定寻路路线\n" +
				"双击空地： 瞬间移动\n" +
				"单击勇士： 转向\n" +
				"双击勇士： 轻按（仅在轻按开关打开时有效）\n" +
				"长按任意位置：跳过剧情对话或打开虚拟键盘"
			]);
		}
	}
	//缩略图不应该更新地图缓存
	maps.prototype._drawThumbnail_realDrawTempCanvas = function (floorId, blocks, options) {
		// 缩略图：背景
		this.drawBg(floorId, options);
		// 缩略图：事件
		this.drawEvents(floorId, blocks, options);
		// 缩略图：勇士
		if (options.heroLoc) {
			options.heroIcon = options.heroIcon || core.status.hero.image || 'hero.png';
			options.heroIcon = core.getMappedName(options.heroIcon);
			var icon = core.material.icons.hero[options.heroLoc.direction];
			var height = core.material.images.images[options.heroIcon].height / 4;
			var width = (core.material.images.images[options.heroIcon].width || 128) / 4;
			core.drawImage(options.ctx, core.material.images.images[options.heroIcon], icon.stop * width, icon.loc * height, width, height,
				32 * options.heroLoc.x + 32 - width, 32 * options.heroLoc.y + 32 - height, width, height);
		}
		// 缩略图：前景
		this.drawFg(floorId, options);
		// 缩略图：显伤
		if (options.damage && core.hasItem('book')) {
			core.updateCheckBlock(floorId);
			core.control.updateDamage(floorId, options.ctx);
		}

		// =========== 新增代码：清理缓存 ===========
		// 强制清除当前楼层的 bg/fg 缓存，防止缩略图生成的脏数据污染实际游戏
		if (core.status.bgmaps) core.status.bgmaps[floorId] = null;
		if (core.status.fgmaps) core.status.fgmaps[floorId] = null;
		// =======================================
	}
	////// 设置一个怪物属性 //////
	events.prototype.setEnemy = function (id, name, value, operator, prefix, norefresh) {
		if (!core.hasFlag('enemyInfo')) {
			core.setFlag('enemyInfo', {});
		}
		var enemyInfo = core.getFlag('enemyInfo');
		if (!enemyInfo[id]) enemyInfo[id] = {};
		if (typeof value === 'string' && name == 'name') value = value.replace(/\r/g, '\\r');
		value = this._updateValueByOperator(core.calValue(value, prefix), (core.material.enemys[id] || {})[name], operator);
		enemyInfo[id][name] = value;
		core.setFlag('enemyInfo', enemyInfo);
		//(core.material.enemys[id] || {})[name] = core.clone(value);
		if (!norefresh) core.updateStatusBar();
	}


	enemys.prototype.getEnemyValue = function (enemy, name, x, y, floorId) {
		floorId = floorId || core.status.floorId;

		// 1. 获取特定坐标点的覆盖信息
		const pointInfo = (((flags.enemyOnPoint || {})[floorId] || {})[x + "," + y] || {});

		// 如果指定了具体属性名且该点有覆盖，直接返回
		if (core.isset(name) && pointInfo[name] != null) {
			return pointInfo[name];
		}

		// 获取怪物的原始数据对象
		var enemyId = null;
		var enemyData = null;

		if (enemy == null) {
			var block = core.getBlock(x, y, floorId);
			if (block == null || block.event.cls.indexOf('enemy') !== 0) return null;
			enemyId = block.event.id;
			enemyData = core.material.enemys[enemyId];
		} else if (typeof enemy == 'string') {
			enemyId = enemy;
			enemyData = core.material.enemys[enemyId];
		} else {
			enemyId = enemy.id;
			enemyData = enemy;
		}

		if (enemyData == null) return null;

		// 2. 获取全局怪物属性覆盖信息 (来自 setEnemy)
		const enemyInfo = core.getFlag('enemyInfo', {});
		const globalEnemyOverlay = enemyInfo[enemyId] || {};

		// 深度克隆原始数据以防污染
		var resultEnemy = core.clone(enemyData);

		// 3. 优先级合并：原始数据 < 全局覆盖 (setEnemy) < 坐标覆盖 (enemyOnPoint)

		// 合并全局覆盖
		for (let key in globalEnemyOverlay) {
			if (globalEnemyOverlay.hasOwnProperty(key)) {
				resultEnemy[key] = globalEnemyOverlay[key];
			}
		}

		// 合并坐标点覆盖
		for (let key in pointInfo) {
			if (pointInfo.hasOwnProperty(key)) {
				resultEnemy[key] = pointInfo[key];
			}
		}

		// 根据参数返回
		if (!core.isset(name)) {
			return resultEnemy;
		} else {
			return resultEnemy[name];
		}
	}



},
    "高级动画": 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,
	};
},
    "自动操作": function () {

	// 合并版自动清怪插件 (修改版：支持过滤血瓶)

	const ctxName = 'autoClear';

	// 定义血瓶ID列表
	const potionIds = ['superPotion', 'poisonWine', 'weakWine'];

	// ----------------- 辅助函数 / 工具 -----------------
	function has(v) {
		return v !== null && v !== undefined;
	}

	function willLvUp(exp) {
		const nextExp = core.getNextLvUpNeed();
		if (typeof exp === 'number' && typeof nextExp === 'number' && exp >= nextExp) return true;
		return false;
	}

	// ----------------- canBattle -----------------
	function canBattle(enemy, x, y) {
		const loc = `${x},${y}`;
		const floor = core.floors[core.status.floorId];
		const e = core.getEnemyValue(enemy, null, x, y);
		const hasEvent =
			has(floor.afterBattle[loc]) || has(floor.beforeBattle[loc]) ||
			has(e.beforeBattle) || has(e.afterBattle) ||
			has(floor.events[loc]) || willLvUp(e.exp); // 防止有升级后事件
		const cache = core.status.checkBlock.cache;
		const hasGuards = has(cache) && has(cache[loc]) && cache[loc]?.guards?.length > 0; // 会被支援

		// 有事件，不清
		if (hasEvent) return false;
		// 有特定特殊属性的怪不清
		if (
			core.hasSpecial(e.special, 12) || // 中毒
			core.hasSpecial(e.special, 13) || // 衰弱
			core.hasSpecial(e.special, 14) || // 诅咒
			core.hasSpecial(e.special, 19) || // 自爆 
			core.hasSpecial(e.special, 21) || // 退化
			core.hasSpecial(e.special, 26) || // 支援
			hasGuards || // 会被支援
			core.hasSpecial(e.special, 27) || // 捕捉
			core.hasSpecial(e.special, 34) // 再生
		) {
			return false;
		}
		const damage = core.getDamageInfo(enemy, void 0, x, y)?.damage;
		// 0伤或负伤，清
		if (has(damage) && damage <= 0) return true;
		return false;
	}

	// ----------------- canGetItem -----------------
	function canGetItem(item, loc, floorId) {
		return !hasBlockDamage(loc) || core.flags.enableGentleClick;
	}

	// ----------------- 拾取动画 -----------------
	class AttractAnimate {
		constructor() {
			this.name = 'attractAnimate';
			this.isPlaying = false;
			this.nodes = [];
			this.lastTime = -1;
			this.thr = 5; // 缓动比例倒数，越大移动越慢
		}

		add(id, x, y, callback) {
			if (core.isReplaying()) return;
			this.nodes.push({ id, x, y, callback });
		}

		start() {
			if (this.isPlaying) return;
			if (core.isReplaying()) return;
			if (core.getLocalStorage && core.getLocalStorage('skipPerform')) return;
			this.isPlaying = true;
			core.registerAnimationFrame(this.name, true, this.update.bind(this));
			this.ctx = core.createCanvas(this.name, 0, 0, core.__PIXELS__, core.__PIXELS__, 120);
		}

		remove() {
			core.unregisterAnimationFrame(this.name);
			core.deleteCanvas(this.name);
			this.isPlaying = false;
		}

		clear() {
			this.nodes = [];
			this.remove();
		}

		update(timeStamp) {
			if (this.lastTime < 0) this.lastTime = timeStamp;
			if (timeStamp - this.lastTime < 20) return;
			this.lastTime = timeStamp;

			core.clearMap(this.name);

			const heroCenterX = core.status.heroCenter.px - 16;
			const heroCenterY = core.status.heroCenter.py - 16;

			for (const n of this.nodes) {
				const dx = heroCenterX - n.x;
				const dy = heroCenterY - n.y;

				if (Math.abs(dx) <= this.thr && Math.abs(dy) <= this.thr) {
					n.dead = true;
				} else {
					n.x += ~~(dx / this.thr);
					n.y += ~~(dy / this.thr);
				}

				core.drawIcon(this.name, n.id, n.x, n.y, 32, 32);
			}

			const remaining = [];
			for (const n of this.nodes) {
				if (n.dead && n.callback) n.callback();
				if (!n.dead) remaining.push(n);
			}
			this.nodes = remaining;

			if (this.nodes.length === 0) this.remove();
		}
	}

	const animateHwnd = new AttractAnimate();

	this.pickOneItemAnimate = function (id, x, y, callback) {
		if (core.isReplaying()) return;
		animateHwnd.add(id, x, y, callback);
		animateHwnd.start();
	};
	this.clearAttractAnimate = function () {
		animateHwnd.clear();
	};

	// ----------------- 判断地图伤害 -----------------
	function hasBlockDamage(loc) {
		const checkblockInfo = core.status.checkBlock;
		const damage = checkblockInfo?.damage?.[loc];
		const ambush = checkblockInfo?.ambush?.[loc];
		const repulse = checkblockInfo?.repulse?.[loc];
		const chase = checkblockInfo?.chase?.[loc];

		return (has(damage) && damage > 0) || has(ambush) || has(repulse) || has(chase);
	}

	// ----------------- judge -----------------
	function judge(block, nx, ny, tx, ty, dir, floorId, autoFlags) {
		if (core.getFgNumber(tx, ty) === 103) return { type: "unknown", canGoThrough: false };
		if (!has(block) || block.disable) {
			return { type: "none", canGoThrough: true };
		}
		const cls = block.event.cls;
		const loc = `${tx},${ty}`;
		const floor = core.floors[floorId];
		const changeFloor = floor.changeFloor[loc];

		if (has(changeFloor)) {
			if (core.canIgnoreChangeFloor(block)) {
				return { type: "unknown", canGoThrough: true };
			}
			return { type: "unknown", canGoThrough: false };
		}

		if (has(block.event.data)) return { type: "unknown", canGoThrough: false };

		const isEnemy = autoFlags.battle && cls && cls.startsWith && cls.startsWith('enemy'),
			isItem = autoFlags.item && cls === 'items';

		const isWall = autoFlags.wall && (block.id === 2);
		const isLava = autoFlags.wall && (block.id === 5 && core.hasItem && core.hasItem('snow') && core.material.items["snow"] && core.material.items["snow"].cls === 'constants');

		if (isEnemy) return { type: "enemy", canGoThrough: true };
		if (isItem) return { type: "item", canGoThrough: true };
		if (isWall) return { type: "wall", canGoThrough: true };
		if (isLava) return { type: "lava", canGoThrough: true };

		return { type: "unknown", canGoThrough: false };
	}

	const { Transition, hyper, Ticker } = core.plugin.animate ?? {};
	const transitionTime = 200;
	const transitionList = [];

	// ------ 修改：增加 autoFlags 参数以判断是否过滤血瓶 ------
	function _performGetItem(item, x, y, loc, floorId, autoFlags) {
		if (!canGetItem(item, loc, floorId)) {
			return false;
		}

		// 判断：如果模式是 nopotion 且物品在血瓶列表中，则不拾取
		if ( /*autoFlags.item === 'nopotion' &&*/ potionIds.includes(item.id)) {
			return false;
		}

		core.getItem(item.id, 1, x, y);

		if (!main.replayChecking && Ticker) {
			let px = x * 32 - core.bigmap.offsetX;
			let py = y * 32 - core.bigmap.offsetY;
			const t = new Transition();
			t.mode(hyper('sin', 'out'))
				.time(transitionTime)
				.absolute()
				.transition('x', px)
				.transition('y', py);
			let { x: hx, y: hy } = core.status.hero.loc;
			t.value.x = hx * 32 - core.bigmap.offsetX;
			t.value.y = hy * 32 - core.bigmap.offsetY;
			transitionList.push(t);
			t.ticker.add(() => {
				core.drawIcon('_autoItem_', item.id, t.value.x, t.value.y, 32, 32);
				let { x: hx, y: hy } = core.status.hero.loc;
				const heroPx = hx * 32 - core.bigmap.offsetX;
				const heroPy = hy * 32 - core.bigmap.offsetY;
				if (Math.abs(t.value.x - heroPx) < 0.05 && Math.abs(t.value.y - heroPy) < 0.05) {
					t.ticker.destroy();
					const index = transitionList.findIndex(v => v === t);
					if (index !== -1) transitionList.splice(index, 1);
				}
			});
		}

		return true;
	}

	if (!main.replayChecking) {
		const ticker = new Ticker();
		ticker.add(() => {
			if (!core.isPlaying()) return;
			const ctx = core.getContextByName('_autoItem_');
			if (!has(ctx)) return;
			core.clearMap(ctx);
		});
	}

	// ----------------- BFS -----------------
	function bfs(floorId, deep = Infinity, autoFlags) {
		core.extractBlocks(floorId);
		const objs = core.getMapBlocksObj(floorId);
		const bgMap = core.getBgMapArray(floorId);
		const { x, y } = core.status.hero.loc;
		const dir = Object.entries(core.utils.scan).map(v => [v[0], v[1].x, v[1].y]);
		const floor = core.status.maps[floorId];

		const queue = [
			[x, y]
		];
		const mapped = {
			[`${x},${y}`]: true
		};

		if (!autoFlags) autoFlags = { battle: true, item: true, wall: false };

		while (queue.length > 0 && deep > 0) {
			const [nx, ny] = queue.shift();
			dir.forEach(v => {
				const [tx, ty] = [nx + v[1], ny + v[2]];
				if (tx < 0 || ty < 0 || tx >= floor.width || ty >= floor.height) {
					return;
				}
				const loc = `${tx},${ty}`;
				if (mapped[loc]) return;
				const block = objs[loc];
				mapped[loc] = true;
				if (core.onSki && core.onSki(bgMap[ty][tx])) return;
				const { type, canGoThrough } = judge(block, nx, ny, tx, ty, v[0], floorId, autoFlags);
				if (!canGoThrough) return;

				if (type === 'enemy') {
					if (canBattle(block.event.id, tx, ty) && !block.disable) {
						core.battle(block.event.id, tx, ty);
						core.updateCheckBlock(floorId);
					} else {
						return;
					}
				} else if (type === 'item') {
					const item = core.material.items[block.event.id];
					// 修改：传入 autoFlags
					if (_performGetItem(item, tx, ty, loc, floorId, autoFlags)) {
						core.updateCheckBlock(floorId);
					} else {
						return;
					}
				} else if (type === 'wall' || type === 'lava') {
					if (block && !block.disable && !block.event.data) {
						if (type === 'wall') {
							core.removeBlock(tx, ty);
							core.updateCheckBlock(floorId);
						} else if (type === 'lava') {
							core.removeBlock(tx, ty);
							core.updateCheckBlock(floorId);
						}
					} else {
						return;
					}
				}

				if (hasBlockDamage(loc)) return;
				queue.push([tx, ty]);
			});
			deep--;
		}
	}

	this.auto = function () {
		if (!core.status.floorId || !core.status.checkBlock || !core.status.checkBlock.damage) return;
		if (core.status.event.id == 'action' || core.events.onSki && core.events.onSki() || core.status.lockControl || !core.maps._canMoveDirectly_checkGlobal() || core.hasFlag('poison')) return;

		let autoFlags = { battle: false, item: false, wall: false };
		try {
			const heroFlags = core.status.hero && core.status.hero.flags && core.status.hero.flags.__auto__;
			autoFlags.battle = !!heroFlags.battle;
			// 修改：不要强制转换 boolean，允许 item 为字符串状态
			autoFlags.item = heroFlags.item;
			autoFlags.wall = !!heroFlags.wall;
		} catch (e) {}
		if (!autoFlags.battle && !autoFlags.item && !autoFlags.wall) return;

		const { x, y } = core.status.hero.loc;
		const floor = core.floors[core.status.floorId];
		const loc = `${x},${y}`;
		const hasEvent = has(floor.events[loc]);
		if (hasEvent) return;
		const block = core.getBlock(x, y);
		if (block != null && block.event.cls !== 'items') return;

		const before = core.flags.__forbidSave__;
		core.flags.__forbidSave__ = true;
		core.flags.__statistics__ = true;

		let deep = Infinity;

		core.updateCheckBlock(core.status.floorId);

		if (hasBlockDamage(loc)) {
			deep = core.flags.enableGentleClick ? 1 : 0;
		}

		if (!core.getContextByName('_autoItem_')) {
			core.createCanvas(
				'_autoItem_',
				0,
				0,
				core._PX_ ?? core.__PIXELS__,
				core._PY_ ?? core.__PIXELS__,
				75
			);
		}

		bfs(core.status.floorId, deep, autoFlags);

		if (!core.isReplaying()) animateHwnd.start();

		core.flags.__statistics__ = false;
		core.flags.__forbidSave__ = before;
		core.updateStatusBar();
	}

	core.registerReplayAction("moveDirectly", function (action) {
		if (action.indexOf("move:") != 0) return false;

		var pos = action.substring(5).split(":");
		var x = parseInt(pos[0]),
			y = parseInt(pos[1]);
		var nowx = core.getHeroLoc('x'),
			nowy = core.getHeroLoc('y');
		var ignoreSteps = core.canMoveDirectly(x, y);
		if (!core.moveDirectly(x, y, ignoreSteps)) return false;
		if (core.status.replay.speed == 24) {
			core.replay();
			return true;
		}

		core.ui.drawArrow('ui', 32 * nowx + 16 - core.bigmap.offsetX, 32 * nowy + 16 - core.bigmap.offsetY,
			32 * x + 16 - core.bigmap.offsetX, 32 * y + 16 - core.bigmap.offsetY, '#FF0000', 3);
		var timeout = this.__replay_getTimeout();
		if (ignoreSteps < 10) timeout = timeout * ignoreSteps / 10;
		setTimeout(function () {
			core.clearMap('ui');
			core.replay();
		}, timeout);
		return true;
	});

	function update() {
		core.control.updateCheckBlock();
		core.control.updateDamage();
		core.auto();
		if (main.replayChecking) return;
	}
	core.moveOneStep = function (fromX, fromY, callback) {
		return core.control.moveOneStep(fromX, fromY, callback);
	}
	control.prototype.moveOneStep = function (fromX, fromY, callback) {
		const res = this.controldata.moveOneStep(fromX, fromY, callback);
		if (core.status.event.data)
			core.insertAction({ "type": "function", "function": "function(){ core.auto(); if (main.replayChecking) return; }" }, null, null, null, true);
		else
			update();
		return res;
	};

	control.prototype.moveDirectly = function (destX, destY, ignoreSteps) {
		const res = this.controldata.moveDirectly(
			destX,
			destY,
			ignoreSteps
		);
		update();
		return res;
	};
	control.prototype.setHeroLoc = function (name, value, noGather) {
		if (!core.status.hero) return;
		if (typeof name === 'object' && name !== null) {
			const locObject = name;
			const shouldGather = !value;
			Object.assign(core.status.hero.loc, locObject);
			if (('x' in locObject || 'y' in locObject) && shouldGather) {
				this.gatherFollowers();
			}
		} else {
			core.status.hero.loc[name] = value;
			if ((name == 'x' || name == 'y') && !noGather) {
				this.gatherFollowers();
			}
		}
		for (let i = 0; i < transitionList.length; i++) {
			const t = transitionList[i];
			let { x, y } = core.status.hero.loc;
			t.value.x = x * 32 - core.bigmap.offsetX;
			t.value.y = y * 32 - core.bigmap.offsetY;
		}
	}

	core.control.registerReplayAction("help", function (action) {
		if (!action || action.indexOf("help") !== 0) return false;
		core.insertAction([{ type: "insert", name: "游戏说明" }]);
		return true;
	});

	enemys.prototype.getEnemyValue = function (enemy, name, x, y, floorId) {
		floorId = floorId || core.status.floorId;

		// 1. 获取特定坐标点的覆盖信息
		const pointInfo = (((flags.enemyOnPoint || {})[floorId] || {})[x + "," + y] || {});

		// 如果指定了具体属性名且该点有覆盖，直接返回
		if (core.isset(name) && pointInfo[name] != null) {
			return pointInfo[name];
		}

		// 获取怪物的原始数据对象
		var enemyId = null;
		var enemyData = null;

		if (enemy == null) {
			var block = core.getBlock(x, y, floorId);
			if (block == null || block.event.cls.indexOf('enemy') !== 0) return null;
			enemyId = block.event.id;
			enemyData = core.material.enemys[enemyId];
		} else if (typeof enemy == 'string') {
			enemyId = enemy;
			enemyData = core.material.enemys[enemyId];
		} else {
			enemyId = enemy.id;
			enemyData = enemy;
		}

		if (enemyData == null) return null;

		// 2. 获取全局怪物属性覆盖信息 (来自 setEnemy)
		const enemyInfo = core.getFlag('enemyInfo', {});
		const globalEnemyOverlay = enemyInfo[enemyId] || {};

		// 深度克隆原始数据以防污染
		var resultEnemy = core.clone(enemyData);

		// 3. 优先级合并：原始数据 < 全局覆盖 (setEnemy) < 坐标覆盖 (enemyOnPoint)

		// 合并全局覆盖
		for (let key in globalEnemyOverlay) {
			if (globalEnemyOverlay.hasOwnProperty(key)) {
				resultEnemy[key] = globalEnemyOverlay[key];
			}
		}

		// 合并坐标点覆盖
		for (let key in pointInfo) {
			if (pointInfo.hasOwnProperty(key)) {
				resultEnemy[key] = pointInfo[key];
			}
		}

		// 根据参数返回
		if (!core.isset(name)) {
			return resultEnemy;
		} else {
			return resultEnemy[name];
		}
	}
},
    "交易统计": function () {
	// 在此增加新插件
	// 在此增加新插件
	this.showAltarSummary = function () {
		let lines = [];

		// 3F金币商店
		let moneyShop3F_hp = core.getFlag("moneyShop3F_hp_times", 0);
		let moneyShop3F_atk = core.getFlag("moneyShop3F_atk_times", 0);
		let moneyShop3F_def = core.getFlag("moneyShop3F_def_times", 0);
		let moneyShop3F_times = core.getFlag("moneyShop3F_times", 0);
		if (moneyShop3F_times > 0) {
			// 价格为 20 + 1*times（先扣费后 times+=1），总花费为等差数列求和：sum_{i=0..n-1}(20+i)
			let totalGoldCost = (moneyShop3F_times * 20) + (moneyShop3F_times * (moneyShop3F_times - 1)) / 2;
			let shopLines = [];
			if (moneyShop3F_hp > 0) shopLines.push(`生命+${moneyShop3F_hp * 500}`);
			if (moneyShop3F_atk > 0) shopLines.push(`攻击+${moneyShop3F_atk * 3}`);
			if (moneyShop3F_def > 0) shopLines.push(`防御+${moneyShop3F_def * 3}`);
			lines.push(`【3F金币商店】购买${moneyShop3F_times}次（生命${moneyShop3F_hp}次，攻击${moneyShop3F_atk}次，防御${moneyShop3F_def}次），花费金币${totalGoldCost}，${shopLines.join('，')}`);
		}

		// B5F金币商店
		let moneyShopB5F_hp = core.getFlag("moneyShopB5F_hp_times", 0);
		let moneyShopB5F_atk = core.getFlag("moneyShopB5F_atk_times", 0);
		let moneyShopB5F_def = core.getFlag("moneyShopB5F_def_times", 0);
		let moneyShopB5F_times = core.getFlag("moneyShopB5F_times", 0);
		if (moneyShopB5F_times > 0) {
			// 价格为 50 + 2*times（先扣费后 times+=1），总花费 sum_{i=0..n-1}(50+2i)
			let totalGoldCost = (moneyShopB5F_times * 50) + (moneyShopB5F_times * (moneyShopB5F_times - 1));
			let shopLines = [];
			if (moneyShopB5F_hp > 0) shopLines.push(`生命+${moneyShopB5F_hp * 800}`);
			if (moneyShopB5F_atk > 0) shopLines.push(`攻击+${moneyShopB5F_atk * 6}`);
			if (moneyShopB5F_def > 0) shopLines.push(`防御+${moneyShopB5F_def * 6}`);
			lines.push(`【B5F金币商店】购买${moneyShopB5F_times}次（生命${moneyShopB5F_hp}次，攻击${moneyShopB5F_atk}次，防御${moneyShopB5F_def}次），花费金币${totalGoldCost}，${shopLines.join('，')}`);
		}

		// 7F经验商店
		let expShop7F_lv = core.getFlag("expShop7F_lv_times", 0);
		let expShop7F_atk = core.getFlag("expShop7F_atk_times", 0);
		let expShop7F_def = core.getFlag("expShop7F_def_times", 0);
		let expShop7F_times = core.getFlag("expShop7F_times", 0);
		if (expShop7F_times > 0) {
			let totalExpCost = (expShop7F_lv * 70) + (expShop7F_atk * 20) + (expShop7F_def * 20);
			let totalHp = expShop7F_lv * 250;
			let totalAtk = (expShop7F_lv * 3) + (expShop7F_atk * 1);
			let totalDef = (expShop7F_lv * 3) + (expShop7F_def * 2);
			lines.push(`【7F经验商店】交易${expShop7F_times}次（升级${expShop7F_lv}次，攻击${expShop7F_atk}次，防御${expShop7F_def}次），花费经验${totalExpCost}，总计：生命+${totalHp}，攻击+${totalAtk}，防御+${totalDef}，等级+${expShop7F_lv}，攻击+${expShop7F_atk * 1}(单独购买)，防御+${expShop7F_def * 2}(单独购买)`);
		}

		// B7F经验商店
		let expShopB7F_lv = core.getFlag("expShopB7F_lv_times", 0);
		let expShopB7F_atk = core.getFlag("expShopB7F_atk_times", 0);
		let expShopB7F_def = core.getFlag("expShopB7F_def_times", 0);
		let expShopB7F_times = core.getFlag("expShopB7F_times", 0);
		if (expShopB7F_times > 0) {
			let totalExpCost = (expShopB7F_lv * 190) + (expShopB7F_atk * 50) + (expShopB7F_def * 50);
			let totalHp = expShopB7F_lv * 600;
			let totalAtk = (expShopB7F_lv * 10) + (expShopB7F_atk * 3);
			let totalDef = (expShopB7F_lv * 10) + (expShopB7F_def * 5);
			lines.push(`【B7F经验商店】交易${expShopB7F_times}次（升级${expShopB7F_lv}次，攻击${expShopB7F_atk}次，防御${expShopB7F_def}次），花费经验${totalExpCost}，总计：生命+${totalHp}，攻击+${totalAtk}，防御+${totalDef}，等级+${expShopB7F_lv * 3}，攻击+${expShopB7F_atk * 3}(单独购买)，防御+${expShopB7F_def * 5}(单独购买)`);
		}

		// 购入钥匙（小偷）
		let keyShop_yellow = core.getFlag("keyShop_yellow_times", 0);
		let keyShop_blue = core.getFlag("keyShop_blue_times", 0);
		let keyShop_times = core.getFlag("keyShop_times", 0);
		if (keyShop_times > 0) {
			let totalKeys = keyShop_yellow + keyShop_blue;
			let totalGold = (keyShop_yellow * 20) + (keyShop_blue * 80);
			let shopLines = [];
			if (keyShop_yellow > 0) shopLines.push(`黄钥匙${keyShop_yellow}把`);
			if (keyShop_blue > 0) shopLines.push(`蓝钥匙${keyShop_blue}把`);
			lines.push(`【购入钥匙】共${totalKeys}把，花费金币${totalGold}：${shopLines.join('，')}`);
		}

		// 出售钥匙（商人）
		let keySell_yellow = core.getFlag("keySell_yellow_times", 0);
		let keySell_blue = core.getFlag("keySell_blue_times", 0);
		let keySell_red = core.getFlag("keySell_red_times", 0);
		let totalSellKeys = keySell_yellow + keySell_blue + keySell_red;
		if (totalSellKeys > 0) {
			let totalGold = core.getFlag("keySell_money", 0);
			let sellLines = [];
			if (keySell_yellow > 0) sellLines.push(`黄钥匙${keySell_yellow}把`);
			if (keySell_blue > 0) sellLines.push(`蓝钥匙${keySell_blue}把`);
			if (keySell_red > 0) sellLines.push(`红钥匙${keySell_red}把`);
			lines.push(`【出售钥匙】共${totalSellKeys}把，获得金币${totalGold}：${sellLines.join('，')}`);
		}

		// 任意门商店
		let keydoorShop_keydoor = core.getFlag("keydoorShop_keydoor_times", 0);
		if (keydoorShop_keydoor > 0) {
			lines.push(`【任意门商店】购买${keydoorShop_keydoor}次，花费金币${keydoorShop_keydoor * 150}，任意门+${keydoorShop_keydoor}`);
		}

		if (lines.length === 0) {
			core.insertAction([{
				"type": "text",
				"text": "你尚未进行过任何购买或出售。"
			}]);
			return;
		}

		const resultText = "\t[交易统计]\n" + lines.join("\n");
		core.insertAction([{
			"type": "text",
			"text": resultText,
			"pos": [16, 16, 384, 384]
		}]);
	}

	ui.prototype._drawStatistics_buildObj = function () {
		// 数据统计要统计如下方面：
		// 1. 当前全塔剩余下的怪物数量，总金币数，总经验数，总加点数
		// 2. 当前全塔剩余的黄蓝红铁门数量，和对应的钥匙数量
		// 3. 当前全塔剩余的三种宝石数量，血瓶数量，装备数量；总共增加的攻防生命值
		// 4. 当前层的上述信息
		// 5. 当前已走的步数；瞬间移动的步数，瞬间移动的次数（和少走的步数）；游戏时长
		// 6. 当前已恢复的生命值；当前总伤害、战斗伤害、阻激夹域血网伤害、中毒伤害。
		var ori = this.uidata.drawStatistics();
		var ids = ori.filter(function (e) {
			return e.endsWith("Door") || core.material.items[e];
		});
		var cnt = {},
			cls = {},
			ext = {};
		ids.forEach(function (e) {
			if (e.endsWith("Door")) cls[e] = "doors";
			else cls[e] = core.material.items[e].cls;
			cnt[e] = 0;
		})
		var order = ["doors", "items", "tools", "constants", "equips"];
		ids.sort(function (a, b) {
			var c1 = order.indexOf(cls[a]),
				c2 = order.indexOf(cls[b]);
			if (c1 == c2) return ori.indexOf(a) - ori.indexOf(b);
			return c1 - c2;
		});
		var obj = {
			'monster': {
				'count': 0,
				'money': 0,
				'exp': 0,
				'point': 0,
			},
			'count': cnt,
			'add': {
				'hp': 0,
				'atk': 0,
				'def': 0,
				'mdef': 0,
				'agi': 0, // 增加敏捷统计
				'lv': 0 // 增加等级统计
			}
		};
		return { ids: ids, cls: cls, ext: ext, total: core.clone(obj), current: core.clone(obj), marked: core.clone(obj) };
	}

	ui.prototype._drawStatistics_items = function (floorId, floor, id, obj) {
		var hp = 0,
			atk = 0,
			def = 0,
			mdef = 0,
			agi = 0,
			lv = 0;

		// 如果是消耗类道具（不含超级圣水防止死循环）
		if (obj.cls[id] == 'items' && id != 'superPotion') {
			var temp = core.clone(core.status.hero);
			core.setFlag("__statistics__", true);
			var ratio = core.status.thisMap.ratio;
			core.status.thisMap.ratio = core.clone(core.status.maps[floorId].ratio);
			try { eval(core.material.items[id].itemEffect); } catch (e) {}
			core.status.thisMap.ratio = ratio;

			// 计算各项数值差值
			hp = core.status.hero.hp - temp.hp;
			atk = core.status.hero.atk - temp.atk;
			def = core.status.hero.def - temp.def;
			mdef = core.status.hero.mdef - temp.mdef;
			agi = core.status.hero.agi - temp.agi;
			lv = core.status.hero.lv - temp.lv;

			// 还原状态
			core.status.hero = temp;
			window.hero = core.status.hero;
			window.flags = core.status.hero.flags;
		}
		// 如果是装备类
		else if (obj.cls[id] == 'equips') {
			var values = core.material.items[id].equip || {};
			atk = values.atk || 0;
			def = values.def || 0;
			mdef = values.mdef || 0;
			agi = values.agi || 0;
			lv = values.lv || 0;
		}

		if (id.indexOf('sword') == 0 || id.indexOf('shield') == 0 || obj.cls[id] == 'equips') {
			var t = "";
			if (lv > 0) t += lv + core.getStatusLabel('lv');
			if (atk > 0) t += atk + core.getStatusLabel('atk');
			if (def > 0) t += def + core.getStatusLabel('def');
			if (mdef > 0) t += mdef + core.getStatusLabel('mdef');
			if (agi > 0) t += agi + core.getStatusLabel('agi');
			if (t != "") obj.ext[id] = t;
		}

		// 累加到统计对象
		this._drawStatistics_add(floorId, obj, 'count', id, 1);
		this._drawStatistics_add(floorId, obj, 'add', 'hp', hp);
		this._drawStatistics_add(floorId, obj, 'add', 'atk', atk);
		this._drawStatistics_add(floorId, obj, 'add', 'def', def);
		this._drawStatistics_add(floorId, obj, 'add', 'mdef', mdef);
		this._drawStatistics_add(floorId, obj, 'add', 'agi', agi);
		this._drawStatistics_add(floorId, obj, 'add', 'lv', lv);
	}

	ui.prototype._drawStatistics_generateText = function (obj, type, data) {
		var text = type + "地图中：\n";
		text += "共有怪物" + data.monster.count + "个";
		if (core.flags.statusBarItems.indexOf('enableMoney') >= 0) text += "，总金币数" + data.monster.money;
		if (core.flags.statusBarItems.indexOf('enableExp') >= 0) text += "，总经验数" + data.monster.exp;
		if (core.flags.enableAddPoint) text += "，总加点数" + data.monster.point;
		text += "。\n";

		var prev = "";
		obj.ids.forEach(function (key) {
			var value = data.count[key];
			if (value == 0) return;
			if (obj.cls[key] != prev) {
				if (prev != "") text += "。";
				text += "\n";
			} else text += "，";
			prev = obj.cls[key];
			var name = ((core.material.items[key] || (core.getBlockById(key) || {}).event) || {}).name || key;
			text += name + value + "个";
			if (obj.ext[key])
				text += "(" + obj.ext[key] + ")";
		})
		if (prev != "") text += "。";

		text += "\n";
		text += "共加等级" + data.add.lv + "级，生命值" + core.formatBigNumber(data.add.hp) + "点，攻击" +
			core.formatBigNumber(data.add.atk) + "点，防御" +
			core.formatBigNumber(data.add.def) + "点，敏捷" +
			core.formatBigNumber(data.add.agi) + "点，护盾" +
			core.formatBigNumber(data.add.mdef) + "点。";
		return text;
	}
},
    "绘制工具栏": function () {
	// 覆写setToolbarButton函数，在竖屏时绘制自定义工具栏
	var originalSetToolbarButton = control.prototype.setToolbarButton;
	control.prototype.setToolbarButton = function (useButton) {
		// 调用原函数
		originalSetToolbarButton.call(this, useButton);
		this._drawCustomToolbar();
	};

	// 修改绘制函数
	control.prototype._drawCustomToolbar = function () {
		if (!core.domStyle.isVertical) return;
		if (!core.dom.toolCanvas || !core.dom.toolCanvasCtx) return;
		if (!core.material.images.images['menu_mobile_toolbar.png']) return;

		var canvas = core.dom.toolCanvas;
		var ctx = core.dom.toolCanvasCtx;
		var w = canvas.width / core.domStyle.ratio;
		var h = canvas.height / core.domStyle.ratio;

		// 清空画布并绘制背景
		ctx.clearRect(0, 0, w, h);
		core.drawImage(ctx, 'menu_mobile_toolbar.png', 0, 0, w, h);

		// 统一定义中心点横坐标
		var centers = [32, 82, 132, 182, 232, 282, 332, 382];
		var y = 24; // 文字基线或图标中心参考

		if (core.isReplaying()) {
			// --- 录像回放状态：绘制图标 ---
			var icons = [
				core.status.replay.pausing ? 'play' : 'pause',
				'stop',
				'rewind',
				'speedDown',
				'speedUp',
				'save',
				'book',
				'upFloor'
			];

			for (var i = 0; i < icons.length; i++) {
				var iconSize = 26;
				var offset = iconSize / 2;
				core.drawIcon(ctx, icons[i], centers[i] - offset, 19 - offset, iconSize, iconSize);
			}
		} else {
			// --- 正常游戏状态：绘制文字 ---
			var texts = ["心镜", "羽根", "背包", "键盘", "商店", "保存", "读取", "设置"];

			for (var i = 0; i < texts.length; i++) {
				core.setTextAlign(ctx, "center");

				// 动态计算颜色
				var color = "#FFFFFF";
				if (i === 0 && !core.hasItem('book')) color = "#888888";
				else if (i === 1 && !core.hasItem('fly')) color = "#888888";

				core.fillBoldText(
					ctx,
					texts[i],
					centers[i],
					y,
					color,
					"#000000",
					"14px 布丁体"
				);
			}
		}

		core.setTextAlign(ctx, "left");
	};

	core.registerResize("toolBar", function (obj) {
		// toolBar
		var toolBar = core.dom.toolBar;

		if (core.domStyle.isVertical) {
			toolBar.style.left = 0;
			toolBar.style.right = "";
			toolBar.style.width = obj.outerSize + "px";
			toolBar.style.top = obj.statusBarHeightInVertical + obj.outerSize + "px";
			toolBar.style.height = obj.toolbarHeightInVertical + "px";
			toolBar.style.background = obj.globalAttribute.toolsBackground;
		} else {
			toolBar.style.display = 'none';
			toolBar.style.left = "";
			toolBar.style.right = 0;
			toolBar.style.width = 0;
			toolBar.style.top = 0;
			toolBar.style.height = 0;
			toolBar.style.background = obj.globalAttribute.toolsBackground;
			return;
		}
		toolBar.style.borderLeft = obj.border;
		toolBar.style.borderRight = toolBar.style.borderBottom = core.domStyle.isVertical || obj.extendToolbar ? obj.border : '';
		toolBar.style.fontSize = 16 * core.domStyle.scale + "px";

		if (!core.domStyle.showStatusBar && !core.domStyle.isVertical && !obj.extendToolbar) {
			toolBar.style.display = 'none';
		} else {
			toolBar.style.display = 'block';
		}

		if (core.domStyle.isVertical) {
			var toolCanvas = core.dom.toolCanvas;
			var ctx = core.dom.toolCanvasCtx;

			if (!core.dom.toolCanvas || !core.dom.toolCanvasCtx) return;

			var canvasWidth, canvasHeight;
			if (core.domStyle.isVertical) {
				canvasWidth = obj.CANVAS_WIDTH;
				canvasHeight = obj.TOOLBAR_HEIGHT;
			} else {
				if (obj.extendToolbar) {
					canvasWidth = obj.CANVAS_WIDTH;
					canvasHeight = obj.TOOLBAR_HEIGHT;
				} else {
					canvasWidth = obj.BAR_WIDTH;
					canvasHeight = Math.floor(obj.CANVAS_WIDTH * 0.281); // 与原来工具区比例一致
				}
			}
			// 设置 canvas 实际像素尺寸（支持高清屏）
			core.maps._setHDCanvasSize(ctx, canvasWidth, canvasHeight);
			toolCanvas.style.width = "100%";
			toolCanvas.style.height = "100%";
			toolCanvas.style.position = "absolute";
			toolCanvas.style.left = "0";
			toolCanvas.style.top = "0";

			// 隐藏原有的 img 工具
			for (var i = 0; i < core.dom.tools.length; i++) {
				core.dom.tools[i].style.display = "none";
			}
			core.dom.hard.style.display = "none";
			this._drawCustomToolbar();
		}
	});
},
    "游戏说明": function () {
	// 扩展 control 原型，实现游戏说明文字浏览功能（原图片改为文字，带背景图）
	this.gameIntroduce = function () {
		// ===== 【预留：游戏说明文字列表，你可自行修改】=====
		const gameIntroTexts = [
			`《新新魔塔》被称为“三原塔”之一，由原作者cos105hk于2005年发布，主要版本包含V1.1、V1.33、V1.4等。
本作是由三原塔翻新小组在前人基础上翻新的H5复刻版，复刻对应原作版本为最经典的FlashV1.1，与原作主要区别如下：

1、开局可选择“经典模式”与“极速模式”。
【经典模式】还原原作的战斗模式。显示完整的战斗流程，战斗伤害为非固定值（随机触发暴击或闪避）。主角可以从战斗中撤退（怪物体力重置，主角体力不重置，但也无法通过撤退令主角血量超越战前）。
【极速模式】 H5自创的战斗模式。预先计算战斗伤害并瞬间完成战斗，战斗伤害为固定值（取数学期望）。主角不能从战斗中撤退。

2、修复原作中的恶性bug，增加一键清零伤怪、一键拾取、一键魔塔语、交易统计等功能（可在游戏中“设置”按钮下的第二个子按钮“便捷功能”里打开）。

3、可使用H5样板的便捷功能。`,
			`体力（HP）
即“生命值”或“血量”，指角色被消灭前的最大伤害承受力。
可通过拾取血瓶/圣水、提升等级、商店购买恢复体力；体力没有上限。
攻击力（ATK）
角色或怪物造成伤害的能力。每点攻击力可以提高每次攻击的1点伤害。
可通过拾取红宝石/剑、提升等级、商店购买增加攻击力。
防御力（DEF）
角色或怪物抵御伤害的能力。每点防御力可以降低每次受击的1点伤害。
可通过拾取蓝宝石/盾、提升等级、商店购买增加防御力。

暴击值（CRI）
角色或怪物每次攻击有概率“暴击”，暴击时伤害结果将变为2倍。

敏捷值（AGI）
角色或怪物每次受击有概率“闪避”，闪避时伤害结果将变为0。`,
			`等级（LV）
等级对游戏数据没有直接影响，只作为解锁高等级“章”的增幅器条件。
20级可解锁二级章的增幅器，35级可解锁三级章的增幅器。
等级可通过拾取跃进之翼、经验商店购买提升。
经验值（EXP）
经验值通过杀怪获得，可在“战斗之神”（经验商店）处提升等级或购买攻防。
地上7F经验商店：70经验升1级+250血3攻3防；20经验换1攻或2防。
地下17F经验商店：190经验升3级+600血10攻10防；50经验换3攻或5防。

金币（GOLD）
金币通过杀怪或拾取获得，可在“贪婪之神”（金币商店）处购买血量或攻防。
地上3F金币商店：初始价格20金换500血或3攻或3防，每买一次涨价1金。
地下15F金币商店：初始价格50金换800血或6攻或6防，每买一次涨价2金。

钥匙
魔塔中共有黄、蓝、红三种颜色的钥匙，可用于打开对应颜色的门。
2F盗贼（开通地下后移动到0F）：分别以20/80金币出售黄钥匙/蓝钥匙。
15F商人：分别以10/40/500金币收购黄钥匙/蓝钥匙/红钥匙。`,
			`攻击临界
新新魔塔独创设定。当主角的攻击力小于怪物的防御力时：
若主角的攻击力与攻击临界之和大于等于怪物的防御力，则主角每次攻击可以造成1点伤害（暴击时为2点）；否则无法造成伤害。
初始攻击临界为0，铁剑/长剑/宝石剑/灵光剑/勇者之剑分别增加10/20/30/40/50点攻击临界。

防御临界
新新魔塔独创设定。当主角的防御力大于怪物的攻击力时：
若怪物的攻击力与主角的防御临界之和大于等于主角的防御力，则怪物每次攻击可以造成1点伤害（暴击时为2点）；否则无法造成伤害。
初始防御临界为30，皮革之盾/铁盾/光之盾/圣光十字盾/勇者之盾均减少5点防御临界。
（可以理解为“全体怪物共用的攻击临界”，此数值越低越好）`,
			`负面状态
主角与部分怪物交战时，有可能会陷入中毒或衰弱状态。
中毒状态可以免疫衰弱，衰弱状态可以免疫中毒。
若主角处于中毒状态，则每走一步将失去5点体力。
若主角处于衰弱状态，则攻击、防御、敏捷均下降为原来的50%（四舍五入）。
拾取地图上的解毒剂/火酒可以解除中毒/衰弱状态。
衰弱状态中若拾取宝石、商店购买攻防，新增属性不会乘以50%，并在解衰后全额保留（先解衰再加属性，还是先加属性再解衰，最终结果完全一致）。

负面状态的判定条件
【经典模式】勇士每次受到毒蝙蝠/毒丧尸/毒龙攻击（注意是每次受击，不是每场战斗），均有12%/14%/20%概率进入中毒状态。勇士每一次受到巨大八爪鱼/高级骷髅兵/大魔王攻击，均有7%/13%/10%概率进入“衰弱中”状态（战斗胜利后将由“衰弱中”状态转为“衰弱”状态）。
【极速模式】和上述六种怪物战斗，则100%会中毒/衰弱；但是中毒状态免疫衰弱，衰弱状态免疫中毒。`,
			`特殊物品说明

随意门：当勇者面对某些特定的墙或熔岩时，可使面前一格地形变为黄门，使用成功后，随意门-1。先从地下7F左下老人处免费获得5个，之后可找地下14F左下老人补充，售价为150金币/个。

神剑之证：当勇者面对怪物时，可使面前一格怪物变为绿色史莱姆，使用成功后，神剑之证-1。（对魔王格勒第、大魔王、毒龙、暗黑魔法师、黄金史莱姆、白银史莱姆无效）。在地下10F的红门内获得5个，无法补充数量。

锄头：交给主塔2F盗贼，开启通往地下的通道。战胜主塔20F假公主获得。

宝石锄头：共有4把，用来打开地下22层的4道花门。

冰之护符：持有后可使勇者不受熔岩的伤害。在地下15F的3道红门内获得。

魔法护符：攻击方式为魔法攻击的怪物的防御力下降(勇者的防御力/3)，四舍五入，且最低下降为0，但对暗黑魔法师无效。在地下16F左下商人处购买，售价为700金币。

十字架：一旦勇者的体力0时，则体力+2000。在地下13F穿越多个岩浆后获得。
`,
			`章
“章”是一种可以用于赋予主角新外貌与特殊技能的宝物，在魔塔（隐藏层）5层可获得，在主塔14层老人处鉴定后生效，主角到达20级/35级后可对“章”进行二级/三级增幅器的强化。
<img class="intro-img" src="project/images/yongzhe.png" data-base-width="100">勇者之证（镶有红宝石的章）
被鉴定时，主角将晋升为勇者，铠甲变为红色。勇者能充分发挥攻击力的优势，迅猛出招，造成令人生畏的伤害，快速解决战斗。

<img class="intro-img" src="project/images/bazhe.png" data-base-width="100">霸者之证（镶有黑珍珠的章）
被鉴定时，主角被晋升为霸者，铠甲变为黑色。霸者在受到攻击时，有概率反弹受到的伤害，或在战斗中提升自己的防御力。

<img class="intro-img" src="project/images/xianzhe.png" data-base-width="100">贤者之证（镶有钻石的章）
被鉴定时，主角被晋升为贤者，铠甲变为黄色。贤者在闪避敌人攻击时，有概率将攻击吸收并转化为自己的体力。`,
			`勇者之证效果：
【一阶勇者】暴击率+5%。
【二阶勇者】攻击次数+1。（勇士每回合可以进行2次独立的攻击）
【三阶勇者】当主角的一次攻击击中怪物且能造成伤害，并且主角防御力小于怪物攻击力时，有16%的几率主角对怪物造成附加伤害（暴击判定前），数值为主角的防御力除以4。 （对铁怪无效）
注：低阶勇者的效果在增幅器强化后依然有效。

增幅方法：
【一阶升二阶】主角等级20级时，花费500金币，从主塔0层右上角奸商处获得。
【二阶升三阶】主角等级35级时，地下14层左下角红门内的商人处免费获得。`,
			`霸者之证效果：
【一阶霸者】攻击力+15，防御力+15。一阶反弹：当非魔法师系怪物的一次攻击击中主角时，主角有6%的几率反弹攻击，如果怪物对主角的伤害/2≧怪物体力，那么怪物直接死亡，否则主角承受伤害=怪物对主角的伤害，怪物承受伤害=怪物对主角的伤害/2。（攻击反弹时，怪物不会暴击。当怪物击中主角时，先进行是否反弹的判定，如果判定为不反弹，接着才会进入暴击的判定。）
【二阶霸者】二阶反弹：当非魔法师系怪物的一次攻击击中主角时，主角有11%的几率反弹攻击，如果怪物对主角的伤害≧怪物体力，那么怪物直接死亡，否则主角承受伤害=怪物对主角的伤害/2，怪物承受伤害=怪物对主角的伤害。二阶霸体：当非魔法师系怪物的一次攻击击中主角但没有触发反弹功效时，主角有11%的几率本次攻击伤害计算中防御力*2。
【三阶霸者】三阶反弹：当非魔法师系怪物的一次攻击击中主角时，主角有16%的几率反弹攻击，如果怪物攻击力/2≧怪物体力，那么怪物直接死亡，否则主角承受伤害=怪物对主角的伤害/3，怪物承受伤害=怪物攻击力/3。（三阶反弹目标为毒龍、大魔王、魔王格勒第时，双方均不受伤）三阶霸体：当非魔法师系怪物的一次攻击击中主角但没有触发反弹功效时，主角有16%的几率本次攻击伤害计算中防御力*2。破防：当主角的一次攻击击中怪物时，怪物有11%的几率本次攻击伤害计算中防御力-怪物防御力/1.3。（破防技能对魔王格勒第无效）

增幅方法：
【一阶升二阶】主角等级20级时，主塔1层左上角的老人处免费获得。
【二阶升三阶】主角等级35级时，花费100经验值，从主塔19层上方老人获得。`,
			`贤者之证效果：
【一阶贤者】闪避率+5%。一阶回血：当怪物的一次攻击没打中主角时，主角有61%的几率回复体力，如果主角当前体力小于300，那么回复体力=怪物攻击力/5+100，否则回复体力=怪物攻击力/5。
【二阶贤者】二阶回血：当怪物的一次攻击没打中主角时，主角有81%的几率回复体力，如果主角当前体力小于300，那么回复体力=怪物攻击力/5+200，否则回复体力=怪物攻击力/5。二阶集能：当非魔法师系怪物的一次攻击击中主角且能造成伤害时，主角有11%的几率收集一次能量，同时减伤50点（怪物暴击判定前），每次收集的能量会使主角在战斗胜利后随机回复0到29点体力。
【三阶贤者】三阶回血：当怪物的一次攻击没打中主角时，如果主角当前体力小于300，那么回复体力=怪物攻击力/5+300，否则回复体力=怪物攻击力/5。三阶集能：当怪物的一次攻击击中主角时，如果该怪物不是魔法师系怪物且能对主角造成伤害，那么主角有31%的几率收集一次能量，同时减伤50点（怪物暴击判定前），每次收集的能量会使主角在最近的一次战斗胜利后随机回复0到29点体力；如果该怪物是魔法师系怪物，那么主角有21%的几率收集一次能量，同时减伤100点（怪物暴击判定前），每次收集的能量会使主角在战斗胜利后随机回复体力为：怪物攻击力到怪物攻击力+19。
（所有减伤均可减为负数）

增幅方法：
【一阶升二阶】主角等级20级时，地下7层右下角的商人处免费获得。
【二阶升三阶】主角等级35级时，主塔15层右上角的商人处免费获得。`,
			`秒杀
当怪物不是魔法师，怪物的攻击击中了勇士，没有触发霸者之证的反弹或霸体效果，且怪物的攻击力与勇士的防御临界之和小于勇士的防御（即怪物不能对勇士造成伤害），且勇士攻击力大于怪物防御力时，有16%概率触发秒杀效果，立刻终止战斗，怪物直接被杀死。

注意事项（关于极速模式）：
在极速模式下，对于无章、勇者之证以及霸者之证来说，只有怪物的期望伤害是0的时候才能触发秒杀，因此有没有秒杀效果是没有区别的。
但对于贤者之证来说，触发秒杀杀死怪物会导致战斗回合数减少，从而导致贤者之证的回血降低，因此：
从"未防杀状态"加防变为"完美防杀状态"时，可能会让极速模式下的贤者之证的吸血量降低。`,
		];

		// ===== 目录标题数组（与 gameIntroTexts 一一对应）=====
		const gameIntroTitles = [
			"本作简介",
			"基础属性",
			"等级/经验/金币/钥匙",
			"攻击临界与防御临界",
			"负面状态",
			"特殊物品说明",
			"章（勇者/霸者/贤者）",
			"勇者之证效果",
			"霸者之证效果",
			"贤者之证效果",
			"秒杀机制"
		];

		let currentIndex = 0;
		const startButtonGroup = document.getElementById('startButtonGroup');
		const startPanel = document.getElementById('startPanel');
		if (!startPanel) {
			alert('未找到startPanel容器，请检查容器ID是否正确！');
			return;
		}
		startPanel.style.position = 'relative';

		const scaleRatio = core?.domStyle?.scale ? (core.domStyle.scale / 1.5) : 1;
		const finalScale = Math.max(scaleRatio, 0.1);

		const overlayStyle = `
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.85);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 9999;
        cursor: pointer;
        box-sizing: border-box;
    `;

		const modalStyle = `
        position: relative;
        width: 80%;
        height: 80%;
        max-width: 1200px;
        max-height: 800px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        box-sizing: border-box;
    `;

		const bgImageStyle = `
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
        z-index: 1;
        opacity: 0.9;
        box-sizing: border-box;
    `;

		const baseFontSize = 18;
		const scaledFontSize = baseFontSize * finalScale;
		const textContainerStyle = `
        position: relative;
        z-index: 2;
        width: 90%;
        height: 80%;
        font-family: 'ubuntu', sans-serif;
        font-size: ${scaledFontSize}px;
        color: #fff;
        font-weight: bold;
        text-align: left;
        line-height: 1.5;
        padding: 20px;
        box-sizing: border-box;
        white-space: pre-line;
        overflow-y: auto;
        text-shadow: 
            -1px -1px 0 #000,
             1px -1px 0 #000,
            -1px  1px 0 #000,
             1px  1px 0 #000;
    `;

		const baseTipFontSize = 16;
		const scaledTipFontSize = baseTipFontSize * finalScale;
		const tipTextStyle = `
        position: relative;
        z-index: 3;
        width: 90%;
        padding: 10px 20px;
        margin-top: 10px;
        font-family: 'ubuntu', sans-serif;
        font-size: ${scaledTipFontSize}px;
        color: #fff;
        font-weight: bold;
        text-align: center;
        text-shadow: 
            -1px -1px 0 #000,
             1px -1px 0 #000,
            -1px  1px 0 #000,
             1px  1px 0 #000;
        box-sizing: border-box;
    `;

		// 目录样式（独立面板，显示在正文上层）
		const tocContainerStyle = `
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 10;
        background: rgba(0, 0, 0, 0.9);
        display: none;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        overflow-y: auto;
        padding: 20px;
        box-sizing: border-box;
    `;

		// 查找或创建遮罩层
		let overlay = document.getElementById('gameIntroOverlay');
		if (!overlay) {
			overlay = document.createElement('div');
			overlay.id = 'gameIntroOverlay';
			overlay.style.cssText = overlayStyle;

			const modal = document.createElement('div');
			modal.id = 'gameIntroModal';
			modal.style.cssText = modalStyle;

			const bgImage = document.createElement('img');
			bgImage.id = 'gameIntroBg';
			bgImage.src = 'project/images/startBackground.png';
			bgImage.style.cssText = bgImageStyle;
			modal.appendChild(bgImage);

			const textElement = document.createElement('div');
			textElement.id = 'gameIntroText';
			textElement.style.cssText = textContainerStyle;
			modal.appendChild(textElement);

			// 提示行（包含翻页提示和目录按钮）
			const tipWrapper = document.createElement('div');
			tipWrapper.style.cssText = tipTextStyle;
			const tipLabel = document.createElement('span');
			tipLabel.textContent = '-点击屏幕左侧或右侧翻页；点击屏幕中间返回游戏-';
			const tocButton = document.createElement('span');
			tocButton.id = 'gameIntroTocBtn';
			tocButton.textContent = '[目录]';
			tocButton.style.cssText = `
            color: #ffd700;
            cursor: pointer;
            margin-left: 15px;
            font-weight: bold;
            text-decoration: underline;
        `;
			tipWrapper.appendChild(tipLabel);
			tipWrapper.appendChild(tocButton);
			modal.appendChild(tipWrapper);

			// 目录容器
			const tocContainer = document.createElement('div');
			tocContainer.id = 'gameIntroToc';
			tocContainer.style.cssText = tocContainerStyle;
			modal.appendChild(tocContainer);

			overlay.appendChild(modal);
			startPanel.appendChild(overlay);
		} else {
			overlay.style.cssText = overlayStyle;
			const modal = document.getElementById('gameIntroModal');
			modal.style.cssText = modalStyle;
			const bgImage = document.getElementById('gameIntroBg');
			bgImage.style.cssText = bgImageStyle;
			const textElement = document.getElementById('gameIntroText');
			textElement.style.cssText = textContainerStyle;
			const tipWrapper = document.getElementById('gameIntroTip') || (() => {
				const tw = document.createElement('div');
				tw.id = 'gameIntroTip';
				tw.style.cssText = tipTextStyle;
				modal.appendChild(tw);
				return tw;
			})();
			// 确保目录按钮存在
			if (!document.getElementById('gameIntroTocBtn')) {
				const tocButton = document.createElement('span');
				tocButton.id = 'gameIntroTocBtn';
				tocButton.textContent = '[目录]';
				tocButton.style.cssText = `
                color: #ffd700;
                cursor: pointer;
                margin-left: 15px;
                font-weight: bold;
                text-decoration: underline;
            `;
				tipWrapper.appendChild(tocButton);
			}
		}

		// 动态生成目录
		const tocContainer = document.getElementById('gameIntroToc');
		tocContainer.innerHTML = '';
		gameIntroTitles.forEach((title, idx) => {
			const btn = document.createElement('div');
			btn.textContent = `${idx + 1}. ${title}`;
			btn.style.cssText = `
            font-family: 'ubuntu', sans-serif;
            font-size: ${scaledFontSize}px;
            color: #fff;
            font-weight: bold;
            margin: ${6 * finalScale}px 0;
            cursor: pointer;
            text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
            transition: color 0.2s;
        `;
			btn.addEventListener('mouseenter', () => btn.style.color = '#ffd700');
			btn.addEventListener('mouseleave', () => btn.style.color = '#fff');
			btn.onclick = (e) => {
				e.stopPropagation();
				currentIndex = idx;
				renderIntroText(currentIndex);
				tocContainer.style.display = 'none';
			};
			tocContainer.appendChild(btn);
		});

		// 目录按钮事件
		const tocButton = document.getElementById('gameIntroTocBtn');
		tocButton.onclick = (e) => {
			e.stopPropagation();
			tocContainer.style.display = 'flex';
		};

		// 显示遮罩，隐藏开始按钮组
		startButtonGroup.style.display = 'none';
		overlay.style.display = 'flex';

		const renderIntroText = (index) => {
			const textElement = document.getElementById('gameIntroText');
			textElement.innerHTML = gameIntroTexts[index];
			const introImages = textElement.querySelectorAll('.intro-img');
			introImages.forEach(img => {
				const baseWidth = parseInt(img.dataset.baseWidth) || 100;
				const scaledWidth = baseWidth * finalScale;
				img.style.width = `${scaledWidth}px`;
				img.style.float = 'left';
				img.style.margin = `0 ${15 * finalScale}px ${5 * finalScale}px 0`;
			});
		};

		renderIntroText(currentIndex);

		// 点击交互逻辑
		overlay.onclick = function (e) {
			const toc = document.getElementById('gameIntroToc');
			// 如果目录打开，点击遮罩只关闭目录
			if (toc && toc.style.display === 'flex') {
				toc.style.display = 'none';
				return;
			}
			const modal = document.getElementById('gameIntroModal');
			const modalRect = modal.getBoundingClientRect();
			const clickX = e.clientX - modalRect.left;
			const modalWidth = modalRect.width;

			if (clickX < modalWidth / 3) {
				currentIndex = (currentIndex - 1 + gameIntroTexts.length) % gameIntroTexts.length;
				renderIntroText(currentIndex);
			} else if (clickX > modalWidth * 2 / 3) {
				currentIndex = (currentIndex + 1) % gameIntroTexts.length;
				renderIntroText(currentIndex);
			} else {
				overlay.style.display = 'none';
				startButtonGroup.style.display = 'block';
				overlay.onclick = null;
				return;
			}
		};
	};

	//关于怪物 —— 背景图+怪物图都按core.domStyle.scale/1.5缩放，尺寸结构完全不变
	this.aboutEnemys = function () {
		const enemyImages = [
			'project/images/aboutMonstersHover1.png',
			'project/images/aboutMonstersHover2.png',
			'project/images/aboutMonstersHover3.png',
		];
		let currentIndex = 0;
		const startPanel = document.getElementById('startPanel');
		const startButtonGroup = document.getElementById('startButtonGroup');

		const scaleRatio = core?.domStyle?.scale ? (core.domStyle.scale) : 1;
		const finalScale = Math.max(scaleRatio, 0.1);

		let imgViewer = document.getElementById('imgViewer');
		const bgImageStyle = `
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%) scale(${finalScale});
        transform-origin: center center;
        max-width: 100%;
        max-height: 100%;
        width: auto;
        height: auto;
        object-fit: contain;
        opacity: 1;
        z-index: 1;
        display: block;
        box-sizing: border-box;
        padding: 0;
    `;

		if (!imgViewer) {
			imgViewer = document.createElement('div');
			imgViewer.id = 'imgViewer';
			imgViewer.style.cssText = `
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.85);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 999;
            cursor: pointer;
            padding: 10px;
            box-sizing: border-box;
        `;

			const bgImage = document.createElement('img');
			bgImage.id = 'enemyViewerBg';
			bgImage.src = 'project/images/aboutMonsters.png';
			bgImage.style.cssText = bgImageStyle;
			imgViewer.appendChild(bgImage);

			const imgElement = document.createElement('img');
			imgElement.id = 'viewerImage';
			imgElement.style.cssText = `
            max-width: 100%;
            max-height: 100%;
            object-fit: contain;
            position: relative;
            z-index: 2;
            transform: scale(${finalScale});
            transform-origin: center center;
        `;
			imgViewer.appendChild(imgElement);
			startPanel.appendChild(imgViewer);
		} else {
			let bgImage = document.getElementById('enemyViewerBg');
			if (!bgImage) {
				bgImage = document.createElement('img');
				bgImage.id = 'enemyViewerBg';
				bgImage.src = 'project/images/aboutMonsters.png';
				bgImage.style.cssText = bgImageStyle;
				imgViewer.insertBefore(bgImage, imgViewer.firstChild);
			} else {
				bgImage.src = 'project/images/aboutMonsters.png';
				bgImage.style.display = 'block';
				bgImage.style.cssText = bgImageStyle;
			}
			const imgElement = document.getElementById('viewerImage');
			if (imgElement) {
				imgElement.style.zIndex = 2;
				imgElement.style.transform = `scale(${finalScale})`;
				imgElement.style.transformOrigin = 'center center';
			}
		}

		startButtonGroup.style.display = 'none';
		imgViewer.style.display = 'flex';
		document.getElementById('viewerImage').src = enemyImages[currentIndex];

		imgViewer.onclick = function (e) {
			const viewerRect = imgViewer.getBoundingClientRect();
			const clickX = e.clientX - viewerRect.left;
			const viewerWidth = viewerRect.width;

			if (clickX < viewerWidth / 3) {
				currentIndex = (currentIndex - 1 + enemyImages.length) % enemyImages.length;
				document.getElementById('viewerImage').src = enemyImages[currentIndex];
			} else if (clickX > viewerWidth * 2 / 3) {
				currentIndex = (currentIndex + 1) % enemyImages.length;
				document.getElementById('viewerImage').src = enemyImages[currentIndex];
			} else {
				imgViewer.style.display = 'none';
				startButtonGroup.style.display = 'block';
				imgViewer.onclick = null;
			}
		};
	};

	// 游戏进行中显示游戏说明（挂载到gameGroup画布）
	this.gameIntroduceInGame = function () {
		// 游戏说明文字列表（与上方一致）
		const gameIntroTexts = [
			`《新新魔塔》被称为“三原塔”之一，由原作者cos105hk于2005年发布，主要版本包含V1.1、V1.33、V1.4等。
本作是由三原塔翻新小组在前人基础上翻新的H5复刻版，复刻对应原作版本为最经典的FlashV1.1，与原作主要区别如下：

1、开局可选择“经典模式”与“极速模式”。
【经典模式】还原原作的战斗模式。显示完整的战斗流程，战斗伤害为非固定值（随机触发暴击或闪避）。主角可以从战斗中撤退（怪物体力重置，主角体力不重置，但也无法通过撤退令主角血量超越战前）。
【极速模式】 H5自创的战斗模式。预先计算战斗伤害并瞬间完成战斗，战斗伤害为固定值（取数学期望）。主角不能从战斗中撤退。

2、修复原作中的恶性bug，增加一键清零伤怪、一键拾取、一键魔塔语、交易统计等功能（可在游戏中“设置”按钮下的第二个子按钮“便捷功能”里打开）。

3、可使用H5样板的便捷功能。`,
			`体力（HP）
即“生命值”或“血量”，指角色被消灭前的最大伤害承受力。
可通过拾取血瓶/圣水、提升等级、商店购买恢复体力；体力没有上限。
攻击力（ATK）
角色或怪物造成伤害的能力。每点攻击力可以提高每次攻击的1点伤害。
可通过拾取红宝石/剑、提升等级、商店购买增加攻击力。
防御力（DEF）
角色或怪物抵御伤害的能力。每点防御力可以降低每次受击的1点伤害。
可通过拾取蓝宝石/盾、提升等级、商店购买增加防御力。

暴击值（CRI）
角色或怪物每次攻击有概率“暴击”，暴击时伤害结果将变为2倍。

敏捷值（AGI）
角色或怪物每次受击有概率“闪避”，闪避时伤害结果将变为0。`,
			`等级（LV）
等级对游戏数据没有直接影响，只作为解锁高等级“章”的增幅器条件。
20级可解锁二级章的增幅器，35级可解锁三级章的增幅器。
等级可通过拾取跃进之翼、经验商店购买提升。
经验值（EXP）
经验值通过杀怪获得，可在“战斗之神”（经验商店）处提升等级或购买攻防。
地上7F经验商店：70经验升1级+250血3攻3防；20经验换1攻或2防。
地下17F经验商店：190经验升3级+600血10攻10防；50经验换3攻或5防。

金币（GOLD）
金币通过杀怪或拾取获得，可在“贪婪之神”（金币商店）处购买血量或攻防。
地上3F金币商店：初始价格20金换500血或3攻或3防，每买一次涨价1金。
地下15F金币商店：初始价格50金换800血或6攻或6防，每买一次涨价2金。

钥匙
魔塔中共有黄、蓝、红三种颜色的钥匙，可用于打开对应颜色的门。
2F盗贼（开通地下后移动到0F）：分别以20/80金币出售黄钥匙/蓝钥匙。
15F商人：分别以10/40/500金币收购黄钥匙/蓝钥匙/红钥匙。`,
			`攻击临界
新新魔塔独创设定。当主角的攻击力小于怪物的防御力时：
若主角的攻击力与攻击临界之和大于等于怪物的防御力，则主角每次攻击可以造成1点伤害（暴击时为2点）；否则无法造成伤害。
初始攻击临界为0，铁剑/长剑/宝石剑/灵光剑/勇者之剑分别增加10/20/30/40/50点攻击临界。

防御临界
新新魔塔独创设定。当主角的防御力大于怪物的攻击力时：
若怪物的攻击力与主角的防御临界之和大于等于主角的防御力，则怪物每次攻击可以造成1点伤害（暴击时为2点）；否则无法造成伤害。
初始防御临界为30，皮革之盾/铁盾/光之盾/圣光十字盾/勇者之盾均减少5点防御临界。
（可以理解为“全体怪物共用的攻击临界”，此数值越低越好）`,
			`负面状态
主角与部分怪物交战时，有可能会陷入中毒或衰弱状态。
中毒状态可以免疫衰弱，衰弱状态可以免疫中毒。
若主角处于中毒状态，则每走一步将失去5点体力。
若主角处于衰弱状态，则攻击、防御、敏捷均下降为原来的50%（四舍五入）。
拾取地图上的解毒剂/火酒可以解除中毒/衰弱状态。
衰弱状态中若拾取宝石、商店购买攻防，新增属性不会乘以50%，并在解衰后全额保留（先解衰再加属性，还是先加属性再解衰，最终结果完全一致）。

负面状态的判定条件
【经典模式】勇士每次受到毒蝙蝠/毒丧尸/毒龙攻击（注意是每次受击，不是每场战斗），均有12%/14%/20%概率进入中毒状态。勇士每一次受到巨大八爪鱼/高级骷髅兵/大魔王攻击，均有7%/13%/10%概率进入“衰弱中”状态（战斗胜利后将由“衰弱中”状态转为“衰弱”状态）。
【极速模式】和上述六种怪物战斗，则100%会中毒/衰弱；但是中毒状态免疫衰弱，衰弱状态免疫中毒。`,
			`特殊物品说明

随意门：当勇者面对某些特定的墙或熔岩时，可使面前一格地形变为黄门，使用成功后，随意门-1。先从地下7F左下老人处免费获得5个，之后可找地下14F左下老人补充，售价为150金币/个。

神剑之证：当勇者面对怪物时，可使面前一格怪物变为绿色史莱姆，使用成功后，神剑之证-1。（对魔王格勒第、大魔王、毒龙、暗黑魔法师、黄金史莱姆、白银史莱姆无效）。在地下10F的红门内获得5个，无法补充数量。

锄头：交给主塔2F盗贼，开启通往地下的通道。战胜主塔20F假公主获得。

宝石锄头：共有4把，用来打开地下22层的4道花门。

冰之护符：持有后可使勇者不受熔岩的伤害。在地下15F的3道红门内获得。

魔法护符：攻击方式为魔法攻击的怪物的防御力下降(勇者的防御力/3)，四舍五入，且最低下降为0，但对暗黑魔法师无效。在地下16F左下商人处购买，售价为700金币。

十字架：一旦勇者的体力0时，则体力+2000。在地下13F穿越多个岩浆后获得。
`,
			`章
“章”是一种可以用于赋予主角新外貌与特殊技能的宝物，在魔塔（隐藏层）5层可获得，在主塔14层老人处鉴定后生效，主角到达20级/35级后可对“章”进行二级/三级增幅器的强化。

<table class="chapter-table" style="width:100%;border-collapse:collapse;margin:0;padding:0;">
    <tr style="height:120px;vertical-align:top;">
        <td style="width:100px;padding:0 15px 0 0;text-align:center;vertical-align:top;">
            <img src="project/images/yongzhe.png" class="chapter-img" style="width:100px;height:100px;display:block;margin:0 auto;">
        </td>
        <td style="padding:0;vertical-align:top;">
            <strong style="color:#FFD700;">勇者之证（镶有红宝石的章）</strong><br>
            被鉴定时，主角将晋升为勇者，铠甲变为红色。勇者能充分发挥攻击力的优势，迅猛出招，造成令人生畏的伤害，快速解决战斗。
        </td>
    </tr>
    <tr style="height:120px;vertical-align:top;">
        <td style="width:100px;padding:0 15px 0 0;text-align:center;vertical-align:top;">
            <img src="project/images/bazhe.png" class="chapter-img" style="width:100px;height:100px;display:block;margin:0 auto;">
        </td>
        <td style="padding:0;vertical-align:top;">
            <strong style="color:#FFD700;">霸者之证（镶有黑珍珠的章）</strong><br>
            被鉴定时，主角被晋升为霸者，铠甲变为黑色。霸者在受到攻击时，有概率反弹受到的伤害，或在战斗中提升自己的防御力。
        </td>
    </tr>
    <tr style="height:120px;vertical-align:top;">
        <td style="width:100px;padding:0 15px 0 0;text-align:center;vertical-align:top;">
            <img src="project/images/xianzhe.png" class="chapter-img" style="width:100px;height:100px;display:block;margin:0 auto;">
        </td>
        <td style="padding:0;vertical-align:top;">
            <strong style="color:#FFD700;">贤者之证（镶有钻石的章）</strong><br>
            被鉴定时，主角被晋升为贤者，铠甲变为黄色。贤者在闪避敌人攻击时，有概率将攻击吸收并转化为自己的体力。
        </td>
    </tr>
</table>`,
			`勇者之证效果：
【一阶勇者】暴击率+5%。
【二阶勇者】攻击次数+1。（勇士每回合可以进行2次独立的攻击）
【三阶勇者】当主角的一次攻击击中怪物且能造成伤害，并且主角防御力小于怪物攻击力时，有16%的几率主角对怪物造成附加伤害（暴击判定前），数值为主角的防御力除以4。 （对铁怪无效）
注：低阶勇者的效果在增幅器强化后依然有效。

增幅方法：
【一阶升二阶】主角等级20级时，花费500金币，从主塔0层右上角奸商处获得。
【二阶升三阶】主角等级35级时，地下14层左下角红门内的商人处免费获得。`,
			`霸者之证效果：
【一阶霸者】攻击力+15，防御力+15。一阶反弹：当非魔法师系怪物的一次攻击击中主角时，主角有6%的几率反弹攻击，如果怪物对主角的伤害/2≧怪物体力，那么怪物直接死亡，否则主角承受伤害=怪物对主角的伤害，怪物承受伤害=怪物对主角的伤害/2。（攻击反弹时，怪物不会暴击。当怪物击中主角时，先进行是否反弹的判定，如果判定为不反弹，接着才会进入暴击的判定。）
【二阶霸者】二阶反弹：当非魔法师系怪物的一次攻击击中主角时，主角有11%的几率反弹攻击，如果怪物对主角的伤害≧怪物体力，那么怪物直接死亡，否则主角承受伤害=怪物对主角的伤害/2，怪物承受伤害=怪物对主角的伤害。二阶霸体：当非魔法师系怪物的一次攻击击中主角但没有触发反弹功效时，主角有11%的几率本次攻击伤害计算中防御力*2。
【三阶霸者】三阶反弹：当非魔法师系怪物的一次攻击击中主角时，主角有16%的几率反弹攻击，如果怪物攻击力/2≧怪物体力，那么怪物直接死亡，否则主角承受伤害=怪物对主角的伤害/3，怪物承受伤害=怪物攻击力/3。（三阶反弹目标为毒龍、大魔王、魔王格勒第时，双方均不受伤）三阶霸体：当非魔法师系怪物的一次攻击击中主角但没有触发反弹功效时，主角有16%的几率本次攻击伤害计算中防御力*2。破防：当主角的一次攻击击中怪物时，怪物有11%的几率本次攻击伤害计算中防御力-怪物防御力/1.3。（破防技能对魔王格勒第无效）

增幅方法：
【一阶升二阶】主角等级20级时，主塔1层左上角的老人处免费获得。
【二阶升三阶】主角等级35级时，花费100经验值，从主塔19层上方老人获得。`,
			`贤者之证效果：
【一阶贤者】闪避率+5%。一阶回血：当怪物的一次攻击没打中主角时，主角有61%的几率回复体力，如果主角当前体力小于300，那么回复体力=怪物攻击力/5+100，否则回复体力=怪物攻击力/5。
【二阶贤者】二阶回血：当怪物的一次攻击没打中主角时，主角有81%的几率回复体力，如果主角当前体力小于300，那么回复体力=怪物攻击力/5+200，否则回复体力=怪物攻击力/5。二阶集能：当非魔法师系怪物的一次攻击击中主角且能造成伤害时，主角有11%的几率收集一次能量，同时减伤50点（怪物暴击判定前），每次收集的能量会使主角在战斗胜利后随机回复0到29点体力。
【三阶贤者】三阶回血：当怪物的一次攻击没打中主角时，如果主角当前体力小于300，那么回复体力=怪物攻击力/5+300，否则回复体力=怪物攻击力/5。三阶集能：当怪物的一次攻击击中主角时，如果该怪物不是魔法师系怪物且能对主角造成伤害，那么主角有31%的几率收集一次能量，同时减伤50点（怪物暴击判定前），每次收集的能量会使主角在最近的一次战斗胜利后随机回复0到29点体力；如果该怪物是魔法师系怪物，那么主角有21%的几率收集一次能量，同时减伤100点（怪物暴击判定前），每次收集的能量会使主角在战斗胜利后随机回复体力为：怪物攻击力到怪物攻击力+19。
（所有减伤均可减为负数）

增幅方法：
【一阶升二阶】主角等级20级时，地下7层右下角的商人处免费获得。
【二阶升三阶】主角等级35级时，主塔15层右上角的商人处免费获得。`,
			`秒杀
当怪物不是魔法师，怪物的攻击击中了勇士，没有触发霸者之证的反弹或霸体效果，且怪物的攻击力与勇士的防御临界之和小于勇士的防御（即怪物不能对勇士造成伤害），且勇士攻击力大于怪物防御力时，有16%概率触发秒杀效果，立刻终止战斗，怪物直接被杀死。

注意事项（关于极速模式）：
在极速模式下，对于无章、勇者之证以及霸者之证来说，只有怪物的期望伤害是0的时候才能触发秒杀，因此有没有秒杀效果是没有区别的。
但对于贤者之证来说，触发秒杀杀死怪物会导致战斗回合数减少，从而导致贤者之证的回血降低，因此：
从"未防杀状态"加防变为"完美防杀状态"时，可能会让极速模式下的贤者之证的吸血量降低。`,
		];

		const gameIntroTitles = [
			"本作简介",
			"基础属性",
			"等级/经验/金币/钥匙",
			"攻击临界与防御临界",
			"负面状态",
			"特殊物品说明",
			"章（勇者/霸者/贤者）",
			"勇者之证效果",
			"霸者之证效果",
			"贤者之证效果",
			"秒杀机制"
		];

		let currentIndex = 0;

		const gameGroupPanel = document.getElementById('gameGroup');
		if (!gameGroupPanel) {
			alert('未找到gameGroup容器，请检查容器ID是否正确！');
			return;
		}
		gameGroupPanel.style.position = 'relative';

		const scaleRatio = core?.domStyle?.scale ? (core.domStyle.scale / 1.5) : 1;
		const finalScale = Math.max(scaleRatio, 0.1);

		const overlayStyle = `
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.9);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 999;
        cursor: pointer;
        box-sizing: border-box;
    `;

		const modalStyle = `
        position: relative;
        width: 90%;
        height: 90%;
        max-width: 1200px;
        max-height: 800px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        box-sizing: border-box;
    `;

		const bgImageStyle = `
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
        z-index: 1;
        opacity: 0.8;
        box-sizing: border-box;
    `;

		const baseFontSize = 16;
		const scaledFontSize = baseFontSize * finalScale;
		const textContainerStyle = `
        position: relative;
        z-index: 2;
        width: 95%;
        height: 85%;
        font-family: 'ubuntu', sans-serif;
        font-size: ${scaledFontSize}px;
        color: #fff;
        font-weight: bold;
        text-align: left;
        line-height: 1.6;
        padding: 15px;
        box-sizing: border-box;
        white-space: pre-line;
        overflow-y: auto;
        text-shadow: 
            -2px -2px 0 #000,
             2px -2px 0 #000,
            -2px  2px 0 #000,
             2px  2px 0 #000;
    `;

		const baseTipFontSize = 14;
		const scaledTipFontSize = baseTipFontSize * finalScale;
		const tipTextStyle = `
        position: relative;
        z-index: 3;
        width: 95%;
        padding: 8px 15px;
        margin-top: 8px;
        font-family: 'ubuntu', sans-serif;
        font-size: ${scaledTipFontSize}px;
        color: #ffd700;
        font-weight: bold;
        text-align: center;
        text-shadow: 
            -1px -1px 0 #000,
             1px -1px 0 #000,
            -1px  1px 0 #000,
             1px  1px 0 #000;
        box-sizing: border-box;
    `;

		const tocContainerStyle = `
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 10;
        background: rgba(0, 0, 0, 0.9);
        display: none;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        overflow-y: auto;
        padding: 20px;
        box-sizing: border-box;
    `;

		// 创建/复用遮罩层
		let overlay = document.getElementById('gameIntroInGameOverlay');
		if (!overlay) {
			overlay = document.createElement('div');
			overlay.id = 'gameIntroInGameOverlay';
			overlay.style.cssText = overlayStyle;

			const modal = document.createElement('div');
			modal.id = 'gameIntroInGameModal';
			modal.style.cssText = modalStyle;

			const bgImage = document.createElement('img');
			bgImage.id = 'gameIntroInGameBg';
			bgImage.src = 'project/images/startBackground.png';
			bgImage.style.cssText = bgImageStyle;
			modal.appendChild(bgImage);

			const textElement = document.createElement('div');
			textElement.id = 'gameIntroInGameText';
			textElement.style.cssText = textContainerStyle;
			modal.appendChild(textElement);

			const tipWrapper = document.createElement('div');
			tipWrapper.id = 'gameIntroInGameTip';
			tipWrapper.style.cssText = tipTextStyle;
			const tipLabel = document.createElement('span');
			tipLabel.textContent = '-点击左侧/右侧翻页 | 点击中间关闭说明-';
			const tocButton = document.createElement('span');
			tocButton.id = 'gameIntroInGameTocBtn';
			tocButton.textContent = '[目录]';
			tocButton.style.cssText = `
            color: #ffd700;
            cursor: pointer;
            margin-left: 15px;
            font-weight: bold;
            text-decoration: underline;
        `;
			tipWrapper.appendChild(tipLabel);
			tipWrapper.appendChild(tocButton);
			modal.appendChild(tipWrapper);

			const tocContainer = document.createElement('div');
			tocContainer.id = 'gameIntroInGameToc';
			tocContainer.style.cssText = tocContainerStyle;
			modal.appendChild(tocContainer);

			overlay.appendChild(modal);
			gameGroupPanel.appendChild(overlay);
		} else {
			overlay.style.cssText = overlayStyle;
			const modal = document.getElementById('gameIntroInGameModal');
			modal.style.cssText = modalStyle;
			const bgImage = document.getElementById('gameIntroInGameBg');
			bgImage.style.cssText = bgImageStyle;
			const textElement = document.getElementById('gameIntroInGameText');
			textElement.style.cssText = textContainerStyle;
			const tipWrapper = document.getElementById('gameIntroInGameTip');
			tipWrapper.style.cssText = tipTextStyle;
			if (!document.getElementById('gameIntroInGameTocBtn')) {
				const tocButton = document.createElement('span');
				tocButton.id = 'gameIntroInGameTocBtn';
				tocButton.textContent = '[目录]';
				tocButton.style.cssText = `
                color: #ffd700;
                cursor: pointer;
                margin-left: 15px;
                font-weight: bold;
                text-decoration: underline;
            `;
				tipWrapper.appendChild(tocButton);
			}
		}

		// 动态生成目录
		const tocContainer = document.getElementById('gameIntroInGameToc');
		tocContainer.innerHTML = '';
		gameIntroTitles.forEach((title, idx) => {
			const btn = document.createElement('div');
			btn.textContent = `${idx + 1}. ${title}`;
			btn.style.cssText = `
            font-family: 'ubuntu', sans-serif;
            font-size: ${scaledFontSize}px;
            color: #fff;
            font-weight: bold;
            margin: ${6 * finalScale}px 0;
            cursor: pointer;
            text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
            transition: color 0.2s;
        `;
			btn.addEventListener('mouseenter', () => btn.style.color = '#ffd700');
			btn.addEventListener('mouseleave', () => btn.style.color = '#fff');
			btn.onclick = (e) => {
				e.stopPropagation();
				currentIndex = idx;
				renderIntroText(currentIndex);
				tocContainer.style.display = 'none';
			};
			tocContainer.appendChild(btn);
		});

		// 目录按钮事件
		const tocButton = document.getElementById('gameIntroInGameTocBtn');
		tocButton.onclick = (e) => {
			e.stopPropagation();
			tocContainer.style.display = 'flex';
		};

		// 显示遮罩层
		overlay.style.display = 'flex';

		const renderIntroText = (index) => {
			const textElement = document.getElementById('gameIntroInGameText');
			textElement.innerHTML = gameIntroTexts[index];

			if (index !== 6) {
				const introImages = textElement.querySelectorAll('.intro-img');
				introImages.forEach(img => {
					const baseWidth = parseInt(img.dataset.baseWidth) || 100;
					const scaledWidth = baseWidth * finalScale;
					img.style.width = `${scaledWidth}px`;
					img.style.float = 'left';
					img.style.margin = `0 ${10 * finalScale}px ${5 * finalScale}px 0`;
					img.style.border = `2px solid #ffd700`;
				});
			} else {
				const chapterImgs = textElement.querySelectorAll('.chapter-img');
				chapterImgs.forEach(img => {
					const baseSize = 100;
					const scaledSize = baseSize * finalScale;
					img.style.width = `${scaledSize}px`;
					img.style.height = `${scaledSize}px`;
					img.style.display = 'block';
					img.style.margin = '0 auto';
					img.style.float = 'none';
					img.style.border = `2px solid #ffd700`;
				});
				const chapterTable = textElement.querySelector('.chapter-table');
				if (chapterTable) {
					const scaledIconWidth = 100 * finalScale;
					const scaledRowHeight = 120 * finalScale;
					const scaledPadding = 15 * finalScale;
					chapterTable.style.width = '100%';
					chapterTable.style.borderCollapse = 'collapse';
					chapterTable.style.margin = '0';
					chapterTable.style.padding = '0';
					chapterTable.style.whiteSpace = 'normal';
					const rows = chapterTable.querySelectorAll('tr');
					rows.forEach(row => {
						row.style.height = `${scaledRowHeight}px`;
						row.style.verticalAlign = 'top';
					});
					const iconCells = chapterTable.querySelectorAll('td:first-child');
					iconCells.forEach(cell => {
						cell.style.width = `${scaledIconWidth}px`;
						cell.style.padding = `0 ${scaledPadding}px 0 0`;
						cell.style.textAlign = 'center';
						cell.style.verticalAlign = 'top';
					});
					const textCells = chapterTable.querySelectorAll('td:last-child');
					textCells.forEach(cell => {
						cell.style.padding = '0';
						cell.style.verticalAlign = 'top';
						cell.style.lineHeight = '1.6';
					});
				}
			}
		};

		renderIntroText(currentIndex);

		overlay.onclick = function (e) {
			const toc = document.getElementById('gameIntroInGameToc');
			if (toc && toc.style.display === 'flex') {
				toc.style.display = 'none';
				return;
			}
			const modal = document.getElementById('gameIntroInGameModal');
			const modalRect = modal.getBoundingClientRect();
			const clickX = e.clientX - modalRect.left;
			const modalWidth = modalRect.width;

			if (clickX < modalWidth / 3) {
				currentIndex = (currentIndex - 1 + gameIntroTexts.length) % gameIntroTexts.length;
				renderIntroText(currentIndex);
			} else if (clickX > modalWidth * 2 / 3) {
				currentIndex = (currentIndex + 1) % gameIntroTexts.length;
				renderIntroText(currentIndex);
			} else {
				overlay.style.display = 'none';
				overlay.onclick = null;
			}
		};
	};
},
    "楼梯穿行": function () {
	// 在此增加新插件
	this.canIgnoreChangeFloor = function (block) {
		// 原有逻辑：如果全局或数据中明确标记忽略切换楼层，直接允许穿行
		var able = core.flags.ignoreChangeFloor;
		if (block.event.data && block.event.data.ignoreChangeFloor != null) {
			able = block.event.data.ignoreChangeFloor;
		}
		if (able) return true;

		// 中毒状态下不允许穿行
		if (core.hasFlag('poison')) return false;

		// 获取目标楼层与坐标
		var data = block.event.data;
		if (!data || !data.floorId || !core.status.maps[data.floorId]) return false;

		var heroLoc = core.events._changeFloor_getHeroLoc(
			data.floorId,
			data.stair,
			data.loc ? { x: data.loc[0], y: data.loc[1], direction: data.direction } : null
		);
		var tx = heroLoc.x,
			ty = heroLoc.y;

		// 1. 检查目标传送点四周是否有空地（确保不会被卡住）
		var dirs = [
			[0, -1],
			[0, 1],
			[-1, 0],
			[1, 0]
		];
		var hasEmpty = false;
		for (var i = 0; i < dirs.length; i++) {
			var nx = tx + dirs[i][0];
			var ny = ty + dirs[i][1];
			if (nx < 0 || ny < 0 || nx >= core.bigmap.width || ny >= core.bigmap.height) continue;
			// 空点定义为不存在任何块（null）
			if (core.getBlock(nx, ny, data.floorId, true) === null) {
				hasEmpty = true;
				break;
			}
		}
		if (!hasEmpty) return false;

		// 2. 检查目标点本身的楼梯事件是否指向当前楼梯（双向）
		var targetStairBlock = core.getBlock(tx, ty, data.floorId, true);
		if (!targetStairBlock || !targetStairBlock.event || targetStairBlock.event.trigger !== 'changeFloor') {
			return false;
		}
		var targetData = targetStairBlock.event.data;
		if (!targetData || !targetData.floorId) return false;
		// 目标楼梯必须传回当前楼层
		if (targetData.floorId !== core.status.floorId) return false;
		// 计算目标楼梯传回的位置
		var backLoc = core.events._changeFloor_getHeroLoc(
			targetData.floorId,
			targetData.stair,
			targetData.loc ? { x: targetData.loc[0], y: targetData.loc[1], direction: targetData.direction } : null
		);
		// 传回位置必须恰好是当前楼梯的坐标
		if (backLoc.x !== block.x || backLoc.y !== block.y) return false;

		// 所有条件满足，允许穿行
		return true;
	};
	events.prototype._trigger_ignoreChangeFloor = function (block) {
		if (core.canIgnoreChangeFloor(block)) {
			if (core.isReplaying()) {
				if (core.status.replay.toReplay[0] == 'no') {
					core.status.replay.toReplay.shift();
					core.status.route.push("no");
					return true;
				}
			} else if (core.status.automaticRoute.autoHeroMove ||
				core.status.automaticRoute.autoStep < core.status.automaticRoute.autoStepRoutes.length) {
				core.status.route.push("no");
				return true;
			}
		}
		return false;
	};

	function doorCheck(x, y) {
		let block = core.getBlock(x, y);
		if (!block?.event) return false;

		const id = block.event.id;
		const isDoorLike = core.material.icons.animates[id] || core.material.icons.npc48[id];

		if (!isDoorLike) return false;

		const doorInfo = block.event.doorInfo;
		if (!doorInfo) return false;

		const keyInfo = doorInfo.keys || {};

		for (let rawName in keyInfo) {
			const needCount = keyInfo[rawName];
			const pureName = rawName.endsWith(':o') ? rawName.slice(0, -2) : rawName;

			const item = core.material.items[pureName];

			// — 道具不存在 —
			if (!item) {
				return false;
			}

			// — 数量不足 —
			if (core.itemCount(pureName) < needCount) {
				return false;
			}
		}
		return true;
	}
	maps.prototype._canMoveDirectly_checkNextPoint = function (blocksObj, x, y) {
		var index = x + "," + y;
		var block = blocksObj[index];
		// 该点是否不可通行或有脚本
		if (block && !block.disable && (block.event.noPass || block.event.script || block.event.event))
			return false;
		// 该点是否是绿点可触发
		if (block && !block.disable && block.event.trigger) {
			if (block.event.trigger != 'changeFloor') return false;
			if (!core.canIgnoreChangeFloor(block)) return false;
		}
		// 是否存在阻激夹域伤害
		if (core.status.checkBlock.damage[index]) return false;
		if (core.status.checkBlock.repulse[index]) return false;
		// 是否存在捕捉
		if (core.status.checkBlock.ambush[index]) return false;

		return true;
	};
	maps.prototype._automaticRoute_bfs = function (startX, startY, destX, destY) {
		var route = {},
			canMoveArray = this.generateMovableArray();
		// 使用优先队列
		var queue = new PriorityQueue({ comparator: function (a, b) { return a.depth - b.depth; } });
		route[startX + "," + startY] = '';
		queue.queue({ depth: 0, x: startX, y: startY });
		var blocks = core.getMapBlocksObj();
		while (queue.length != 0) {
			var curr = queue.dequeue(),
				deep = curr.depth,
				nowX = curr.x,
				nowY = curr.y;
			for (var direction in core.utils.scan) {
				if (!core.inArray(canMoveArray[nowX][nowY], direction)) continue;
				var nx = nowX + core.utils.scan[direction].x;
				var ny = nowY + core.utils.scan[direction].y;
				if (nx < 0 || nx >= core.bigmap.width || ny < 0 || ny >= core.bigmap.height || route[nx + "," + ny] != null) continue;
				// 重点
				if (nx == destX && ny == destY) {
					route[nx + "," + ny] = direction;
					break;
				}
				// 不可通行
				let block = core.getBlock(nx, ny);
				if (core.noPass(nx, ny) && block?.event?.cls !== 'items' && block?.event?.cls !== 'enemys' && !([16, 18, 19, 66, 68].includes(block?.id))) continue;
				if (block?.event?.cls === 'enemys' && !core.canBattle(block.event.id, block.x, block.y)) continue;
				if ([16, 18, 19, 66, 68].includes(block?.id) && !doorCheck(block.x, block.y)) continue;
				if (block && block.event.trigger === 'changeFloor') {
					if (!core.canIgnoreChangeFloor(block)) continue;
				}
				route[nx + "," + ny] = direction;
				queue.queue({ depth: deep + this._automaticRoute_deepAdd(nx, ny, blocks), x: nx, y: ny });
			}
			if (route[destX + "," + destY] != null) break;
		}
		return route;
	};
	maps.prototype._automaticRoute_deepAdd = function (x, y, blocks) {
		// 判定每个可通行点的损耗值，越高越应该绕路
		var deepAdd = 1;
		var block = blocks[x + "," + y];
		if (block && !block.disable) {

			var id = block.event.id;
			// 绕过亮灯
			if (id == "light") deepAdd += 100;
			// 绕过路障
			if (id.endsWith("Net") && !core.hasFlag(id.substring(0, id.length - 3))) deepAdd += 100;
			// 绕过血瓶和绿宝石
			if (block.event.cls === 'items' && (id.endsWith("Potion"))) deepAdd += 20;
			// 绕过传送点
			// if (block.event.trigger == 'changeFloor') deepAdd+=10;
			if (block.event.cls === 'enemys') deepAdd += core.getDamage(block.event.id, x, y) * 100 + 100;
			else if ([16, 18, 19, 66].includes(block?.id)) deepAdd += Math.pow(block?.id, 2) * 100;
			else if (block?.id === 68) deepAdd += 0;
		}


		// 绕过存在伤害的地方
		deepAdd += (core.status.checkBlock.damage[x + "," + y] || 0) * 100;
		// 绕过捕捉
		if (core.status.checkBlock.ambush[x + "," + y]) deepAdd += 1000;
		return deepAdd;
	}
}
}