var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = 
{
    "init": function () {

	console.log("插件编写测试");

	this._afterLoadResources = function () {
		// 本函数将在所有资源加载完毕后，游戏开启前被执行
		// 可以在这个函数里面对资源进行一些操作。
		// 若需要进行切分图片，可以使用 core.splitImage() 函数，或直接在全塔属性-图片切分中操作
		core.ui.statusBar.init();
	}

	this.splitArray = function (arr, n = 5) {
		if (arr.length <= n) {
			core.push(arr, {
				"text": "取消",
				"action": [{ "type": "exit" }]
			});
			return arr; // 如果数组长度小于等于n，直接返回
		}

		// 取出前n个元素，保留它们
		let head = arr.slice(0, n);
		// 剩下的元素递归调用 splitArray，生成对象
		core.push(head, {
			"text": "取消",
			"action": [{ "type": "exit" }]
		});
		let tail = {
			"text": "下一页",
			"action": [{
				type: "choices",
				text: "选择要到达的点",
				choices: core.plugin.splitArray(arr.slice(n), n) // 递归拆分剩余部分
			}]
		};

		// 返回前n个元素和包含剩余部分的对象
		return [...head, tail];
	}

	// 可以在任何地方（如afterXXX或自定义脚本事件）调用函数，方法为 core.plugin.xxx();
	// 从V2.6开始，插件中用this.XXX方式定义的函数也会被转发到core中，详见文档-脚本-函数的转发。
},
    "resizeTo11": function () {
	if (main.mode == 'editor') {
		return;
	}

	// 在不修改libs的情况下将页面适配为11x11
	core.__SIZE__ = 11;
	core.__PIXELS__ = core.__SIZE__ * 32;
	core.__HALF_SIZE__ = 5;
	// core.bigmap.width = core.__SIZE__;
	// core.bigmap.height = core.__SIZE__;
	core.actions.SIZE = core.__SIZE__;
	core.actions.HSIZE = core.__HALF_SIZE__;
	core.actions.LAST = core.__SIZE__ - 1;
	core.actions.CHOICES_LEFT = 3;
	core.actions.CHOICES_RIGHT = core.actions.LAST - core.actions.CHOICES_LEFT;
	core.ui.SIZE = core.__SIZE__;
	core.ui.HSIZE = core.__HALF_SIZE__;
	core.ui.LAST = core.__SIZE__ - 1;
	core.ui.PIXEL = core.__PIXELS__;
	core.ui.HPIXEL = core.__PIXELS__ / 2;
},
    "statusBar": function () {
	main.dom.floorMsgGroup.style.display = 'none';
	main.dom.statusBar.style.display = 'none';
	main.dom.toolBar.style.display = 'none';

	const GAMEVIEW_WIDTH = 640;
	const GAMEVIEW_HEIGHT = 422;

	const GAMEVIEW_WIDTH_VERTICAL = 388;
	const GAMEVIEW_HEIGHT_VERTICAL = 630;

	const BAR_WIDTH = 125;
	const BAR_HEIGHT_VERTICAL = 130;
	const BORDER_WIDTH = 18;
	const BORDER_HEIGHT = 24;

	const ITEM_BOX_LEFT = 13;
	const ITEM_BOX_TOP = 190;
	const ITEM_BOX_LEFT_VERTICAL = 8;
	const ITEM_BOX_TOP_VERTICAL = 538;

	const ITEM_ICON_OUTER_SIZE = 33;

	const EQUIP_BLOCK_LEFT = 525;
	const EQUIP_BLOCK_TOP = 40;
	const EQUIP_BLOCK_LEFT_VERTICAL = 108;
	const EQUIP_BLOCK_TOP_VERTICAL = 0;

	const KEY_BLOCK_LEFT = EQUIP_BLOCK_LEFT;
	const KEY_BLOCK_LEFT_VERTICAL = EQUIP_BLOCK_LEFT_VERTICAL;
	const KEY_BLOCK_TOP_VERTICAL = 86;

	const INFO_BLOCK_LEFT = EQUIP_BLOCK_LEFT;
	const INFO_BLOCK_TOP = 220;
	const INFO_BLOCK_LEFT_VERTICAL = 219;
	const INFO_BLOCK_TOP_VERTICAL = 10;

	const TOOL_BOX_LEFT = EQUIP_BLOCK_LEFT;
	const TOOL_BOX_TOP = 318;
	const TOOL_BOX_LEFT_VERTICAL = 278;
	const TOOL_BOX_TOP_VERTICAL = ITEM_BOX_TOP_VERTICAL;

	const TOOL_ICON_OUTER_SIZE = 34;

	const INFO_BAR_HEIGHT = 22;
	const INFO_BAR_HEIGHT_VERTICAL = 18;
	const INFO_BAR_TOP = GAMEVIEW_HEIGHT - INFO_BAR_HEIGHT;
	const INFO_BAR_TOP_VERTICAL = GAMEVIEW_HEIGHT_VERTICAL - INFO_BAR_HEIGHT_VERTICAL;

	const TEXT_COLOR = "#E1E1E1";

	const FORCE_COUNTABLE_ITEMS = ['centerFly'];

	const outerBackground = document.createElement('canvas');
	outerBackground.style.position = 'absolute';
	outerBackground.style.zIndex = 5;
	outerBackground.id = 'outerBackground';
	main.dom.outerBackground = outerBackground;
	main.dom.startPanel.insertAdjacentElement('afterend', outerBackground);

	const outerUI = document.createElement('canvas');
	outerUI.style.position = 'absolute';
	outerUI.style.zIndex = 165;
	outerUI.id = 'outerUI';
	main.dom.outerUI = outerUI;
	outerBackground.insertAdjacentElement('afterend', outerUI);
	setTimeout(function () {
		// Should be executed immediately after init()
		main.canvas.outerUI = outerUI.getContext('2d');
	});
	outerUI.onclick = function (e) {
		try {
			e.preventDefault();
			if (!core.isPlaying()) return false;
			const left = core.dom.gameGroup.offsetLeft;
			const top = core.dom.gameGroup.offsetTop;
			const px = parseInt((e.clientX - left) / core.domStyle.scale),
				py = parseInt((e.clientY - top) / core.domStyle.scale);
			core.ui.statusBar.onclick(px, py);
		} catch (ee) {
			main.log(ee);
		}
	};

	const _resize_gameGroup = function (obj) {
		const gameGroup = core.dom.gameGroup;
		gameGroup.style.width = obj.totalWidth + 'px';
		gameGroup.style.height = obj.totalHeight + 'px';
		gameGroup.style.left =
			(obj.clientWidth - obj.totalWidth) / 2 + 'px';
		gameGroup.style.top =
			(obj.clientHeight - obj.totalHeight) / 2 + 'px';

		core.dom.musicBtn.style.right =
			(obj.clientWidth - obj.totalWidth) / 2 + 'px';
		core.dom.musicBtn.style.bottom =
			(obj.clientHeight - obj.totalHeight) / 2 - 27 + 'px';
		core.dom.enlargeBtn.style.right =
			(obj.clientWidth - obj.totalWidth) / 2 + 31 + 'px';
		core.dom.enlargeBtn.style.bottom =
			(obj.clientHeight - obj.totalHeight) / 2 - 27 + 'px';

		main.dom.startBackground.src = main.styles.startBackground;
	};

	const _resize_canvas = function (obj) {
		var innerSize = (obj.canvasWidth * core.domStyle.scale) + "px";
		if (!core.isPlaying()) {
			for (var i = 0; i < core.dom.gameCanvas.length; ++i) {
				var ctx = core.dom.gameCanvas[i].getContext('2d');
				core.resizeCanvas(ctx, core.__PIXELS__, core.__PIXELS__);
				core.dom.gameCanvas[i].style.width = innerSize;
				core.dom.gameCanvas[i].style.height = innerSize;
			}
		} else {
			requestAnimationFrame(function () {
				for (var i = 0; i < core.dom.gameCanvas.length; ++i) {
					if (!core.bigmap.canvas.includes(core.dom.gameCanvas[i].id)) {
						core.dom.gameCanvas[i].style.width = innerSize;
						core.dom.gameCanvas[i].style.height = innerSize;
					}
				}
			});
		}
		core.dom.gif.style.width = innerSize;
		core.dom.gif.style.height = innerSize;
		core.dom.gif2.style.width = innerSize;
		core.dom.gif2.style.height = innerSize;
		core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = innerSize;
		core.dom.gameDraw.style.top = obj.gameDrawBox.top * core.domStyle.scale + 'px';
		core.dom.gameDraw.style.left = obj.gameDrawBox.left * core.domStyle.scale + 'px';
		// resize bigmap

		core.bigmap.canvas.forEach(function (cn) {
			let v2 = core.bigmap.width * core.bigmap.height > core.bigmap.threshold;
			let width = v2 ? core.__PIXELS__ + 64 : core.bigmap.width * 32;
			let height = v2 ? core.__PIXELS__ + 64 : core.bigmap.height * 32;
			core.canvas[cn].canvas.style.width = width * core.domStyle.scale + "px";
			core.canvas[cn].canvas.style.height = height * core.domStyle.scale + "px";
		});
		// resize dynamic canvas
		if (!core.isPlaying()) {
			for (var name in core.dymCanvas) {
				var ctx = core.dymCanvas[name],
					canvas = ctx.canvas;
				// core.maps._setHDCanvasSize(ctx, parseFloat(canvas.getAttribute('_width')), parseFloat(canvas.getAttribute('_height')));
				canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px";
				canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px";
				var scale = canvas.getAttribute('_scale') || 1;
				core.resizeCanvas(canvas, canvas.width * scale / core.domStyle.scale, canvas.height * scale / core.domStyle.scale);
			}
		} else {
			for (var name in core.dymCanvas) {
				var ctx = core.dymCanvas[name],
					canvas = ctx.canvas;
				core.resizeCanvas(ctx, parseFloat(canvas.getAttribute("_width")), parseFloat(canvas.getAttribute("_height")))
				canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px";
				canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px";
			}
		}
		// resize next
		main.dom.next.style.width = main.dom.next.style.height = 5 * core.domStyle.scale + "px";
		main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px";
		if (main.dom.outerBackground) {
			main.dom.outerBackground.style.width = obj.totalWidth + 'px';
			main.dom.outerBackground.style.height = obj.totalHeight + 'px';
		}
		if (main.dom.outerUI) {
			main.dom.outerUI.style.width = obj.totalWidth + 'px';
			main.dom.outerUI.style.height = obj.totalHeight + 'px';
		}

	};


	core.control.resize = function () {
		if (main.mode == 'editor') return;

		const clientWidth = main.dom.body.clientWidth,
			clientHeight = main.dom.body.clientHeight;
		const canvasWidth = core.__PIXELS__;

		const isVertical = clientHeight > clientWidth;
		core.domStyle.isVertical = isVertical;

		const totalWidth = isVertical ?
			GAMEVIEW_WIDTH_VERTICAL :
			GAMEVIEW_WIDTH,
			totalHeight = isVertical ?
			GAMEVIEW_HEIGHT_VERTICAL :
			GAMEVIEW_HEIGHT;

		const maxRatio = Math.min(
			clientWidth / totalWidth,
			clientHeight / totalHeight
		);

		// ======= 在 core.control.resize 中，替换原来有关 availableScale / scale 的那一段 =======
		const PRESET_SCALES = [1, 1.25, 1.5, 1.75, 2];

		// 重新计算可用 scale 列表（仅用于 UI / setDisplayScale）
		core.domStyle.availableScale = PRESET_SCALES.filter(v => v <= maxRatio);


		// localStorage key（分别记录横屏/竖屏的用户偏好）
		const storageKey = isVertical ? 'scale_vertical' : 'scale_horizontal';

		// 尝试读取用户之前保存的值（按 orientation 区分）
		let chosenScale = null;
		const rawSaved = core.getLocalStorage(storageKey);
		if (rawSaved != null) {
			const s = parseFloat(rawSaved);
			if (!isNaN(s) && s <= maxRatio) {
				chosenScale = s;
			}
		}

		// 如果没有可用的 saved scale，则从 preset 里选出“最近似且不超出的”一个
		if (chosenScale == null) {
			if (maxRatio < PRESET_SCALES[0]) {
				// 屏幕太小，连 1 倍都放不下 -> 使用 maxRatio（保证不溢出）
				chosenScale = maxRatio;
			} else {
				// 候选为所有 <= maxRatio 的 preset，选与 maxRatio 差值最小的
				const candidates = PRESET_SCALES.filter(v => v <= maxRatio);
				let best = candidates[0];
				let bestDiff = Math.abs(maxRatio - best);
				for (let v of candidates) {
					const d = Math.abs(maxRatio - v);
					if (d < bestDiff) {
						best = v;
						bestDiff = d;
					}
				}
				chosenScale = best;
			}
		}

		// 最终赋值（注意不覆盖其他 orientation 的 localStorage）
		core.domStyle.scale = chosenScale;
		// ===================================================================


		const totalWidthScaled = totalWidth * core.domStyle.scale,
			totalHeightScaled = totalHeight * core.domStyle.scale;

		const gameDrawBox = isVertical ? {
			left: BORDER_WIDTH,
			top: BAR_HEIGHT_VERTICAL + BORDER_HEIGHT
		} : { left: BAR_WIDTH + BORDER_WIDTH, top: BORDER_HEIGHT };

		const obj = {
			clientWidth: clientWidth,
			clientHeight: clientHeight,
			canvasWidth: canvasWidth,
			totalWidth: totalWidthScaled,
			totalHeight: totalHeightScaled,
			gameDrawBox: gameDrawBox,
			globalAttribute: core.status.globalAttribute ||
				core.initStatus.globalAttribute
		};

		_resize_gameGroup(obj);
		_resize_canvas(obj);

		if (core.status.automaticRoute == null)
			core.status.automaticRoute = {};
		core.control.setViewport(32, 32);
		if (core.canvas.outerUI) {
			core.ui.statusBar._update_background();
			core.ui.statusBar._update_toolBox();
		}
		core.status.lastPropsDrawn = null;
		core.updateStatusBar();
	};

	class StatusBar {
		constructor() {
			this.infoText = undefined;
			this.infocnt = 0;
			this.itemMx = [
				["book", "coin", "fly"],
				["cross", "superPotion", "pickaxe"],
				["bomb", "centerFly", "upFly"],
				["downFly", "knife", "snow"],
				["bigKey", "earthquake", "wand"],
				["skill1"]
			];
			this.itemMxVertical = [
				["book", "coin", "fly", "cross", "superPotion", "pickaxe", "bomb", "centerFly"],
				["upFly", "downFly", "knife", "snow", "bigKey", "earthquake", "wand", "skill1"],
			];
		}
		init() {
			this.toolbarAction = [
				[
					main.core.openKeyBoard,
					function () {
						if (!core.status.lockControl)
							core.doSL("autoSave", "load")
					},
					function () {
						core.insertAction([
							{ type: 'insert', name: '游戏说明' }
						]);
					}
				],
				[main.core.save, main.core.load, main.core.openSettings]
			];
			this.replayAction = [
				[core.triggerReplay, core.stopReplay, core.rewindReplay],
				[core.speedDownReplay, core.speedUpReplay, core.control._replay_SL]
			];
		}
		update() {
			//this._update_background();
			this._update_props();
			this._update_items();
			this._update_equips();
			this._update_keys();
			//this._update_infoWindow();
			this._update_infoBar();
		}
		_update_background(updatedFloorTitle) {
			const bgctx = main.dom.outerBackground.getContext('2d');
			const uictx = main.dom.outerUI.getContext('2d');
			if (!bgctx || !uictx) return;

			const isVertical = core.domStyle.isVertical;

			const W = isVertical ? GAMEVIEW_WIDTH_VERTICAL : GAMEVIEW_WIDTH;
			const H = isVertical ? GAMEVIEW_HEIGHT_VERTICAL : GAMEVIEW_HEIGHT;
			const bgImg = core.material.images.images[
				isVertical ? 'statusBackground_vertical.png' : 'statusBackground.png'
			];

			// 使用封装好的函数设置 canvas 尺寸和缩放
			core.maps._setHDCanvasSize(bgctx, W, H);
			core.maps._setHDCanvasSize(uictx, W, H);

			bgctx.imageSmoothingEnabled = false;

			// 绘制背景图
			if (bgImg instanceof Image && bgImg.complete) {
				bgctx.drawImage(bgImg, 0, 0, W, isVertical ? H : (H - INFO_BAR_HEIGHT));
			} else {
				console.warn('[statusBar] 背景图未加载完成，跳过绘制');
			}

			// 绘制 infoBar 背景色（横屏专用）
			if (!isVertical) {
				bgctx.fillStyle = '#676767';
				bgctx.fillRect(0, INFO_BAR_TOP, W, INFO_BAR_HEIGHT);
			}

			core.setTextAlign('outerUI', 'center');
		}

		// 更新属性
		_update_props(updatedFloorTitle) {
			const ctx = core.getContextByName('outerUI');
			if (ctx) {
				const statusList = ['hp', 'atk', 'def', 'money'];
				const TEXT_COLOR = "#E1E1E1";

				const isVertical = core.domStyle.isVertical;
				const baseFontSize = isVertical ? 14 : 16;
				const font = `bold ${baseFontSize}px Verdana`;
				// 修改1：标题坐标改为区域中心点
				const titleX = isVertical ? 56 : 62; // 居中位置
				const titleY = isVertical ? 22 : 60;

				const startX = isVertical ? 96 : 110;
				const startY = isVertical ? 46 : 93.5;
				const lineHeight = 24;

				// 修改2：清除区域调整为整个标题宽度
				const clearX = isVertical ? 10 : 10;
				const clearY = isVertical ? 0 : 40;
				const clearW = isVertical ? 100 : 100; // 整个标题区域宽度
				const clearH = 130;

				// 设置字体 & 对齐方式
				core.setFont('outerUI', font);
				core.setTextAlign('outerUI', 'right');
				ctx.textBaseline = 'alphabetic';

				if (!core.status.lastPropsDrawn) {
					core.status.lastPropsDrawn = {
						hp: null,
						atk: null,
						def: null,
						money: null,
						title: null
					};
				}

				if (!updatedFloorTitle && core.status.floorId) {
					updatedFloorTitle = core.status.maps[core.status.floorId].title;
				}

				// 更新楼层标题
				if (core.status.lastPropsDrawn.title !== updatedFloorTitle) {
					core.status.lastPropsDrawn.title = updatedFloorTitle;
					// 清理整个标题区域
					ctx.clearRect(clearX, clearY, clearW, lineHeight + 5);

					// 修改3：仅设置标题为居中，不改变原始对齐状态
					const originalAlign = ctx.textAlign;
					ctx.textAlign = 'center'; // 直接设置context

					// 修改4：使用居中对齐绘制在中心位置
					core.fillBoldText('outerUI', updatedFloorTitle, titleX, titleY, TEXT_COLOR, '#000000');

					// 恢复原始对齐状态
					ctx.textAlign = originalAlign;
				}

				// 更新属性值（保持右对齐）
				statusList.forEach((item, i) => {
					let real = Math.round(core.status.hero[item]);
					let tempBonus = 0;

					if (item === 'atk') tempBonus = core.getFlag('临时攻击', 0);
					if (item === 'def') tempBonus = core.getFlag('临时防御', 0);

					const text = tempBonus > 0 ? `${real}+${tempBonus}` : `${real}`;
					const lastText = core.status.lastPropsDrawn[item];

					if (text !== lastText) {
						core.status.lastPropsDrawn[item] = text;
						const y = startY + i * lineHeight;
						// 清除对应行
						ctx.clearRect(clearX, y - 16, clearW, lineHeight + 2);
						// 绘制（保持右对齐）
						core.fillBoldText('outerUI', text, startX, y, TEXT_COLOR, '#000000');
					}
				});
				// 保持最后设置为居中（如果其他部分需要）
				core.setTextAlign('outerUI', 'center');
			}
		}
		_update_items() {
			const ctx = core.getContextByName('outerUI');
			if (ctx) {
				const itemMx = core.domStyle.isVertical ? this.itemMxVertical : this.itemMx;
				const startX = core.domStyle.isVertical ? ITEM_BOX_LEFT_VERTICAL : ITEM_BOX_LEFT;
				const startY = core.domStyle.isVertical ? ITEM_BOX_TOP_VERTICAL : ITEM_BOX_TOP;

				if (!core.status.lastItemsDrawn) {
					core.status.lastItemsDrawn = {
						//[itemId]: { count: number, x: number, y: number }
					}
				}
				const last = core.status.lastItemsDrawn;
				const iconImage = core.material.images.items;

				const drawItem = (item, x, y) => {
					const posx = startX + x * 33,
						posy = startY + y * 32;
					const count = core.itemCount(item);
					const cache = last[item];

					if (count < 1) {
						// 如果不再拥有此道具，则清理并移除缓存
						if (cache) {
							ctx.clearRect(cache.x, cache.y, 32, 32);
							delete last[item];
						}
						if (item === 'book') core.clearMap('damage');
						return;
					}

					/*if (item === 'wand') {
						ctx.clearRect(posx, posy, 32, 32);
						core.drawImage('outerUI', iconImage, 0, 32 * core.material.icons.items[item], 32, 32, posx, posy, 32, 32);
						core.fillBoldText('outerUI', core.getFlag('间隔', 20) - core.getFlag('时间', 0), posx + 25, posy + 30, '#ffffff', '#000', 'bold 10px Verdana');
					} else*/
					if (!cache || cache.count !== count || cache.x !== posx || cache.y !== posy) {
						ctx.clearRect(posx, posy, 32, 32);
						core.drawImage('outerUI', iconImage, 0, 32 * core.material.icons.items[item], 32, 32, posx, posy, 32, 32);
						if ((core.items.items[item].cls === 'tools' && count > 1) || FORCE_COUNTABLE_ITEMS.includes(item))
							core.fillBoldText('outerUI', count, posx + 25, posy + 30, '#FFF', '#000', 'bold 10px Verdana');

						last[item] = { count, x: posx, y: posy };
					}
				};
				core.canvas.outerUI.imageSmoothingEnabled = false;
				for (let y = 0; y < itemMx.length; y++)
					for (let x = 0; x < itemMx[y].length; x++)
						drawItem(itemMx[y][x], x, y);
				core.canvas.outerUI.imageSmoothingEnabled = true;
			}
		}
		_update_equips() {
			const ctx = core.getContextByName('outerUI');
			if (ctx) {
				const isVertical = core.domStyle.isVertical;
				const left = isVertical ? EQUIP_BLOCK_LEFT_VERTICAL : EQUIP_BLOCK_LEFT;
				const top = isVertical ? EQUIP_BLOCK_TOP_VERTICAL : EQUIP_BLOCK_TOP;

				const font = 'bold 16px Verdana';
				const iconImg = core.material.images.items;
				const iconMap = core.material.icons.items;

				const nowWeapon = core.getFlag('nowWeapon');
				const nowShield = core.getFlag('nowShield');

				if (!core.status.lastEquipsDrawn) {
					core.status.lastEquipsDrawn = { weapon: null, shield: null };
				}

				const cache = core.status.lastEquipsDrawn;

				core.setFont('outerUI', font);

				const drawEquip = (id, type, offsetY, color, emptyText) => {
					const key = type === 'weapon' ? 'weapon' : 'shield';
					if (cache[key] === id) return;

					const y = top + offsetY;
					const textX = left + 32;
					const iconX = left + 64;

					// 清除该装备栏区域
					ctx.clearRect(left, y, 105, 40);

					if (!id)
						core.fillBoldText('outerUI', emptyText, left + 50, y + 22, color, '#000');
					else {
						const icon = iconMap[id];
						const name = core.material.items[id].name;
						core.fillBoldText('outerUI', name, textX, y + 22, color, '#000');
						core.canvas.outerUI.imageSmoothingEnabled = false;
						core.drawImage('outerUI', iconImg, 0, 32 * icon, 32, 32, iconX, y, 32, 32);
						core.canvas.outerUI.imageSmoothingEnabled = true;
					}
					cache[key] = id;
				};
				drawEquip(nowWeapon, 'weapon', isVertical ? 9 : 8, '#FFCFAE', '无武器');
				drawEquip(nowShield, 'shield', isVertical ? 49 : 56, '#D1CEFF', '无防具');
			}
		}
		_update_keys() {
			const ctx = core.getContextByName('outerUI');
			if (ctx) {
				const isVertical = core.domStyle.isVertical;
				const keyList = ['yellowKey', 'blueKey', 'redKey'];
				const counts = keyList.map(key => core.itemCount(key));

				if (!core.status.lastKeysDrawn) {
					core.status.lastKeysDrawn = [null, null, null]; // 黄、蓝、红
				}

				const cache = core.status.lastKeysDrawn;

				// 若无变化，直接返回
				if (counts[0] === cache[0] && counts[1] === cache[1] && counts[2] === cache[2]) return;

				// 更新缓存
				core.status.lastKeysDrawn = [...counts];

				// 重新绘制
				const baseX = isVertical ? KEY_BLOCK_LEFT_VERTICAL + 3 : KEY_BLOCK_LEFT + 3;
				const baseY = isVertical ? KEY_BLOCK_TOP_VERTICAL + 2 : 142;
				const lines = isVertical ? 2 : 3;
				const rows = 7;

				// 清除区域
				const clearX = isVertical ? KEY_BLOCK_LEFT_VERTICAL : KEY_BLOCK_LEFT;
				const clearY = isVertical ? KEY_BLOCK_TOP_VERTICAL : 140;
				ctx.clearRect(clearX, clearY, 105, 75);

				const drawKey = this.drawKey.bind(this);
				const total = counts.reduce((a, b) => a + b, 0);

				if (total > lines * rows) {
					// 压缩绘制模式
					for (let i = 0; i < 3; i++) {
						const x = isVertical ? i * 32 : 0;
						const y = isVertical ? parseInt((lines - 1) / 2 * 14) : 48 - i * 22;
						drawKey(keyList[i], baseX + x, baseY + y);
						core.setFont('outerUI', isVertical ? 'bold 10px Verdana' : '14px Verdana');
						core.setTextAlign('outerUI', 'left');
						core.canvas.outerUI.textBaseline = 'middle';
						core.fillBoldText('outerUI', counts[i], baseX + x + (isVertical ? 10 : 16), baseY + y + (isVertical ? 20 : 9), TEXT_COLOR, '#000');
					}
				} else {
					// 多枚堆叠绘制
					let dn = 2,
						dc = 0;
					core.canvas.outerUI.imageSmoothingEnabled = false;
					while (dn >= 0 && dc < lines * rows) {
						if (counts[dn]) {
							const x = baseX + (dc % rows) * 14;
							const y = baseY + parseInt(dc / rows) * 17;
							drawKey(keyList[dn], x, y);
							counts[dn]--, dc++;
						} else dn--;
					}
					core.canvas.outerUI.imageSmoothingEnabled = true;
				}
			}

		}
		drawKey(key, x, y) {
			let sx = 3,
				sy = 0;
			if (key === 'blueKey') sx += 16;
			else if (key === 'yellowKey') sy += 16;
			core.drawImage('outerUI', core.statusBar.icons.keys, sx, sy, 10, 16, x, y, 10, 16);
		}

		_update_infoWindow() {
			const ctx = core.getContextByName('outerUI');
			if (ctx) {
				const itemId = this.selectedItem;
				if (core.status.lastInfoWindowItem === itemId) return;
				core.status.lastInfoWindowItem = itemId;
				if (core.domStyle.isVertical) {
					ctx.clearRect(INFO_BLOCK_LEFT_VERTICAL, INFO_BLOCK_TOP_VERTICAL, 200, 200);
					if (itemId) {
						const icon = core.material.icons.items[itemId];
						const item = core.material.items[itemId];
						core.fillText('outerUI', item.name, INFO_BLOCK_LEFT_VERTICAL + 48, INFO_BLOCK_TOP_VERTICAL + 22, '#D1CEFF');
						core.drawImage('outerUI', core.material.images.items, 0, 32 * icon, 32, 32, INFO_BLOCK_LEFT_VERTICAL + 80, INFO_BLOCK_TOP_VERTICAL + 2, 32, 32);
						core.ui.drawTextContent('outerUI', eval('`' + item.text + '`'), {
							left: INFO_BLOCK_LEFT_VERTICAL + 3,
							top: INFO_BLOCK_TOP_VERTICAL + 36,
							maxWidth: 150,
							color: '#D1CEFF'
						});
					}
				} else {
					ctx.clearRect(INFO_BLOCK_LEFT, INFO_BLOCK_TOP, 105, 100);
					if (itemId) {
						const icon = core.material.icons.items[itemId];
						const item = core.material.items[itemId];
						core.fillText('outerUI', item.name, INFO_BLOCK_LEFT + 32, INFO_BLOCK_TOP + 25, '#D1CEFF');
						core.drawImage('outerUI', core.material.images.items, 0, 32 * icon, 32, 32, INFO_BLOCK_LEFT + 64, INFO_BLOCK_TOP + 4, 32, 32);
						core.ui.drawTextContent('outerUI', eval('`' + item.text + '`'), {
							left: INFO_BLOCK_LEFT + 1,
							top: INFO_BLOCK_TOP + 36,
							maxWidth: 105,
							color: '#D1CEFF'
						});
					}
				}
			}
		};
		showItemInfo(itemId) {
			this.selectedItem = itemId;
			this._update_infoWindow();
		}
		clearItemInfo() {
			this.selectedItem = null;
			this._update_infoWindow();
		}
		_update_toolBox() {
			const ctx = core.getContextByName('outerUI');

			if (ctx) {
				const tools = core.isReplaying() ? [
					[core.status.replay.pausing ? 'play' : 'pause', 'stop', 'rewind'],
					['speedDown', 'speedUp', 'save']
				] : [
					['keyboard', 'autoload', 'help'],
					['save', 'load', 'settings']
				];
				core.canvas.outerUI.imageSmoothingEnabled = false;
				if (core.domStyle.isVertical) {
					ctx.clearRect(TOOL_BOX_LEFT_VERTICAL, TOOL_BOX_TOP_VERTICAL, 115, 80);
					for (let i = 0; i < tools.length; i++) {
						for (let j = 0; j < tools[i].length; j++) {
							core.drawIcon('outerUI', tools[i][j], TOOL_BOX_LEFT_VERTICAL + j * 34, TOOL_BOX_TOP_VERTICAL + i * 34, 32, 32);
						}
					}
				} else {
					ctx.clearRect(TOOL_BOX_LEFT, TOOL_BOX_TOP, 115, 80);
					for (let i = 0; i < tools.length; i++) {
						for (let j = 0; j < tools[i].length; j++) {
							core.drawIcon('outerUI', tools[i][j], TOOL_BOX_LEFT + j * 34, TOOL_BOX_TOP + i * 34, 32, 32);
						}
					}
				}
				core.canvas.outerUI.imageSmoothingEnabled = true;
			}
		}
		onclick(x, y) {
			const pos = [x, y];
			const isVertical = core.domStyle.isVertical;

			const makeBox = ([x, y], [w, h]) => [
				[x, y],
				[x + w, y + h]
			];
			const gridify = ([x, y], [gw, gh]) => [Math.floor(x / gw), Math.floor(y / gh)];
			const inRect = ([x, y], [
				[sx, sy],
				[dx, dy]
			]) => sx <= x && x <= dx && sy <= y && y <= dy;
			const relativeTo = ([x, y], [ax, ay]) => [x - ax, y - ay];
			const sizeOfMX = (mx, gw = 32, gh = 33) => [gw * mx[0].length, gh * mx.length];

			const useItem = itemId => {
				if (!core.hasItem(itemId)) return;
				const cls = core.material.items[itemId].cls;
				if (cls === "constants") {
					switch (itemId) {
					case "book":
						core.openBook(true);
						break;
					case "fly":
						core.useFly(true);
						break;
					case "wand":
					case "snow":
					case "skill1":
						core.useItem(itemId);
						break;
					default:
						this.showItemInfo(itemId);
					}
				} else if (itemId !== this.selectedItem) {
					this.showItemInfo(itemId);
				} else {
					if (itemId === "centerFly") core.ui._drawCenterFly();
					else core.useItem(itemId);
				}
			};

			const handleItemClick = (itemMx, left, top, gw, gh) => {
				const itemBox = makeBox([left, top], sizeOfMX(itemMx, gw, gh));
				if (!inRect(pos, itemBox)) return false;

				const [gx, gy] = gridify(relativeTo(pos, itemBox[0]), [gw, gh]);
				if (!itemMx[gy] || !itemMx[gy][gx]) return false;

				const itemId = itemMx[gy][gx];
				if (itemId === "book") useItem(itemId);
				if (core.isReplaying() || core.status.lockControl || core.isMoving()) return true;

				useItem(itemId);
				this._update_items();
				return true;
			};

			const handleToolClick = (left, top) => {
				const toolBox = makeBox([left, top], [102, 68]);
				if (!inRect(pos, toolBox)) return false;

				const [gx, gy] = gridify(relativeTo(pos, toolBox[0]), [34, 34]); // gx = col, gy = row

				if (core.isReplaying()) {
					const action = this.replayAction && this.replayAction[gy] && this.replayAction[gy][gx];
					if (typeof action === 'function') action.call(core);
				} else if (core.isPlaying()) {
					const action = this.toolbarAction && this.toolbarAction[gy] && this.toolbarAction[gy][gx];
					if (typeof action === 'function') action.call(core, true);
				}
				return true;
			};

			if (isVertical) {
				if (handleItemClick(this.itemMxVertical, ITEM_BOX_LEFT_VERTICAL, ITEM_BOX_TOP_VERTICAL, 32, 33)) return;
				if (handleToolClick(TOOL_BOX_LEFT_VERTICAL, TOOL_BOX_TOP_VERTICAL)) return;
			} else {
				if (handleItemClick(this.itemMx, ITEM_BOX_LEFT, ITEM_BOX_TOP, 33, 32)) return;
				if (handleToolClick(TOOL_BOX_LEFT, TOOL_BOX_TOP)) return;
			}
		}

		_update_infoBar() {
			const ctx = core.getContextByName('outerUI');
			if (ctx) {
				// --- 修复开始 ---
				// 在绘制前，明确设置文本对齐方式和基线，避免受其他函数影响
				core.setTextAlign('outerUI', 'left');
				ctx.textBaseline = 'alphabetic'; // 关键修复：确保每次绘制时基线一致
				// --- 修复结束 ---

				const text = this.infoText;

				if (core.domStyle.isVertical) {
					ctx.clearRect(0, INFO_BAR_TOP_VERTICAL, GAMEVIEW_WIDTH, INFO_BAR_HEIGHT_VERTICAL);
					core.setFont('outerUI', 'bold 14px Verdana');
					if (text) core.fillText('outerUI', text, 10, INFO_BAR_TOP_VERTICAL + 14, TEXT_COLOR);
				} else {
					ctx.clearRect(0, INFO_BAR_TOP, GAMEVIEW_WIDTH, INFO_BAR_HEIGHT);
					core.setFont('outerUI', 'bold 16px Verdana');
					if (text) core.fillText('outerUI', text, 10, INFO_BAR_TOP + 16, TEXT_COLOR);
				}

				// 绘制完毕后，可以恢复为默认值，以免影响其他部分的绘制（好习惯）
				core.setTextAlign('outerUI', 'center');
			}
		}
		print(text, cnt = 1) {
			this.infoText = text;
			this.infocnt = cnt == null ? 1 : cnt;
			this._update_infoBar();
		}

		printEnvironmentInfo() {
			if (!this.infocnt) return;
			const ids = [];
			for (const block of core.status.thisMap.blocks) {
				if (!block.disable && core.nearHero(block.x, block.y))
					ids.push(block.event.id);
			}
			for (const infoRule of StatusBar.infoRules) {
				if (ids.indexOf(infoRule.id) >= 0) {
					this.print(infoRule.text);
					return;
				}
			}
		}
		clearInfo(etype) {
			this.clearItemInfo && this.clearItemInfo();
			if (this.infocnt === 1) {
				setTimeout(() => {
					if (this.infocnt === 0) {
						this.infoText = void 0;
						this._update_infoBar();
					}
				}, 200);
				this.infocnt = 0;
			} else if (this.infocnt > 1) {
				this.infocnt--;
			}
		}
	}
	StatusBar.infoRules = [
		{ id: 'lava', text: '岩浆好热啊!' },
		{ id: 'upFloor', text: '你看到了楼梯' },
		{ id: 'downFloor', text: '你看到了楼梯' },
		{ id: 'blueShop', text: '你看到了一个祭坛' },
		{ id: 'man', text: '你看到了一个老人' },
		{ id: 'woman', text: '你看到了一个商人' },
		{ id: 'fairy', text: '你看到了一个商人' },
		{ id: 'thief', text: '你看到了一个小偷' }
	];
	core.ui.statusBar = new StatusBar();

	core.control.clearStatusBar = function () {
		core.clearMap('outerUI');
	};
	// init() called in `afterLoadResources`.
},
    "override": function () {

	core.statusBar.icons = {
		'floor': 0,
		'name': null,
		'lv': 1,
		'hpmax': 2,
		'hp': 3,
		'atk': 4,
		'def': 5,
		'mdef': 6,
		'money': 7,
		'experience': 8,
		'up': 9,
		'book': 10,
		'fly': 11,
		'toolbox': 12,
		'keyboard': 13,
		'shop': 14,
		'save': 15,
		'load': 16,
		'settings': 17,
		'play': 18,
		'pause': 19,
		'stop': 20,
		'speedDown': 21,
		'speedUp': 22,
		'rewind': 23,
		'equipbox': 24,
		'mana': 25,
		'skill': 26,
		'paint': 27,
		'erase': 28,
		'empty': 29,
		'exit': 30,
		'btn1': 31,
		'btn2': 32,
		'btn3': 33,
		'btn4': 34,
		'btn5': 35,
		'btn6': 36,
		'btn7': 37,
		'btn8': 38,
		'keys': 39,
		'help': 40,
		'battle': 41,
		'autoload': 42
	};

	core.actions._clickCenterFly = function (x, y) {
		var posX = core.status.event.data.posX,
			posY = core.status.event.data.posY;
		core.ui.closePanel();
		if (x == posX && y == posY) {
			if (core.canUseItem('centerFly')) {
				core.useItem('centerFly');
			} else {
				core.drawTip('当前不能使用中心对称飞行器');
			}
		} else core.drawTip('取消使用');
	}

	var _clickSL = core.actions._clickSL;
	core.actions._clickSL = function (x, y) {
		var page = core.status.event.data.page,
			offset = core.status.event.data.offset;
		var index = page * 10 + offset;

		// 上一页
		if ((x == this.HSIZE - 1 || x == this.HSIZE - 2) && y == this.LAST) {
			core.ui._drawSLPanel(10 * (page - 1) + offset);
			return;
		}
		// 下一页
		if ((x == this.HSIZE + 1 || x == this.HSIZE + 2) && y == this.LAST) {
			core.ui._drawSLPanel(10 * (page + 1) + offset);
			return;
		}
		if ((x == this.HSIZE - 3 || x == this.HSIZE + 3) && y == this.LAST) {
			return;
		}
		_clickSL.call(this, x, y);
	}

	var _sys_longClick_lockControl = core.actions._sys_longClick_lockControl;
	core.actions._sys_longClick_lockControl = function (x, y) {
		if (!core.status.lockControl) return false;

		// 长按SL上下页快速翻页
		if (["save", "load", "replayLoad", "replayRemain"].indexOf(core.status.event.id) >= 0) {
			if (y == this.LAST && x <= this.HSIZE + 3 && x >= this.HSIZE - 3) {
				core.actions._clickSL(x, y);
				return true;
			}
		}
		return _sys_longClick_lockControl.call(this, x, y);
	}
	core.registerAction('longClick', '_sys_longClick_lockControl', core.actions._sys_longClick_lockControl, 50);

	core.control._moveHero_moving = function () {
		core.status.heroStop = false;
		core.status.automaticRoute.moveDirectly = false;
		var move = function () {
			if (!core.status.heroStop) {
				if (core.hasFlag('debug') && core.status.ctrlDown) {
					if (core.status.heroMoving != 0) return;
					// 检测是否穿出去
					var nx = core.nextX(),
						ny = core.nextY();
					if (nx < 1 || nx >= core.bigmap.width - 1 || ny < 1 || ny >= core.bigmap.height - 1) return;
					core.eventMoveHero([core.getHeroLoc('direction')], core.values.moveSpeed, move);
				} else {
					core.moveAction();
					setTimeout(move, 50);
				}
			}
		}
		move();
	}

	core.control._drawHero_updateViewport = function (x, y, offset) {}

	var _drawThumbnail_drawTempCanvas = core.maps._drawThumbnail_drawTempCanvas;
	core.maps._drawThumbnail_drawTempCanvas = function (floorId, blocks, options) {
		if (main.mode == 'editor') {
			_drawThumbnail_drawTempCanvas.call(this, floorId, blocks, options);
			return;
		}

		var width = core.floors[floorId].width;
		var height = core.floors[floorId].height;
		// 绘制到tempCanvas上面
		var tempCanvas = core.bigmap.tempCanvas;
		options.v2 = false;
		tempCanvas.canvas.width = width * 32;
		tempCanvas.canvas.height = height * 32;
		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;
		}
		this._drawThumbnail_realDrawTempCanvas(floorId, blocks, options);

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

	var _drawThumbnail_drawToTarget = core.maps._drawThumbnail_drawToTarget;
	core.maps._drawThumbnail_drawToTarget = function (floorId, options) {
		if (main.mode == 'editor') {
			_drawThumbnail_drawToTarget.call(this, floorId, options);
			return;
		}

		var ctx = core.getContextByName(options.ctx);
		if (ctx == null) return;
		var x = options.x || 0,
			y = options.y || 0,
			size = options.size || core.__PIXELS__;
		var tempCanvas = core.bigmap.tempCanvas;
		core.drawImage(ctx, tempCanvas.canvas, 32, 32, core.__PIXELS__, core.__PIXELS__, x, y, size, size);
	}

	var _drawWindowSkin = core.ui.drawWindowSkin;
	core.ui.drawWindowSkin = function (background, ctx, x, y, w, h, direction, px, py) {
		_drawWindowSkin.call(this, background, ctx, x, y, w, h, direction, px, py);

		var c = parseInt(w / 2);
		core.drawImage(ctx, background, 160, 90, 16, 6, x + c - 8, y, 16, 6);
	}

	var _drawPagination = ui.prototype.drawPagination;
	core.ui.drawPagination = function (page, totalPage, y) {
		if (["save", "load", "replayLoad", "replayRemain", "replaySince"].indexOf(core.status.event.id) >= 0) {
			if (totalPage <= 1) return;
			if (y == null) y = this.LAST;

			core.setFillStyle('ui', '#DDDDDD');
			var length = core.calWidth('ui', page, this._buildFont(15, true));

			core.setTextAlign('ui', 'left');
			core.fillText('ui', page, parseInt((this.PIXEL - length) / 2), y * 32 + 19);

			core.setTextAlign('ui', 'center');
			if (page > 1)
				core.fillText('ui', '上一页', this.HPIXEL - 48, y * 32 + 19);
			if (page < totalPage)
				core.fillText('ui', '下一页', this.HPIXEL + 48, y * 32 + 19);
			return;
		}
		_drawPagination.call(this, page, totalPage, y);
	}

	core.ui._drawCenterFly = function () {
		core.lockControl();
		core.status.event.id = 'centerFly';
		var fillstyle = 'rgba(255,0,0,0.5)';
		if (core.canUseItem('centerFly')) fillstyle = 'rgba(0,255,0,0.5)';
		var toX = core.bigmap.width - 1 - core.getHeroLoc('x'),
			toY = core.bigmap.height - 1 - core.getHeroLoc('y');
		this.clearUI();
		core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000');
		core.drawThumbnail(null, null, { heroLoc: core.status.hero.loc, heroIcon: core.status.hero.image, ctx: 'ui', centerX: toX, centerY: toY });
		var offsetX = 1,
			offsetY = 1;
		core.fillRect('ui', (toX - offsetX) * 32, (toY - offsetY) * 32, 32, 32, fillstyle);
		core.status.event.data = { "x": toX, "y": toY, "posX": toX - offsetX, "posY": toY - offsetY };
		core.playSound('打开界面');
		core.drawTip("请确认当前" + core.material.items['centerFly'].name + "的位置", 'centerFly');
		return;
	}

	core.ui.drawHelp = function () {
		core.clearUI();
		core.status.event.id = 'help';
		core.lockControl();
		core.setAlpha('ui', 1);
		core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#FFFFFF');
		core.drawImage('ui', core.material.images.keyboard, 0, 0, 416, 416, 0, 0, 352, 352);
	}

	core.actions._getClickLoc = function (x, y) {
		var size = 32 * core.domStyle.scale;
		var left = main.dom.gameDraw.offsetLeft + main.dom.gameGroup.offsetLeft;
		var top = main.dom.gameDraw.offsetTop + main.dom.gameGroup.offsetTop;
		var loc = { 'x': Math.max(x - left, 0), 'y': Math.max(y - top, 0), 'size': size };
		return loc;
	}

	core.enemys.getDamageString = function (enemy, x, y, floorId) {
		if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
		var damage = this.getDamage(enemy, x, y, floorId);

		var color = '#000000';

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

			damage = core.formatBigNumber(damage, true);
			if (core.enemys.hasSpecial(enemy, 19))
				damage += "+";
			if (core.enemys.hasSpecial(enemy, 21))
				damage += "-";
			if (core.enemys.hasSpecial(enemy, 11))
				damage += "^";
		}

		return {
			"damage": damage,
			"color": color
		};
	}

	core.ui._drawBook_drawDamage = function (index, enemy, offset, position) {
		core.setTextAlign('ui', 'center');
		var damage = enemy.damage,
			color = '#FFFF00';
		if (damage == null) {
			damage = '不可攻击';
			color = '#FF2222';
		}
		if (damage == 0) {
			damage = '无危险';
			color = '#11FF11';
		} else {
			if (damage >= core.status.hero.hp) color = '#FF2222';
			else if (damage >= core.status.hero.hp * 2 / 3) color = '#FF9933';
			else if (damage <= 0) color = '#11FF11';
			damage = core.formatBigNumber(damage);
			if (core.enemys.hasSpecial(enemy, 19)) damage += "+";
			if (core.enemys.hasSpecial(enemy, 21)) damage += "-";
			if (core.enemys.hasSpecial(enemy, 11)) damage += "^";
		}
		if (enemy.notBomb) damage += "[b]";
		core.fillText('ui', damage, offset, position, color, this._buildFont(13, true));
	}

	core.control.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;
			core.drawTip("你没有神圣盾不能防御，受到 " + damage + " 点魔法伤害");
			core.playSound('阻激夹域');
			this._checkBlock_disableQuickShop();
			core.status.hero.statistics.extraDamage += damage;
			if (core.status.hero.hp <= 0) {
				core.status.hero.hp = 0;
				core.updateStatusBar();
				core.events.lose();
				return;
			} else {
				core.updateStatusBar();
			}
		}
		this._checkBlock_ambush(core.status.checkBlock.ambush[loc]);
		this._checkBlock_repulse(core.status.checkBlock.repulse[loc]);
	}

	core.registerSystemEvent("man", function (data, callback) {
		var a = parseInt(core.status.floorId.substring(2));
		var b = data.x;
		var c = data.y;
		core.insertAction([
			{ "type": "insert", "name": "对话", "args": [a, b, c, 0] },
		]);
		//console.log(data);
		if (callback) callback();
	});

	core.registerSystemEvent("woman", function (data, callback) {
		var name = core.status.floorId + '@' + data.x + '@' + data.y + '@' + 'A';
		var a = parseInt(core.status.floorId.substring(2));
		var b = data.x;
		var c = data.y;
		if (core.getFlag(name, 0) == 1) {
			core.insertAction([
				{ "type": "insert", "name": "对话", "args": [a, b, c, 1] },
			]);
		} else {
			core.insertAction([
				{ "type": "insert", "name": "商人", "args": [b, c] },
			]);
		}
		//console.log(data);
		if (callback) callback();
	});

	core.registerSystemEvent("specialwall", function (data, callback) {
		if (data.event.id == 'whiteWall' || core.getFlag('talking') > 0) {
			core.insertAction({ 'type': 'openDoor', loc: [data.x, data.y] });
			if (data.event.id != 'whiteWall') {
				core.setFlag('end', 1);
			}
			core.setFlag('talking', 0); //穿墙之后不能再次穿墙
		}
		//console.log(data);
		if (callback) callback();
	});

	core.registerSystemEvent("fakeWall", function (data, callback) {
		if (data.event.id == 'blueWall') {
			core.insertAction([
				{ "type": "openDoor", loc: [data.x, data.y] },
			]);
		}
		//console.log(data);
		if (callback) callback();
	});

	core.ui._drawWindowSelector = function (background, x, y, w, h) {
		w = Math.round(w) + 48;
		h = Math.round(h);
		var ctx = core.ui.createCanvas("_selector", x - 24, y, w, h, 165);
		ctx.canvas.id = '';
		this._drawSelector(ctx, background, w, h);
	}

	core.ui._drawSelector = function (ctx, background, w, h, left, top) {
		left = left || 0;
		top = top || 0;
		ctx = this.getContextByName(ctx);
		if (!ctx) return;
		if (typeof background == 'string')
			background = core.material.images.images[background];
		if (!(background instanceof Image)) return;
		// badge
		ctx.drawImage(background, 132, 68, 24, 24, left + 4, top + 4, 24, 24);
		ctx.drawImage(background, 132, 68, 24, 24, w - left - 28, top + 4, 24, 24);
	}

	core.ui.drawTip = function (text, id, clear) {
		core.ui.statusBar.print(text);
	}

	core.ui.clearMap = function (name, x, y, width, height) {
		if (name == 'all') {
			for (var m in core.canvas) {
				core.canvas[m].clearRect(0, 0, core.bigmap.width * 32, core.bigmap.height * 32);
			}
			core.clearMap("outerUI");
			core.dom.gif.innerHTML = "";
			core.removeGlobalAnimate();
			core.deleteCanvas(function (one) { return one.startsWith('_bigImage_'); });
		} else {
			var ctx = this.getContextByName(name);
			if (ctx) ctx.clearRect(x || 0, y || 0, width || ctx.canvas.width, height || ctx.canvas.height);
		}
	}

	core.control._updateStatusBar_setToolboxIcon = function () {
		core.ui.statusBar._update_toolBox();
	}

	var _changeFloor_getInfo = core.events._changeFloor_getInfo;
	core.events._changeFloor_getInfo = function (floorId, stair, heroLoc, time) {
		var info = _changeFloor_getInfo.call(this, floorId, stair, heroLoc, time);
		if (info == null) return null;
		info.time = 0;
		info.origin = floorId;
		return info;
	}

	core.events._changeFloor_afterChange = function (info, callback) {
		if (!info.locked) core.unlockControl();
		core.status.replay.animate = false;
		core.events.afterChangeFloor(info.floorId);

		if (info.origin == ':before') core.ui.statusBar.print('走下了楼梯')
		else if (info.origin == ':next') core.ui.statusBar.print('登上了楼梯')
		if (callback) callback();
	}

	if (window.jsinterface && window.jsinterface.requestLandscape) {
		window.jsinterface.requestLandscape();
	}
},
    "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;
	}

	/// 能否使用快捷商店
	this.canUseQuickShop = function (id) {
		// 如果返回一个字符串，表示不能，字符串为不能使用的提示
		// 返回null代表可以使用

		// 检查当前楼层的canUseQuickShop选项是否为false
		if (core.status.thisMap.canUseQuickShop === false)
			return '当前楼层不能使用快捷商店。';
		return null;
	}

	var _shouldProcessKeyUp = true;

	/// 允许商店X键退出
	core.registerAction('keyUp', 'shops', function (keycode) {
		if (!core.status.lockControl || core.status.event.id != 'action') return false;
		if ((keycode == 13 || keycode == 32) && !_shouldProcessKeyUp) {
			_shouldProcessKeyUp = true;
			return true;
		}

		if (!core.hasFlag("@temp@shop") || core.status.event.data.type != 'choices') return false;
		var data = core.status.event.data.current;
		var choices = data.choices;
		var topIndex = core.actions._getChoicesTopIndex(choices.length);
		if (keycode == 88 || keycode == 27) { // X, ESC
			core.actions._clickAction(core.actions.HSIZE, topIndex + choices.length - 1);
			return true;
		}
		return false;
	}, 60);

	/// 允许长按空格或回车连续执行操作
	core.registerAction('keyDown', 'shops', function (keycode) {
		if (!core.status.lockControl || !core.hasFlag("@temp@shop") || core.status.event.id != 'action') return false;
		if (core.status.event.data.type != 'choices') return false;
		var data = core.status.event.data.current;
		var choices = data.choices;
		var topIndex = core.actions._getChoicesTopIndex(choices.length);
		if (keycode == 13 || keycode == 32) { // Space, Enter
			core.actions._clickAction(core.actions.HSIZE, topIndex + core.status.event.selection);
			_shouldProcessKeyUp = false;
			return true;
		}
		return false;
	}, 60);

	// 允许长按屏幕连续执行操作
	core.registerAction('longClick', 'shops', function (x, y, px, py) {
		if (!core.status.lockControl || !core.hasFlag("@temp@shop") || core.status.event.id != 'action') return false;
		if (core.status.event.data.type != 'choices') return false;
		var data = core.status.event.data.current;
		var choices = data.choices;
		var topIndex = core.actions._getChoicesTopIndex(choices.length);
		if (x >= core.actions.CHOICES_LEFT && x <= core.actions.CHOICES_RIGHT && y >= topIndex && y < topIndex + choices.length) {
			core.actions._clickAction(x, y);
			return true;
		}
		return false;
	}, 60);
},
    "宝石血瓶显示": function () {
	/* 宝石血瓶左下角显示数值
	 * 需要将 变量：itemDetail改为true才可正常运行
	 * 请尽量减少勇士的属性数量，否则可能会出现严重卡顿（划掉，现在你放一万个属性也不会卡）
	 * 注意：这里的属性必须是core.status.hero里面的，flag无法显示
	 * 如果不想显示，可以core.setFlag("itemDetail", false);
	 * 然后再core.getItemDetail();
	 * 如有bug在大群或造塔群@古祠
	 */

	const ignore = ['superPotion'];

	// 取消注释下面这句可以减少超大地图的判定。
	// 如果地图宝石过多，可能会略有卡顿，可以尝试取消注释下面这句话来解决。
	// core.bigmap.threshold = 256;
	const origin = core.control.updateStatusBar;
	core.updateStatusBar = core.control.updateStatusBar = function () {
		if (core.getFlag('__statistics__')) return;
		else return origin.apply(core.control, arguments);
	}

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

		// 没有怪物手册
		if (!core.hasItem('book')) return;
		core.status.damage.posX = core.bigmap.posX;
		core.status.damage.posY = core.bigmap.posY;
		if (!onMap) {
			const width = core.floors[floorId].width,
				height = core.floors[floorId].height;
			// 地图过大的缩略图不绘制显伤
			if (width * height > core.bigmap.threshold) return;
		}
		this._updateDamage_damage(floorId, onMap);
		this._updateDamage_extraDamage(floorId, onMap);
		core.getItemDetail(floorId); // 宝石血瓶详细信息
		//core.drawDoorDetail(floorId); // 门详细信息
		this.drawDamage(ctx);
	};
	// 获取宝石信息 并绘制
	this.getItemDetail = function (floorId) {
		floorId = floorId != null ? floorId : (core.status.thisMap && core.status.thisMap.floorId);
		if (!core.status.thisMap || !core.status.maps[floorId]) return;

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

		const originalHero = core.clone(core.status.hero);
		const blocks = core.status.maps[floorId].blocks;

		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;

			// v2地图优化跳过不可见区域
			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;
			}

			var itemId = event.id;
			var item = core.material.items[itemId];
			if (!item) return;

			// --- 处理装备类 ---
			if (item.cls === 'equips') {
				var equipValue = (item.equip && item.equip.value) || {};
				var percentage = (item.equip && item.equip.percentage) || {};
				var equipDiff = {};

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

				drawItemDetail(equipDiff, x, y);
				return;
			}

			// --- 处理普通道具 ---
			var heroClone = core.clone(originalHero);
			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(item.itemEffect);
			} catch (e) {
				console.warn("物品效果执行失败：" + itemId, e);
			}

			drawItemDetail(diff, x, y);
		});

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


	this.drawDoorDetail = function (floorId) {
		//if (!core.getFlag('doorDetail')) return;
		floorId = floorId === void 0 || floorId === null ? core.status.thisMap.floorId : floorId;
		let diff = {};
		const before = core.status.hero;
		const hero = core.clone(core.status.hero);
		const handler = {
			set: function (target, key, v) {
				diff[key] = v - (target[key] || 0);
				if (!diff[key]) diff[key] = void 0;
				return true;
			}
		};
		core.status.hero = new Proxy(hero, handler);
		core.status.maps[floorId].blocks.forEach(function (block) {
			if ((block.id < 81) || (block.id > 83))
				return;
			const x = block.x,
				y = block.y;
			// v2优化，只绘制范围内的部分
			if (core.bigmap.v2) {
				if (
					x < core.bigmap.posX - core.bigmap.extend ||
					x > core.bigmap.posX + core._SIZE_ + core.bigmap.extend ||
					y < core.bigmap.posY - core.bigmap.extend ||
					y > core.bigmap.posY + core._SIZE_ + core.bigmap.extend
				) {
					return;
				}
			}
			core.setFlag('__statistics__', true);
			core.status.damage.data.push({
				text: block.id,
				px: 32 * x + 2,
				py: 32 * y + 30,
				color: '#FFFFFF'
			});
		});
		core.status.hero = before;
		window.hero = before;
		window.flags = before.flags;
	};

	// 绘制
	function drawItemDetail(diff, x, y) {
		const px = 32 * x + 2,
			py = 32 * y + 30;
		let content = '';
		// 获得数据和颜色
		let i = 0;
		for (const name in diff) {
			if (!diff[name]) continue;
			let color = '#fff';

			if (typeof diff[name] === 'number')
				content = core.formatBigNumber(diff[name], true);
			else content = diff[name];
			switch (name) {
			case 'atk':
			case 'atkper':
				color = '#FF7A7A';
				break;
			case 'def':
			case 'defper':
				color = '#00E6F1';
				break;
			case 'mdef':
			case 'mdefper':
				color = '#6EFF83';
				break;
			case 'hp':
				color = '#A4FF00';
				break;
			case 'hpmax':
			case 'hpmaxper':
				color = '#F9FF00';
				break;
			case 'mana':
				color = '#c66';
				break;
			}
			// 绘制
			core.status.damage.data.push({
				text: content,
				px: px,
				py: py - 10 * i,
				color: color
			});
			i++;
		}
	}


},
    "HD": function () {
	maps.prototype._setHDCanvasSize = function (ctx, width, height) {
		ctx.setTransform(1, 0, 0, 1, 0, 0);
		var ratio = core.domStyle.scale;
		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.drawImage = function (name, image, x, y, w, h, x1, y1, w1, h1, angle, reverse) {
		// 检测文件名以 :x, :y, :o 结尾，表示左右翻转，上下翻转和中心翻转
		var ctx = this.getContextByName(name);
		if (!ctx) return;

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

		// 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;
		}
	}
	ui.prototype.resizeCanvas = function (name, width, height, styleOnly) {
		var ctx = core.getContextByName(name);
		const canvas = ctx.canvas;
		if (!ctx) return null;
		if (width != null) {
			if (!styleOnly && ctx.canvas.hasAttribute('isHD'))
				core.maps._setHDCanvasSize(ctx, width, null);
			ctx.canvas.style.width = width * core.domStyle.scale + 'px';
			canvas.setAttribute('_width', width);
		}
		if (height != null) {
			if (!styleOnly && ctx.canvas.hasAttribute('isHD'))
				core.maps._setHDCanvasSize(ctx, null, height);
			ctx.canvas.style.height = height * core.domStyle.scale + 'px';
			canvas.setAttribute('_height', height);
		}
		return ctx;
	}
	control.prototype.setDisplayScale = function (delta) {
		// PRESET 与 availableScale 的来源应与 resize 保持一致
		const PRESET_SCALES = [1, 1.25, 1.5, 1.75, 2];

		// 确保 core.domStyle.isVertical 可用；若不可读则根据 viewport 简单判断
		if (typeof core.domStyle.isVertical === 'undefined') {
			core.domStyle.isVertical = (main.dom.body.clientHeight > main.dom.body.clientWidth);
		}
		const isVertical = core.domStyle.isVertical;

		// 如果 availableScale 尚未计算（例如在某些情况下 setDisplayScale 在 resize 之前被调用），就临时计算一次
		if (!core.domStyle.availableScale || core.domStyle.availableScale.length === 0) {
			const clientWidth = main.dom.body.clientWidth;
			const clientHeight = main.dom.body.clientHeight;
			const totalWidth = isVertical ? GAMEVIEW_WIDTH_VERTICAL : GAMEVIEW_WIDTH;
			const totalHeight = isVertical ? GAMEVIEW_HEIGHT_VERTICAL : GAMEVIEW_HEIGHT;
			const maxRatio = Math.min(clientWidth / totalWidth, clientHeight / totalHeight);
			core.domStyle.availableScale = PRESET_SCALES.filter(v => v <= maxRatio);
		}

		// 如果 availableScale 为空（极小屏幕），则允许直接以 maxRatio 为唯一选项
		if (!core.domStyle.availableScale || core.domStyle.availableScale.length === 0) {
			// 直接设置到能 fit 的最大缩放（避免出界）
			const clientWidth = main.dom.body.clientWidth;
			const clientHeight = main.dom.body.clientHeight;
			const totalWidth = isVertical ? GAMEVIEW_WIDTH_VERTICAL : GAMEVIEW_WIDTH;
			const totalHeight = isVertical ? GAMEVIEW_HEIGHT_VERTICAL : GAMEVIEW_HEIGHT;
			const maxRatio = Math.min(clientWidth / totalWidth, clientHeight / totalHeight);
			core.domStyle.scale = maxRatio;
			// 保存为对应 orientation（可选）
			const storageKey0 = isVertical ? 'scale_vertical' : 'scale_horizontal';
			core.setLocalStorage(storageKey0, core.domStyle.scale);
			core.resize();
			return;
		}

		// 在 availableScale 中找到当前 index
		let index = core.domStyle.availableScale.indexOf(core.domStyle.scale);
		if (index < 0) {
			// 当前 scale 不在可选列表（例如之前保存了更大的值），尽量以最接近 maxRatio 的候选为起点
			// 这里我们把 index 设置为 0（第一个候选），然后再按 delta 移动
			index = 0;
		}

		index = (index + delta + core.domStyle.availableScale.length) % core.domStyle.availableScale.length;
		core.domStyle.scale = core.domStyle.availableScale[index];

		// 保存到对应 orientation 的 localStorage
		const storageKey = isVertical ? 'scale_vertical' : 'scale_horizontal';
		core.setLocalStorage(storageKey, core.domStyle.scale);

		// 触发 resize 重新布局
		core.resize();
	};
	events.prototype.setGlobalFlag = function (name, value) {
		var flags = core.getFlag("globalFlags", {});
		if (name.startsWith('s:')) {
			name = name.substring(2);
			var statusBarItems = core.flags.statusBarItems.filter(function (v) { return v != name; });
			if (value) statusBarItems.push(name);
			core.flags.statusBarItems = statusBarItems;
			flags.statusBarItems = core.clone(statusBarItems);
		} else {
			flags[name] = core.flags[name] = value;
		}
		core.setFlag("globalFlags", flags);
		core.resize();
		core.drawMap();
	}
},
    "小游戏": function () {
	// 在此增加新插件
	control.prototype._replayAction_game = function (action) {
		if (!(action && action.startsWith('game:'))) return false;
		if (!core.getFlag('小游戏开关', false)) core.control._replay_error(action);
		// 匹配 game:物品:数量 格式（支持正负数）
		const match = action.match(/^game:([^:]+):(-?\d+)$/);
		if (match) {
			const item = match[1]; // 获取物品名称
			const count = Number(match[2]); // 获取数量并转为数字（支持负数）

			// 根据物品类型执行不同操作
			if (item === 'money') {
				core.addStatus('money', count); // 正数增加，负数减少
			} else {
				core.getItem(item, count);
				// count为0时不执行任何操作
			}

			// 记录操作并继续回放
			core.status.route.push(action);
			core.setFlag('玩小游戏', true);
			core.replay();
			return true;
		}

		// 格式错误处理
		core.control._replay_error(action);
	}

	core.control.registerReplayAction("game", control.prototype._replayAction_game);
},
    "录像验证": function () {
	// 在此增加新插件
	control.prototype._replayAction_verify = function (action) {
		if (!(action && action.startsWith('R'))) return false;
		if (!core.getFlag('调试模式')) {
			core.replay();
			return true;
		}
		const parts = action.substring(1).split(/[,&]/); // 去掉 'R' 并拆分
		const p = parts.slice(0, -1).map(Number); // 提取数值部分
		const f = parts[parts.length - 1]; // 获取最后一个元素
		const x = core.getStatus('x'),
			y = core.getStatus('y'),
			hp = core.getStatus('hp'),
			atk = core.getStatus('atk'),
			def = core.getStatus('def'),
			money = core.getStatus('money'),
			floorId = core.status.floorId;
		if (p[0] !== hp || p[1] !== atk || p[2] !== def || p[3] !== money) {
			core.ui.closePanel();
			core.status.replay.replaying = false;
			core.status.replay.failed = true;
			var len = core.status.replay.toReplay.length;
			var prevList = core.status.replay.totalList.slice(-len - 11, -len - 1);
			var nextList = core.status.replay.toReplay.slice(0, 10);
			main.log(`录像文件出错，\n录像属性为：(${p[0]}, ${p[1]}, ${p[2]}, ${p[3]})，\n而当前属性为(${hp}, ${atk}, ${def}, ${money})`);
			main.log("之前的10个操作是：\n" + prevList.toString());
			main.log("接下来10个操作是：\n" + nextList.toString());
			core.ui.drawConfirmBox(`录像文件出错，\n录像属性为：(${p[0]}, ${p[1]}, ${p[2]}, ${p[3]})，\n而当前属性为(${hp}, ${atk}, ${def}, ${money})`, function () {
				core.status.replay.failed = false;
				core.ui.closePanel();
				if (core.status.replay.save.length > 0) {
					core.status.replay.replaying = true;
					core.status.replay.pausing = true;
					core.rewindReplay();
				} else {
					core.playSound('操作失败');
					core.stopReplay(true);
					core.drawTip("无法回到上一个节点");
					//if (callback) callback();
				}
			}, function () {
				core.status.replay.failed = false;
				core.ui.closePanel();
				core.stopReplay(true);
				//if (callback) callback();
			});
		}
		if (p[4] !== x || p[5] !== y || f !== floorId) {
			core.ui.closePanel();
			core.status.replay.replaying = false;
			core.status.replay.failed = true;
			var len = core.status.replay.toReplay.length;
			var prevList = core.status.replay.totalList.slice(-len - 11, -len - 1);
			var nextList = core.status.replay.toReplay.slice(0, 10);
			main.log(`录像文件出错，\n录像坐标为：(${p[4]}, ${p[5]}, ${f})，\n而当前坐标为(${x}, ${y}, ${floorId})`);
			main.log("之前的10个操作是：\n" + prevList.toString());
			main.log("接下来10个操作是：\n" + nextList.toString());
			core.ui.drawConfirmBox(`录像文件出错，\n录像坐标为：(${p[4]}, ${p[5]}, ${f})，\n而当前坐标为(${x}, ${y}, ${floorId})`, function () {
				core.status.replay.failed = false;
				core.ui.closePanel();
				if (core.status.replay.save.length > 0) {
					core.status.replay.replaying = true;
					core.status.replay.pausing = true;
					core.rewindReplay();
				} else {
					core.playSound('操作失败');
					core.stopReplay(true);
					core.drawTip("无法回到上一个节点");
					//if (callback) callback();
				}
			}, function () {
				core.status.replay.failed = false;
				core.ui.closePanel();
				core.stopReplay(true);
				//if (callback) callback();
			});
		}
		core.replay();
		return true;
	}


	core.control.registerReplayAction("verify", control.prototype._replayAction_verify);


	events.prototype.doAction = function () {
		if (core.getFlag('调试模式', false) && core.status.floorId) {
			while (true) {
				var p = core.status.route.pop();
				if (!p) break;
				if (!p.startsWith('R')) {
					core.status.route.push(p);
					break;
				}
			}
			//console.log(core.status.route);
			core.status.route.push('R' + core.status.hero.hp + ',' + core.status.hero.atk + ',' + core.status.hero.def + ',' + core.status.hero.money + '&' + core.getStatus('x') + ',' + core.getStatus('y') + ',' + core.status.floorId);
		}
		// 清空boxAnimate和UI层
		clearInterval(core.status.event.interval);
		clearTimeout(core.status.event.interval);
		clearInterval(core.status.event.animateUI);
		core.status.event.interval = null;
		delete core.status.event.aniamteUI;
		if (core.status.gameOver || core.status.replay.failed) return;
		// 判定是否执行完毕
		if (this._doAction_finishEvents()) return;
		core.clearUI();
		var floorId = core.status.event.data.floorId || core.status.floorId;
		// 当前点坐标和前缀
		var x = core.status.event.data.x,
			y = core.status.event.data.y;
		var prefix = [floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@");
		var current = core.status.event.data.list[0];
		if (this._popEvents(current, prefix)) return;
		// 当前要执行的事件
		var data = current.todo.shift();
		core.status.event.data.current = data;
		if (typeof data == "string")
			data = { "type": "text", "text": data };
		// 该事件块已经被禁用
		if (data._disabled) return core.doAction();
		data.floorId = data.floorId || floorId;
		core.status.event.data.type = data.type;
		this.doEvent(data, x, y, prefix);
		return;
	}

	events.prototype._action_choices = function (data, x, y, prefix) {
		data.choices = data.choices.filter(function (x) {
			if (x._disabled) return false;
			if (x.condition == null || x.condition == '') return true;
			try { return core.calValue(x.condition, prefix); } catch (e) { return true; }
		})
		if (data.choices.length == 0) return this.doAction();
		if (core.isReplaying()) {
			var action = core.status.replay.toReplay.shift();
			if (action.startsWith('R')) {
				control.prototype._replayAction_verify(action);
				action = core.status.replay.toReplay.shift();
			}
			if (action.indexOf('choices:') == 0 && !(action == 'choices:none' && !data.timeout)) {
				var index = action.substring(8);
				if (!this.__action_choices_replaying(data, index)) {
					core.control._replay_error(action);
					return;
				}
			} else {
				// 容错录像

				if (main.replayChecking) {
					// 录像验证系统中选择第一项
					if (action != 'choices:none') core.status.replay.toReplay.unshift(action); // 首先归还刚才读出的下一步操作
					core.events.__action_choices_replaying(data, 0)
				} else {
					// 正常游戏中弹窗选择
					core.myprompt('录像回放出错！当前需要执行选择项但录像中未记录。\n如需修复请输入您要选的项（从0起），点击取消将不会修复。', 0, function (value) {
						if (value == null) {
							core.control._replay_error(action);
							return;
						}
						if (action != 'choices:none') core.status.replay.toReplay.unshift(action); // 首先归还刚才读出的下一步操作
						core.events.__action_choices_replaying(data, ((parseInt(value) || 0) + data.choices.length) % data.choices.length);
					});
				}
			}
		} else {
			if (data.timeout) {
				core.status.event.interval = setTimeout(function () {
					core.status.route.push("choices:none");
					core.setFlag('timeout', 0);
					core.doAction();
				}, data.timeout);
			}
			core.status.event.timeout = new Date().getTime() + (data.timeout || 0);
		}
		for (var i = 0; i < data.choices.length; i++) {
			if (typeof data.choices[i] === 'string')
				data.choices[i] = { "text": data.choices[i] };
			data.choices[i].text = core.replaceText(data.choices[i].text, prefix);
		}
		core.ui.drawChoices(core.replaceText(data.text, prefix), data.choices, data.width);
	}

	utils.prototype.decodeRoute = function (route) {
		if (!route) return route;

		// 解压缩
		try {
			var v = LZString.decompressFromBase64(route);
			if (v != null) {
				if (v != "" || route.length < 8)
					route = v;
			}
		} catch (e) {}

		var decodeObj = { route: route, index: 0, ans: [] };
		while (decodeObj.index < decodeObj.route.length) {
			this._decodeRoute_decodeOne(decodeObj, decodeObj.route.charAt(decodeObj.index++));
		}
		return decodeObj.ans;
	}

	core.registerEvent("superAutoSave", function (data, x, y, prefix) {
		var forbidSave = core.hasFlag('__forbidSave__');
		core.removeFlag('__forbidSave__');
		let d = core.clone(core.status.event.data);
		core.unshift(d.list[0].todo, { "type": "superAutoSave" });
		core.setFlag("__events__", d);
		if (core.saves.autosave.data == null) {
			core.saves.autosave.data = [];
		}
		core.saves.autosave.data.splice(core.saves.autosave.now, 0, core.saveData());
		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 (forbidSave) core.setFlag('__forbidSave__', true);
		if (!data.nohint) core.drawTip("已自动存档");
		core.doAction();
	})
},
    "图块彻底移除": 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 () {
	//战斗函数修复，避免与不存在的敌人战斗导致卡死
	events.prototype.battle = function (id, x, y, force, callback) {
		core.saveAndStopAutomaticRoute();
		id = id || core.getBlockId(x, y);
		if (!id || !core.material.enemys[id]) return core.clearContinueAutomaticRoute(callback);
		// 非强制战斗
		if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) {
			core.stopSound();
			core.playSound('操作失败');
			core.drawTip("你打不过此怪物！", id);
			return core.clearContinueAutomaticRoute(callback);
		}
		// 自动存档
		if (!core.status.event.id && x === core.nextX() && y === core.nextY()) core.autosave(true);
		// 战前事件
		if (!this.beforeBattle(id, x, y))
			return core.clearContinueAutomaticRoute(callback);
		// 战后事件
		this.afterBattle(id, x, y);
		if (callback) 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 && x === core.nextX() && y === core.nextY()) core.autosave(true);
			for (var keyName in keyInfo) {
				if (!keyName.endsWith(':o'))
					core.status.hero.items[core.material.items[keyName].cls][keyName] -= keyInfo[keyName];
			}
		}
		core.playSound(doorInfo.openSound);
		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));
	}
	//临界函数修复，现在会计入敌人的额外属性加成
	enemys.prototype._nextCriticals_overAtk = function (enemy, x, y, floorId) {
		var hero_atk = core.getRealStatusOrDefault(null, 'atk');
		var enemyInfo = core.enemys.getEnemyInfo(enemy, null, x, y, floorId);
		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) end = mid;
				else start = mid + 1;
			}
			var nextInfo = core.enemys.getDamageInfo(enemy, { "atk": start }, x, y, floorId);
			return nextInfo == null ? null : [start - hero_atk, nextInfo];
		}
		return calNext(hero_atk + 1,
			enemyInfo.hp + enemyInfo.def);
	}
	//临界计算现在会以当前勇士攻击力为准，也就是会计入buff
	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,
			pre = info.damage;
		var list = [];
		var start_atk = hero_atk;
		if (info.__over__) {
			start_atk += info.__overAtk__;
			list.push([info.__overAtk__, -info.damage]);
		}
		var calNext = function (currAtk, maxAtk) {
			var start = Math.floor(currAtk),
				end = Math.floor(maxAtk);
			if (start > end) return null;

			while (start < end) {
				var mid = Math.floor((start + end) / 2);
				if (mid - start > end - mid) mid--;
				var nextInfo = core.enemys.getDamageInfo(enemy, { "atk": mid }, x, y, floorId);
				if (nextInfo == null || (typeof nextInfo == 'number')) return null;
				if (pre > nextInfo.damage) end = mid;
				else start = mid + 1;
			}
			var nextInfo = core.enemys.getDamageInfo(enemy, { "atk": start }, x, y, floorId);
			return nextInfo == null || (typeof nextInfo == 'number') || nextInfo.damage >= pre ? null : [start, nextInfo.damage];
		}
		var currAtk = start_atk;
		while (true) {
			var next = calNext(currAtk + 1, mon_hp + mon_def, pre);
			if (next == null) break;
			currAtk = next[0];
			pre = next[1];
			list.push([currAtk - hero_atk, info.damage - pre]);
			if (pre <= 0 && !core.flags.enableNegativeDamage) break;
			if (list.length >= number) break;
		}
		if (list.length == 0) list.push([0, 0]);
		return list;
	}
	//自动寻路会智能避开楼梯
	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;
				}
				// 不可通行
				if (core.noPass(nx, ny)) continue;
				let block = core.getBlock(nx, ny);
				if (block && block.event.trigger === 'changeFloor') {
					var ignore = core.flags.ignoreChangeFloor;
					if (block.event.data && block.event.data.ignoreChangeFloor != null)
						ignore = block.event.data.ignoreChangeFloor;
					if (!ignore) 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;
	}
	//楼传操作页面按钮加大
	actions.prototype._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') {
			if ((x >= this.SIZE - 4 && x <= this.SIZE - 1) && ((y >= this.HSIZE - 1 && y <= this.HSIZE) || (y >= this.HSIZE + 2 && y <= this.HSIZE + 3))) {
				this._clickFly(x, y);
				return true;
			}
		}
		// 长按SL上下页快速翻页
		if (["save", "load", "replayLoad", "replayRemain", "replaySince"].indexOf(core.status.event.id) >= 0) {
			if ([this.HSIZE - 2, this.HSIZE - 3, this.HSIZE + 2, this.HSIZE + 3].indexOf(x) >= 0 && y == this.LAST) {
				this._clickSL(x, y);
				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;
	}
	//增加报错信息
	utils.prototype.calValue = function (value, prefix) {
		if (!core.isset(value)) return null;

		try {
			if (typeof value === 'string') {
				let original = value;
				if (value.indexOf(':') >= 0 || value.indexOf("flag：") >= 0 || value.indexOf('global：') >= 0) {
					if (value.indexOf('switch:') >= 0)
						value = value.replace(/switch:([a-zA-Z0-9_]+)/g, "core.getFlag('" + (prefix || ":f@x@y") + "@$1', 0)");
					value = this.replaceValue(value);
				}
				return eval(value);
			}
			if (value instanceof Function) {
				return value();
			}
			return value;
		} catch (e) {
			console.error("【calValue 错误】");
			console.error("原始值:", value);
			console.error("前缀:", prefix);
			console.error("错误信息:", e);
			throw e; // 或 return null; 看你是否希望继续运行
		}
	}
	//播放标题页面bgm
	control.prototype._playBgm_play = function (bgm, startTime) {
		if (core.musicStatus.playingBgm === bgm && !core.material.bgms[bgm].paused) return;

		if (core.musicStatus.playingBgm) {
			core.material.bgms[core.musicStatus.playingBgm].pause();
		}

		core.loader.loadBgm(bgm);
		const audio = core.material.bgms[bgm];
		audio.volume = core.musicStatus.userVolume * core.musicStatus.designVolume;
		audio.currentTime = startTime || 0;

		// 创建播放Promise并处理错误
		const playPromise = audio.play();
		if (playPromise !== undefined) {
			playPromise.then(() => {
				core.musicStatus.playingBgm = bgm;
				core.musicStatus.lastBgm = bgm;
			}).catch(e => {
				console.warn("播放失败:", e);

				if (e.name === 'NotAllowedError') {
					core.musicStatus.pendingBgm = { bgm, startTime };

					if (!core.userInteractionListenerAdded) {
						core.userInteractionListenerAdded = true;

						const playPendingBgm = () => {
							document.removeEventListener('click', playPendingBgm);
							core.userInteractionListenerAdded = false;

							const pending = core.musicStatus.pendingBgm;
							if (!pending) return;

							const audio = core.material.bgms[pending.bgm];
							audio.currentTime = pending.startTime || 0;

							// 添加重试播放逻辑
							const retryPlay = () => {
								audio.play().then(() => {
									core.musicStatus.playingBgm = pending.bgm;
									core.musicStatus.lastBgm = pending.bgm;
									core.musicStatus.pendingBgm = null;
								}).catch(retryError => {
									console.warn("重试播放失败:", retryError);
									if (retryError.name === 'NotAllowedError') {
										// 再次添加监听
										document.addEventListener('click', playPendingBgm);
										core.userInteractionListenerAdded = true;
									} else {
										core.musicStatus.pendingBgm = null;
									}
								});
							};

							retryPlay();
						};

						document.addEventListener('click', playPendingBgm);
					}
				} else {
					core.musicStatus.playingBgm = null;
				}
			});
		}
	};
	actions.prototype._keyUpViewMaps = function (keycode) {
		if (core.status.event.data == null) {
			core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId));
			return;
		}
		var floorId = core.floorIds[core.status.event.data.index];

		if (keycode == 27 || keycode == 13 || keycode == 32 || (!core.isReplaying() && keycode == 67)) {
			core.clearMap('data');
			core.playSound('取消');
			core.ui.closePanel();
			return;
		}
		if (keycode == 86) {
			core.status.event.data.damage = !core.status.event.data.damage;
			core.playSound('光标移动');
			core.ui._drawViewMaps(core.status.event.data);
			return;
		}
		if (keycode == 90) {
			core.status.event.data.all = !core.status.event.data.all;
			core.playSound('光标移动');
			core.ui._drawViewMaps(core.status.event.data);
			return;
		}
		if (keycode == 66) {
			if (core.markedFloorIds[floorId]) delete core.markedFloorIds[floorId];
			else core.markedFloorIds[floorId] = true;
			core.playSound('光标移动');
			core.ui._drawViewMaps(core.status.event.data);
			return;
		}
		if (keycode == 88 || (core.isReplaying() && keycode == 67)) {
			if (core.isReplaying()) {
				core.control._replay_book();
			} else {
				core.openBook(false);
			}
			return;
		}
		return;
	}
	/**
	 * 强制触发一个自动事件（无视其触发条件），并立即将其标记为“已执行”。
	 * @param {string} symbol 要强制触发的自动事件的唯一标识符 (symbol)。
	 * @param {boolean} [setExecuted=true] 是否要将此事件标记为“已执行”，以防止其再次自动触发。默认为 true。
	 */
	this.forceTriggerAutoEvent = function (x, y, f = core.status.floorId, n = 0, setExecuted) {
		if (setExecuted == null) setExecuted = true;
		const symbol = `${f}@${x}@${y}@${n}`;
		// 1. 在所有自动事件中查找具有指定 symbol 的事件
		var autoEvent = core.status.autoEvents.find(function (event) {
			return event.symbol === symbol;
		});

		// 如果没找到，就在控制台报错并退出
		if (!autoEvent) {
			console.error("错误：未找到 symbol 为 '" + symbol + "' 的自动事件。");
			return;
		}

		// 2. (可选) 将事件标记为“已执行”，这样 checkAutoEvents 就不会再触发它了
		if (setExecuted) {
			core.autoEventExecuted(symbol, true);
		}

		// 3. 准备要执行的事件内容，这部分逻辑和 checkAutoEvents 中完全一样
		var event;
		var x = autoEvent.x,
			y = autoEvent.y,
			floorId = autoEvent.floorId;

		if (x == null && y == null) {
			// 全局事件
			event = [
				{ "type": "dowhile", "condition": "false", "data": autoEvent.data }
			];
		} else {
			// 地图事件
			event = [
				{ "type": "function", "function": "function() { core.pushEventLoc(" + x + ", " + y + ", '" + floorId + "' ); }" },
				{ "type": "dowhile", "condition": "false", "data": autoEvent.data },
				{ "type": "function", "function": "function() { core.popEventLoc(); }" }
			];
		}

		// 4. 将事件插入到队列中立即执行，从而绕过所有条件检查
		core.insertAction(event);

		console.log("已成功强制触发事件 '" + symbol + "'。");
	}
	//录像中自动存档
	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);
	}
	//怪物手册中自定义怪物合并
	enemys.prototype._getCurrentEnemys_addEnemy = function (enemyId, enemys, used, x, y, floorId) {
		var enemy = this._getCurrentEnemys_getEnemy(enemyId);
		if (enemy == null) return;

		var id = enemy.id;

		var enemyInfo = this.getEnemyInfo(enemy, null, null, null, floorId);
		var locEnemyInfo = this.getEnemyInfo(enemy, null, x, y, floorId);

		// 定义需要比较的属性列表 (添加需要比较的新属性)
		var compareProps = ['atk', 'def', 'hp', 'money', 'exp', 'special', 'point']; // 示例属性

		// 检查属性是否全部相等
		var allPropsEqual = true;
		for (var i = 0; i < compareProps.length; i++) {
			var prop = compareProps[i];
			if (!(Object.is(locEnemyInfo[prop], enemyInfo[prop]))) {
				allPropsEqual = false;
				break;
			}
		}

		if (!core.flags.enableEnemyPoint || allPropsEqual) {
			x = null;
			y = null;
		} else {
			// 检查enemys中是否存在相同属性的怪物
			for (var i = 0; i < enemys.length; ++i) {
				var one = enemys[i];
				if (id == one.id && one.locs != null) {
					// 检查所有定义的属性
					var propsMatch = true;
					for (var j = 0; j < compareProps.length; j++) {
						var prop = compareProps[j];
						if (!Object.is(locEnemyInfo[prop], one[prop])) {
							propsMatch = false;
							break;
						}
					}

					if (propsMatch) {
						one.locs.push([x, y]);
						return;
					}
				}
			}
			enemyInfo = locEnemyInfo;
		}
		var id = enemy.id + ":" + x + ":" + y;
		if (used[id]) return;
		used[id] = true;

		var specialText = core.enemys.getSpecialText(enemy);
		var specialColor = core.enemys.getSpecialColor(enemy);

		var critical = this.nextCriticals(enemy, 1, x, y, floorId);
		if (critical.length > 0) critical = critical[0];

		var e = core.clone(enemy);
		for (var v in enemyInfo) {
			e[v] = enemyInfo[v];
		}
		if (x != null && y != null) {
			e.locs = [
				[x, y]
			];
		}
		e.name = core.getEnemyValue(enemy, 'name', x, y, floorId);
		e.specialText = specialText;
		e.specialColor = specialColor;
		e.damage = this.getDamage(enemy, x, y, floorId);
		e.critical = critical[0];
		e.criticalDamage = critical[1];
		e.defDamage = this._getCurrentEnemys_addEnemy_defDamage(enemy, x, y, floorId);
		enemys.push(e);
	}
	//加防改为一防
	ui.prototype._drawBook_drawRow3 = function (index, enemy, top, left, width, position) {
		// 绘制第三行
		core.setTextAlign('ui', 'left');
		var b13 = this._buildFont(13, true),
			f13 = this._buildFont(13, false);
		var col1 = left,
			col2 = left + width * 9 / 25,
			col3 = left + width * 17 / 25;
		core.fillText('ui', '临界', col1, position, '#DDDDDD', f13);
		core.fillText('ui', core.formatBigNumber(enemy.critical || 0), col1 + 30, position, null, b13);
		core.fillText('ui', '减伤', col2, position, null, f13);
		core.fillText('ui', core.formatBigNumber(enemy.criticalDamage || 0), col2 + 30, position, null, b13);
		core.fillText('ui', '1防', col3, position, null, f13);
		core.fillText('ui', core.formatBigNumber(enemy.defDamage || 0), col3 + 30, position, null, b13);
	}
	enemys.prototype._getCurrentEnemys_addEnemy_defDamage = function (enemy, x, y, floorId) {
		return this.getDefDamage(enemy, 1, x, y, floorId);
	}
	//拾取道具前存档
	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.getFlag('道具存档', false) && core.getBlockCls(core.nextX(), core.nextY()) === 'items') core.autosave();
		this._moveAction_moving(callback);
	}
	//使用道具前存档
	items.prototype.useItem = function (itemId, noRoute, callback) {
		if (!this.canUseItem(itemId)) {
			if (callback) callback();
			return;
		}
		if (core.material.items[itemId].cls === 'tools') {
			if (noRoute) core.autosave(true);
			else core.autosave(false);
		}
		// 执行道具效果
		this._useItemEffect(itemId);
		// 执行完毕
		this._afterUseItem(itemId);
		// 记录路线
		if (!noRoute) core.status.route.push("item:" + itemId);
		if (callback) callback();
	}
},
    "性能优化": function () {
	core.ui.clearMap = function (name, x, y, width, height) {
		if (name == 'all') {
			for (var m in core.canvas) {
				core.canvas[m].clearRect(0, 0, core.bigmap.width * 32, core.bigmap.height * 32);
			}
			core.clearMap("outerUI");
			core.dom.gif.innerHTML = "";
			core.removeGlobalAnimate();
			core.deleteCanvas(function (one) { return one.startsWith('_bigImage_'); });

		} else {
			var ctx = this.getContextByName(name);
			if (ctx) ctx.clearRect(x || 0, y || 0, width || ctx.canvas.width, height || ctx.canvas.height);
			if (name === 'outerUI') {
				delete core.status.lastPropsDrawn;
				delete core.status.lastItemsDrawn;
				delete core.status.lastItemGridCache;
				delete core.status.lastEquipsDrawn;
				delete core.status.lastKeysDrawn;
			}
		}
	}
	control.prototype.updateStatusBar = function (doNotCheckAutoEvents) {
		if (!core.isPlaying() || core.hasFlag('__statistics__')) return;
		this.controldata.updateStatusBar();
		if (!doNotCheckAutoEvents) core.checkAutoEvents();
		this._updateStatusBar_setToolboxIcon();
		core.clearRouteFolding();
	}
	//顺带关闭预览模式
	actions.prototype._sys_ondown = function (x, y, px, py) {
		if (core.status.lockControl) return false;
		core.status.downTime = new Date();
		core.clearMap('route', 0, 0, 352, 352);
		var pos = { 'x': parseInt((px + core.bigmap.offsetX) / 32), 'y': parseInt((py + core.bigmap.offsetY) / 32) };
		core.status.stepPostfix = [];
		core.status.stepPostfix.push(pos);
		core.fillRect('ui', pos.x * 32 + 12 - core.bigmap.offsetX, pos.y * 32 + 12 - core.bigmap.offsetY, 8, 8, '#bfbfbf');

		clearTimeout(core.timeout.onDownTimeout);
		core.timeout.onDownTimeout = null;
		core.status.preview.prepareDragging = false;
		/*if (!core.hasFlag('__lockViewport__') && (core.status.thisMap.width > core.__SIZE__ || core.status.thisMap.height > core.__SIZE__)) {
		    core.status.preview.prepareDragging = true;
		    core.status.preview.px = px;
		    core.status.preview.py = py;
		    core.timeout.onDownTimeout = setTimeout(function () {
		        core.clearMap('ui');
		        core.status.preview.prepareDragging = false;
		        core.status.preview.enabled = true;
		        core.status.preview.dragging = true;
		        core.drawTip('已进入预览模式，可直接拖动大地图');
		        core.status.stepPostfix = [];
		    }, 500);
		} */
	}
	control.prototype.clearAutomaticRouteNode = function (x, y) {
		core.clearMap('route', x * 32 + 5 - core.status.automaticRoute.offsetX, y * 32 + 5 - core.status.automaticRoute.offsetY, 27, 27);
	}
	control.prototype.stopAutomaticRoute = function () {
		if (!core.status.played) return;
		core.status.automaticRoute.autoHeroMove = false;
		core.status.automaticRoute.autoStep = 0;
		core.status.automaticRoute.destStep = 0;
		core.status.automaticRoute.movedStep = 0;
		core.status.automaticRoute.autoStepRoutes = [];
		core.status.automaticRoute.destX = null;
		core.status.automaticRoute.destY = null;
		core.status.automaticRoute.lastDirection = null;
		core.status.heroStop = true;
		if (core.status.automaticRoute.moveStepBeforeStop.length == 0)
			core.clearMap('route', 0, 0, 352, 352);
	}
	control.prototype._setAutomaticRoute_drawRoute = function (moveStep) {
		// 计算绘制区域的宽高，并尽可能小的创建route层
		var sx = core.bigmap.width * 32,
			sy = core.bigmap.height * 32,
			dx = 0,
			dy = 0;
		moveStep.forEach(function (t) {
			sx = Math.min(sx, t.x * 32);
			dx = Math.max(dx, t.x * 32);
			sy = Math.min(sy, t.y * 32);
			dy = Math.max(dy, t.y * 32);
		});
		core.status.automaticRoute.offsetX = sx;
		core.status.automaticRoute.offsetY = sy;
		var ctx = core.createCanvas('route', sx - core.bigmap.offsetX, sy - core.bigmap.offsetY, dx - sx + 32, dy - sy + 32, 95);
		ctx.fillStyle = '#bfbfbf';
		ctx.strokeStyle = '#bfbfbf';
		ctx.lineWidth = 8;
		for (var m = 0; m < moveStep.length; m++) {
			if (m == moveStep.length - 1) {
				ctx.fillRect(moveStep[m].x * 32 + 10 - sx, moveStep[m].y * 32 + 10 - sy, 12, 12);
			} else {
				ctx.beginPath();
				var cx = moveStep[m].x * 32 + 16 - sx,
					cy = moveStep[m].y * 32 + 16 - sy;
				var currDir = moveStep[m].direction,
					nextDir = moveStep[m + 1].direction;
				ctx.moveTo(cx - core.utils.scan[currDir].x * 11, cy - core.utils.scan[currDir].y * 11);
				ctx.lineTo(cx, cy);
				ctx.lineTo(cx + core.utils.scan[nextDir].x * 11, cy + core.utils.scan[nextDir].y * 11);
				ctx.stroke();
			}
		}
	}
},
    "键盘操作优化": function () {

	actions.prototype._clickSwitchs_action_moveSpeed = function (delta) {
		core.values.moveSpeed = core.clamp(core.values.moveSpeed + delta, 1, 200);
		core.setLocalStorage("moveSpeed", core.values.moveSpeed);
		core.ui._drawSwitchs_action();
	}

	events.prototype.eventMoveHero = function (steps, time, callback) {
		time = time || core.values.moveSpeed;
		// 1. 解析所有指令为 {dir, count} 结构
		var moveSteps = (steps || []).map(function (t) {
			var parts = t.split(':');
			return {
				dir: parts[0],
				count: Math.max(1, parseInt(parts[1], 10) || 1)
			};
		}).filter(function (ms) {
			// 只保留合法指令
			return ['up', 'down', 'left', 'right', 'forward', 'backward',
					'leftup', 'leftdown', 'rightup', 'rightdown', 'speed'
				]
				.includes(ms.dir) &&
				!(ms.dir === 'speed' && ms.count < 16);
		});

		if (moveSteps.length === 0) {
			if (callback) callback();
			return;
		}

		core.status.heroMoving = -1;
		var subStep = 0;
		var interval = window.setInterval(function () {
				// 先看是不是 speed 指令，如果是且刚开始该指令，就调整整体速度
				if (moveSteps[0].dir === 'speed' && subStep === 0) {
					time = moveSteps[0].count;
					moveSteps.shift();
					// 如果 speed 是最后一条，直接结束
					if (moveSteps.length === 0) {
						clearInterval(interval);
						delete core.animateFrame.asyncId[interval];
						if (callback) callback();
						return;
					}
				}

				// 执行一次微步动画
				if (core.events._eventMoveHero_moving(++subStep, [
						[moveSteps[0].dir, moveSteps[0].count]
					])) {
					// 这一格走完
					subStep = 0;
					core.status.heroMoving = 0;
					core.drawHero();

					// 普通方向指令：减少 count，走完就出队
					if (moveSteps[0].dir !== 'speed') {
						moveSteps[0].count--;
						if (moveSteps[0].count <= 0) {
							moveSteps.shift();
						}
					}

					// 队列空了，就结束
					if (moveSteps.length === 0) {
						clearInterval(interval);
						delete core.animateFrame.asyncId[interval];
						if (callback) callback();
					}
				}
			}, core.status.replay.speed === 24 ?
			1 :
			time / 8 / core.status.replay.speed);

		core.animateFrame.lastAsyncId = interval;
		core.animateFrame.asyncId[interval] = function () {};
	};


	core.control._moveHero_moving = function () {
		core.status.heroStop = false;
		core.status.automaticRoute.moveDirectly = false;

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

			// —— 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
				);
			}
			// —— 自动寻路：改回原始模式 —— 
			else if (core.status.automaticRoute.autoStepRoutes.length > 0) {
				// 直接走一步，然后 50ms 后继续下一步
				core.moveAction();
				setTimeout(move, 50);
			}
			// —— 手动按键控制：只走一步，结束 —— 
			else {
				core.moveAction();
				core.status.heroStop = true;
			}
		};

		move();
	};

	// 改成新的方向键处理逻辑
	actions.prototype._sys_onkeyDown = function (e) {
		core.status.holdingKeys = core.status.holdingKeys || [];
		var isArrow = { 37: true, 38: true, 39: true, 40: true } [e.keyCode];

		if (isArrow && !core.status.lockControl) {
			if (e.preventDefault) e.preventDefault();

			// 新增：记录是否为新按键
			var isNewKey = !core.status.holdingKeys.includes(e.keyCode);
			if (isNewKey) {
				core.status.holdingKeys.push(e.keyCode);
			}

			// 新增：新按下方向键时立即触发一次移动
			if (isNewKey) {
				this.keyDown(e.keyCode);
			}

			if (!core.status._pressTimer) {
				core.status._pressTimer = setInterval(() => {
					if (!core.status.holdingKeys.length) {
						clearInterval(core.status._pressTimer);
						core.status._pressTimer = null;
						return;
					}
					var keyCode = core.status.holdingKeys.slice(-1)[0];
					this.keyDown(keyCode); // 处理后续长按
				}, 50);
			}
		} else {
			if (e.keyCode === 17) core.status.ctrlDown = true;
			this.keyDown(e.keyCode, e.altKey);
		}
	};

	actions.prototype._sys_onkeyUp = function (e) {
		var isArrow = { 37: true, 38: true, 39: true, 40: true } [e.keyCode];
		if (isArrow && !core.status.lockControl) {
			var idx = core.status.holdingKeys.indexOf(e.keyCode);
			if (idx >= 0) core.status.holdingKeys.splice(idx, 1);

			if (e.preventDefault) e.preventDefault();

			this.keyUp(e.keyCode, e.altKey);
		} else {
			if (e.keyCode === 17) core.status.ctrlDown = false;
			this.keyUp(e.keyCode, e.altKey);
		}
	};

	// 删除了 _sys_fastPressLoop，不再需要！

	// 注册（注意：这里不再注册 _sys_fastPressLoop 了）
	core.actions.registerAction('onkeyDown', '_sys_onkeyDown', core.actions._sys_onkeyDown, 0);
	core.actions.registerAction('pressKey', '_sys_pressKey', core.actions._sys_pressKey, 0);
	core.actions.registerAction('onkeyUp', '_sys_onkeyUp', core.actions._sys_onkeyUp, 0);

},
    "缩略图优化": function () {
	// —— 初始化 tempCanvas，只需一次 —— 
	core.bigmap.tempCanvas = (function () {
		const canvas = document.createElement('canvas');
		const ctx = canvas.getContext('2d');
		return ctx;
	})();

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

		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 = 11;
		height = 11;

		// 设置临时画布的物理尺寸
		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;

		// 绘制时平移坐标到(1,1)格位置
		tempCanvas.save();
		tempCanvas.translate(-32, -32); // 注意这里已经是缩放后的坐标

		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) {
		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.scale * devicePixelRatio;

		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 // 目标画布
		);
	};

},
    "ATRI": function () {
	// 在此增加新插件

	// 请在此手动输入加攻击、防御、护盾的物品的id和增加值
	// 区别在于，宝石的属性的增加值需要乘以地图倍率ratio，剑盾不用
	// 如果想要自动判断，可以参考血瓶宝石显示数据的写法->eval(item.itemEffect)修改本插件
	var itemList = {},
		equipList = { 'sword1': { 'atk': 10 }, 'sword2': { 'atk': 20 }, 'shield1': { 'def': 10 }, 'shield2': { 'def': 20 } },
		keyList = { 'yellowKey': { 'yellowKey': 1 }, 'blueKey': { 'blueKey': 1 }, 'redKey': { 'redKey': 1 }, 'greenKey': { 'greenKey': 1 } },
		walkList = { 'upPortal': 1, 'downPortal': 1, 'leftPortal': 1, 'rightPortal': 1 },
		pointList = { 'atk': 1, 'def': 2, 'mdef': 16, 'hp': 80 };

	var need = [];
	var lvUp = [];

	const maxNum = 310;
	// 怪物列表的最大值，可修改。

	function dp_init() {
		itemList['redGem'] = { 'atk': core.values.redGem };
		itemList['blueGem'] = { 'def': core.values.blueGem };
		itemList['greenGem'] = { 'mdef': core.values.greenGem };
		itemList['yellowGem'] = { 'atk': core.values.redGem, 'def': core.values.blueGem, 'mdef': core.values.greenGem }
		itemList['redPotion'] = { 'hp': core.values.redPotion };
		itemList['bluePotion'] = { 'hp': core.values.bluePotion };
		itemList['yellowPotion'] = { 'hp': core.values.yellowPotion };
		itemList['greenPotion'] = { 'hp': core.values.greenPotion };
		//      itemList['I440'] = { 'atk': 2 };
		//      itemList['I441'] = { 'def': 2 };
		//      itemList['I442'] = { 'mdef': 5 };
		//      itemList['I443'] = { 'atk': 2, 'def': 2, 'mdef': 5 };
		//      itemList['I444'] = { 'atk': 3 };
		//      itemList['I445'] = { 'def': 3 };
		//      itemList['I446'] = { 'mdef': 8 };
		//      itemList['I447'] = { 'atk': 3, 'def': 3, 'mdef': 8 };
		//      itemList['I448'] = { 'hp': 800 };
		//      itemList['I449'] = { 'hp': 1000 };
		//      itemList['I450'] = { 'hp': 2000 };
		//      itemList['I451'] = { 'hp': 1500 };

		let lvArray = core.firstData.levelUp;
		let n = core.firstData.levelUp.length - 1;
		for (let i = 1; i <= n; i++) {
			//          console.log(lvArray[i]);
			need.push(~~lvArray[i].need);
			let act = lvArray[i].action;
			let hp = 0,
				atk = 0,
				def = 0,
				mdef = 0;
			for (let x of act) {
				if (x.name === 'status:hp') hp += ~~x.value;
				else if (x.name === 'status:atk') atk += ~~x.value;
				else if (x.name === 'status:def') def += ~~x.value;
				else if (x.name === 'status:mdef') mdef += ~~x.value;
			}
			lvUp.push({ hp: hp, atk: atk, def: def, mdef: mdef });
		}
	}

	class Node {
		constructor(type = 'item', id = '-1', x = 0, y = 0, floorId = 0, hp = 0, atk = 0, def = 0, mdef = 0, exp = 0, yellowKey = 0, blueKey = 0, redKey = 0, greenKey = 0) {
			this.type = type;
			this.id = id;
			this.x = x;
			this.y = y;
			this.floorId = floorId;
			this.hp = hp;
			this.atk = atk;
			this.def = def;
			this.mdef = mdef;
			this.exp = exp;
			this.yellowKey = yellowKey;
			this.blueKey = blueKey;
			this.redKey = redKey;
			this.greenKey = greenKey;
			this.edge = [];
		}
		addedge(target) {
			if (this.edge.indexOf(target) > -1) return;
			this.edge.push(target);
		}
		print(message) {
			console.log(message);
			console.log("type: " + this.type);
			console.log("x: " + this.x);
			console.log("y: " + this.y);
			console.log("floorId: " + this.floorId);
			console.log("edge: " + this.edge);
			console.log("atk: " + this.atk);
			console.log("def: " + this.def);
			console.log("mdef: " + this.mdef);
			console.log("hp: " + this.hp);
		}
	}

	var node = [];
	var visited = {};
	var visFloor = {};
	var cnt = 0;

	function calcFloor(floorId, currfloor) {
		floorId = floorId || currfloor;
		if (floorId == ':before') {
			var index = core.floorIds.indexOf(currfloor);
			if (index > 0) floorId = core.floorIds[index - 1];
			else floorId = currfloor;
		} else if (floorId == ':next') {
			var index = core.floorIds.indexOf(currfloor);
			if (index < core.floorIds.length - 1) floorId = core.floorIds[index + 1];
			else floorId = currfloor;
		} else if (floorId == ':now') {
			floorId = currfloor;
		}
		if (!core.status.maps[floorId]) {
			console.error("不存在的楼层：" + floorId);
			return null;
		}

		return floorId
	}

	function calcLoc(floorId, stair, heroLoc) {
		if (stair) {
			// --- 对称
			if (stair == ':now');
			else if (stair == ':symmetry') {
				heroLoc.x = core.bigmap.width - 1 - heroLoc.x;
				heroLoc.y = core.bigmap.height - 1 - heroLoc.y;
			} else if (stair == ':symmetry_x')
				heroLoc.x = core.bigmap.width - 1 - heroLoc.x;
			else if (stair == ':symmetry_y')
				heroLoc.y = core.bigmap.height - 1 - heroLoc.y;
			// 检查该层地图的 upFloor & downFloor & flyPoint
			else if (core.status.maps[floorId][stair]) {
				heroLoc.x = core.status.maps[floorId][stair][0];
				heroLoc.y = core.status.maps[floorId][stair][1];
			} else {
				core.extractBlocks(floorId);
				var blocks = core.status.maps[floorId].blocks;
				for (var i in blocks) {
					if (!blocks[i].disable && blocks[i].event.id === stair) {
						heroLoc.x = blocks[i].x;
						heroLoc.y = blocks[i].y;
						break;
					}
				}
			}
		}
		['x', 'y', 'direction'].forEach(function (name) {
			if (heroLoc[name] == null)
				heroLoc[name] = core.getHeroLoc(name);
		});
		return heroLoc;
	}

	function bfsFlood(X) {
		node = [];
		visited = {};
		visFloor = {};
		cnt = 0;

		let canMoveArray = {};
		let blocksObj = {};
		let bgMap = {};
		let eventMap = {};
		let floorMap = {};

		for (let xy of X) {
			const now = xy.split(","),
				x = ~~now[0],
				y = ~~now[1],
				floorId = now[2];
			const id = core.getBlockId(x, y, floorId),
				cls = core.getBlockCls(x, y, floorId);
			if (cls === 'enemys' || cls === 'enemy48') {
				visited[xy] = cnt;
				node[cnt++] = new Node('enemy', id, x, y, floorId, 0, 0, 0, 0, 0, 0, 0, 0);
			} else {
				visited[xy] = cnt;
				node[cnt++] = new Node(id, id, x, y, floorId, 0, 0, 0, 0, 0, 0, 0, 0);
			}
		}

		let atk = 0,
			def = 0,
			mdef = 0,
			hp = 0,
			yellowKey = 0,
			blueKey = 0,
			redKey = 0,
			greenKey = 0;

		let canPick = {};

		const blockfn = function (x, y, floorId) {
			const checkBlockInfo = core.status.checkBlock;
			let idx = x + ',' + y;
			let blk = blocksObj[floorId][idx];

			if (blk && !blk.disable && !core.isMapBlockDisabled(floorId, blk.x, blk.y)) {
				const id = blk.event.id,
					ratio = core.status.maps[floorId].ratio;
				if (itemList.hasOwnProperty(id)) {
					if (!((idx + floorId) in canPick)) {
						atk += itemList[id].hasOwnProperty('atk') ? ratio * itemList[id].atk : 0;
						def += itemList[id].hasOwnProperty('def') ? ratio * itemList[id].def : 0;
						mdef += itemList[id].hasOwnProperty('mdef') ? ratio * itemList[id].mdef : 0;
						hp += itemList[id].hasOwnProperty('hp') ? ratio * itemList[id].hp : 0;
						canPick[idx + floorId] = 1;
					}
					return !checkBlockInfo.damage[idx] && !checkBlockInfo.ambush[idx] && !checkBlockInfo.repulse[idx];
				} else if (equipList.hasOwnProperty(id)) {
					if (!((idx + floorId) in canPick)) {
						atk += equipList[id].hasOwnProperty('atk') ? equipList[id].atk : 0;
						def += equipList[id].hasOwnProperty('def') ? equipList[id].def : 0;
						mdef += equipList[id].hasOwnProperty('mdef') ? equipList[id].mdef : 0;
						canPick[idx + floorId] = 1;
					}
					return !checkBlockInfo.damage[idx] && !checkBlockInfo.ambush[idx] && !checkBlockInfo.repulse[idx];
				} else if (keyList.hasOwnProperty(id)) {
					if (!((idx + floorId) in canPick)) {
						yellowKey += keyList[id].hasOwnProperty('yellowKey') ? keyList[id].yellowKey : 0;
						blueKey += keyList[id].hasOwnProperty('blueKey') ? keyList[id].blueKey : 0;
						redKey += keyList[id].hasOwnProperty('redKey') ? keyList[id].redKey : 0;
						greenKey += keyList[id].hasOwnProperty('greenKey') ? keyList[id].greenKey : 0;
						canPick[idx + floorId] = 1;
					}
					return !checkBlockInfo.damage[idx] && !checkBlockInfo.ambush[idx] && !checkBlockInfo.repulse[idx];
				}
			}
			return core.maps._canMoveDirectly_checkNextPoint(blocksObj[floorId], x, y, floorId) || ((x + "," + y) in floorMap[floorId]) || blk.event.id in walkList;
		}

		for (let xy1 of X) {
			const now1 = xy1.split(","),
				x1 = ~~now1[0],
				y1 = ~~now1[1],
				floorId1 = now1[2];
			if (!(floorId1 in visFloor)) {
				visFloor[floorId1] = 1;
				canMoveArray[floorId1] = core.maps.generateMovableArray(floorId1);
				blocksObj[floorId1] = core.maps.getMapBlocksObj(floorId1);
				bgMap[floorId1] = core.maps.getBgMapArray(floorId1);
				eventMap[floorId1] = core.maps.getMapArray(floorId1);
				floorMap[floorId1] = core.floors[floorId1].changeFloor;
			}
			for (let dir in core.utils.scan) {
				//              console.log(xy1);
				atk = 0,
					def = 0,
					mdef = 0,
					hp = 0,
					yellowKey = 0,
					blueKey = 0,
					redKey = 0,
					greenKey = 0;

				let queue = [];

				const nx = x1 + core.utils.scan[dir].x,
					ny = y1 + core.utils.scan[dir].y,
					nindex = nx + "," + ny + "," + floorId1;
				if (nindex in visited) {
					let u = visited[xy1],
						v = visited[nindex];
					if (u === v) continue;
					node[u].addedge(v);
					node[v].addedge(u);
					continue;
				}
				if (!core.inArray(canMoveArray[floorId1][x1][y1], dir)) continue;
				if (core.onSki(bgMap[floorId1][ny][nx])) continue;
				if (blockfn && !blockfn(nx, ny, floorId1)) continue;
				visited[nindex] = cnt;
				node[cnt] = new Node();
				queue.push(nindex);

				while (queue.length > 0) {
					const xy = queue.shift();
					const now = xy.split(","),
						x = ~~now[0],
						y = ~~now[1],
						floorId = now[2];
					//                  console.log(xy);
					if (!(floorId in visFloor)) {
						visFloor[floorId] = 1;
						canMoveArray[floorId] = core.maps.generateMovableArray(floorId);
						blocksObj[floorId] = core.maps.getMapBlocksObj(floorId);
						bgMap[floorId] = core.maps.getBgMapArray(floorId);
						eventMap[floorId] = core.maps.getMapArray(floorId);
						floorMap[floorId] = core.floors[floorId].changeFloor;
					}
					if ((x + "," + y) in floorMap[floorId]) {
						let now = floorMap[floorId][x + "," + y];
						//                      console.log(now);
						const tfloorId = calcFloor(now.floorId, floorId)
						let txy = calcLoc(tfloorId, now.stair, { x: x, y: y });
						//                      console.log(txy);
						if (now.floorId != ':next' && now.floorId != ':before' && now.floorId != ':now' && 'loc' in now) txy = { x: now.loc[0], y: now.loc[1] };
						const tx = txy.x,
							ty = txy.y,
							nindex = tx + "," + ty + "," + tfloorId;
						//                      console.log(nindex);
						if (nindex in visited) {
							let u = visited[xy],
								v = visited[nindex];
							if (u !== v) {
								node[u].addedge(v);
								node[v].addedge(u);
							}
						} else {
							//                          console.log(xy + " " + nindex);
							visited[nindex] = visited[xy];
							queue.push(nindex);
						}
					}
					for (let direction in core.utils.scan) {
						const nx = x + core.utils.scan[direction].x,
							ny = y + core.utils.scan[direction].y,
							nindex = nx + "," + ny + "," + floorId;
						if (nindex in visited) {
							let u = visited[xy],
								v = visited[nindex];
							if (u === v) continue;
							node[u].addedge(v);
							node[v].addedge(u);
							continue;
						}
						if (!core.inArray(canMoveArray[floorId][x][y], direction)) continue;
						if (core.onSki(bgMap[floorId][ny][nx])) continue;
						if (blockfn && !blockfn(nx, ny, floorId)) continue;
						visited[nindex] = visited[xy];
						queue.push(nindex);
					}
				}

				node[cnt].hp = hp;
				node[cnt].atk = atk;
				node[cnt].def = def;
				node[cnt].mdef = mdef;
				node[cnt].yellowKey = yellowKey;
				node[cnt].blueKey = blueKey;
				node[cnt].redKey = redKey;
				node[cnt].greenKey = greenKey;
				node[cnt].addedge(visited[xy1]);
				node[visited[xy1]].addedge(cnt);
				cnt++;
			}
		}
		//      console.log("cnt: " + cnt);
		//      for (let i = 0; i < cnt; i++) node[i].print(i);
		//      for (let i in visited) {
		//          console.log(i + " " + visited[i]);
		//      }
	}

	function findpos(ele, arr) {
		for (let i = 0, l = arr.length; i < l; i++) {
			if (arr[i].x === ele.x && arr[i].y === ele.y && arr[i].floorId === ele.floorId) return i;
		}
		return -1;
	}

	function drawSetting(floorId, ctx) {
		core.createCanvas(ctx, 0, 0, 416, 416, 100);
		core.clearMap(ctx);
		core.drawThumbnail(floorId, null, { ctx: ctx, damage: true, 'centerX': 0, 'centerY': 0 });
		core.setAlpha(ctx, 0.5);
		core.fillText(ctx, '点击选中敌人，↑↓调整楼层，esc/回车确认', 10, 15, 'red', 'bold 14px Verdana');
		core.setAlpha(ctx, 1);
		let enemyList = core.getFlag('enemyList', []);
		let enemyChooseList = core.getFlag('enemyChooseList', []);
		for (let currEnemy of enemyList) {
			if (currEnemy.floorId === floorId) {
				if (findpos(currEnemy, enemyChooseList) != -1)
					core.strokeRect(ctx, (currEnemy.x - 1) * 32, (currEnemy.y - 1) * 32, 32, 32, "orangered");
				else
					core.strokeRect(ctx, (currEnemy.x - 1) * 32, (currEnemy.y - 1) * 32, 32, 32, "chartreuse");
			}
		}
	}

	function drawChoice() {
		core.setFlag('pick_button', '0');
		core.setFlag('enemyList', []);
		core.setFlag('enemyChooseList', []);
		const ctx = 'choose',
			name = 'chooseEnemy';
		let currFloorId = core.status.floorId,
			index;
		drawSetting(currFloorId, ctx);
		return new Promise(function (res) {

			function keyup(keycode) {
				// 处理按键事件
				switch (keycode) {
				case 13:
				case 32:
				case 67: //回车，空格，C键
				case 27: //Esc
					cancel();
					break;
				case 38: // 上
					index = core.floorIds.indexOf(currFloorId);
					if (index < core.floorIds.length - 1) index++;
					currFloorId = core.floorIds[index];
					drawSetting(currFloorId, ctx);
					break;
				case 40: // 下
					index = core.floorIds.indexOf(currFloorId);
					if (index > 0) index--;
					currFloorId = core.floorIds[index];
					drawSetting(currFloorId, ctx);
					break;
				}
			}

			function click(x, y, px, py) {
				x += 1;
				y += 1;
				// 处理点击事件
				const id = core.getBlockId(x, y, currFloorId),
					cls = core.getBlockCls(x, y, currFloorId);
				if (cls !== 'enemys' && cls !== 'enemy48' && id !== 'yellowDoor' && id !== 'blueDoor' && id !== 'redDoor' && id !== 'greenDoor') {
					core.playSound('error.mp3');
					core.drawTip('错误：该点不是敌人或门');
				} else {
					let enemyList = core.getFlag('enemyList', []);
					let enemyChooseList = core.getFlag('enemyChooseList', []);
					if (enemyList.length > maxNum) {
						core.playSound('error.mp3');
						core.drawTip('错误：目标数量达到上限');
					}
					let currEnemy = { x: x, y: y, id: id, floorId: currFloorId, type: 'enemy' };
					if (cls !== 'enemys' && cls !== 'enemy48') currEnemy.type = id;
					if (findpos(currEnemy, enemyList) == -1) {
						enemyList.push(currEnemy);
						drawSetting(currFloorId, ctx);
					} else if (findpos(currEnemy, enemyChooseList) == -1) {
						enemyChooseList.push(currEnemy);
						drawSetting(currFloorId, ctx);
					} else {
						enemyList.splice(findpos(currEnemy, enemyList), 1);
						enemyChooseList.splice(findpos(currEnemy, enemyChooseList), 1);
						drawSetting(currFloorId, ctx);
					}
				}
			}

			function finish(confirm) {
				// 清除画面，销毁事件监听器
				core.unregisterAction('onclick', name);
				core.unregisterAction('keyUp', name);
				// 等待本次事件处理完全结束
				res(confirm);
			}

			function cancel() {
				core.clearMap(ctx);
				finish(false);
			}

			core.registerAction('ondown', name, click, 100);
			core.registerAction('keyUp', name, keyup, 100);
		});
	}

	this.openChoiceScreen = function () {
		if (core.isReplaying()) return;
		core.setFlag('noOpenMenu', true);
		//禁止Esc打开菜单栏
		core.lockControl();

		drawChoice().then(() => {
			core.unlockControl();
			core.setFlag('noOpenMenu', false);

			core.plugin.getEnemyList();
		});
	}

	function log2(x) {
		return Math.log(x) / Math.log(2);
	}

	var sx = [];
	var op = [];

	function drawAnswer(floorId) {
		if (core.getFlag('pick_button') === '0') return;
		let enemyList = core.getFlag('enemyList', []);
		for (let i = sx.length - 1; i >= 0; i--)
			if (enemyList[sx[i] - 1].floorId === floorId) {
				let tmp = '';
				if (op.length == 0) tmp = '';
				else if (op[i] == 1) tmp = '攻';
				else if (op[i] == 2) tmp = '防';
				else if (op[i] == 3) tmp = '魔';
				else if (op[i] == 4) tmp = '血';
				else tmp = '';
				core.fillBoldText("data", (sx.length - i) + tmp, (enemyList[sx[i] - 1].x - 1) * 32 + 8, (enemyList[sx[i] - 1].y - 1) * 32 + 8, "lime");
			}
	}

	this.drawAnswer = drawAnswer;

	this.getEnemyList = function () {
		var time1 = new Date().getTime();
		core.clearMap("fg")
		let text = '';
		let enemyList = core.getFlag('enemyList', []);

		if (enemyList.length === 0) text = '未选择敌人';
		let tmp = dp();
		let ans = tmp.ans;
		console.log(ans);
		if (ans <= 0) {
			core.insertAction(text);
			core.insertAction("打不过！");
			return;
		}
		core.insertAction(text);
		core.insertAction("计算结果为：" + ans);
		sx = [];
		op = [];
		let resz = tmp.st;
		let j = 0;
		while (resz) {
			sx[j++] = Math.round(log2(resz - pathh[resz]) + 1);
			resz = pathh[resz];
		}
		core.setFlag('pick_button', '1');
		var time2 = new Date().getTime();
		console.log("耗时：" + (time2 - time1) / 1000);
		console.log(sx);
		console.log(enemyList);
		drawAnswer(core.status.floorId);
		//      core.insertAction("击打的顺序为" + sx);
	}

	function findSingleOne(num) {
		let binary = num.toString(2);
		let count = 0;
		for (let i = 0; i < binary.length; i++) {
			if (binary[i] === '1') {
				count++;
			}
		}
		if (count == 1) {
			return binary.length;
		} else {
			return -1;
		}
	}


	function deci(deciNum) {
		let enemyList = core.getFlag('enemyList', []);
		let binaryNumber = deciNum.toString(2);
		if (binaryNumber.length < enemyList.length) {
			binaryNumber = '0'.repeat(enemyList.length - binaryNumber.length) + binaryNumber;
		}
		return binaryNumber;
	}

	//自动拾取相关函数

	function bfsFlood1(X, s) {
		let atk = 0,
			def = 0,
			mdef = 0,
			hp = 0,
			yellowKey = 0,
			blueKey = 0,
			redKey = 0,
			greenKey = 0;

		const pick = function (x) {
			if (node[x].type !== 'item') return;
			atk += node[x].atk;
			def += node[x].def;
			mdef += node[x].mdef;
			hp += node[x].hp;
			yellowKey += node[x].yellowKey;
			blueKey += node[x].blueKey;
			redKey += node[x].redKey;
			greenKey += node[x].greenKey;
		}

		let visited = {},
			queue = [];
		visited[s] = 1;
		queue.push(s);
		pick(s);

		while (queue.length > 0) {
			let x = queue.shift();
			for (let u of node[x].edge) {
				if (u in visited) continue;
				if (node[u].type === 'item') {
					pick(u);
					visited[u] = 1;
					queue.push(u);
				} else if (X.has(u)) {
					visited[u] = 1;
					queue.push(u);
				}
			}
		}
		return { atk: atk, def: def, mdef: mdef, hp: hp, yellowKey: yellowKey, blueKey: blueKey, redKey: redKey, greenKey: greenKey, canMove: visited };
	}

	var memo = {};

	var pathh = {};

	const doorToKey = { yellowDoor: 'yellowKey', blueDoor: 'blueKey', redDoor: 'redKey', greenDoor: 'greenKey' };

	function dp() {
		let yellowNeed = 0,
			blueNeed = 0,
			redNeed = 0,
			greenNeed = 0;

		const keyCallBack = function (ans) {
			let tmp = ans.split(",");
			yellowNeed = ~~tmp[0];
			blueNeed = ~~tmp[1];
			redNeed = ~~tmp[2];
			greenNeed = ~~tmp[3];
		}

		//      core.myprompt("请输入需要留的钥匙数（按照黄蓝红绿的顺序用英文逗号分隔）", "0,0,0,0", keyCallBack);

		dp_init();
		memo = {};
		pathh = {};
		let enemyList = core.getFlag('enemyList', []);
		let enemyChooseList = core.getFlag('enemyChooseList', []);
		var n = enemyList.length;
		let m = 0;
		let id = [];
		let set = new Set();
		for (let i = 0; i < n; i++) {
			if (enemyList[i].type === 'enemy' && findpos(enemyList[i], enemyChooseList) != -1) { m++; }
			set.add(enemyList[i].x + "," + enemyList[i].y + "," + enemyList[i].floorId);
		}
		console.log(set);
		bfsFlood(set);
		for (let i = 0; i < n; i++) {
			id[i] = visited[enemyList[i].x + "," + enemyList[i].y + "," + enemyList[i].floorId];
		}
		var s = visited[core.nextX(0) + "," + core.nextY(0) + "," + core.status.floorId];
		let all = Math.pow(2, n) - 1;
		memo[0] = hero.hp;
		pathh[0] = 0;
		var ans = -1;
		var st = 0;
		var step = 1;
		var full = 100;
		for (let X = 0; X <= all; X++) {
			if (!(X in memo)) continue;
			if (X / all >= step / full) {
				console.log(step);
				step++;
			}
			let cnt = 0;
			let set = new Set();
			let keyNow = { yellowKey: core.itemCount('yellowKey'), blueKey: core.itemCount('blueKey'), redKey: core.itemCount('redKey'), greenKey: core.itemCount('greenKey') };
			//          console.log(keyNow);
			//          console.log(yellowNeed);
			//          console.log(blueNeed);
			//          console.log(redNeed);
			//          console.log(greenNeed);
			let exp = core.getNextLvUpNeed();
			//          console.log(exp);
			for (let i = 0; i < n; i++)
				if ((X >> i) & 1) {
					if (enemyList[i].type === 'enemy') exp -= core.material.enemys[enemyList[i].id].exp;
					set.add(id[i]);
					if (enemyList[i].type !== 'enemy') keyNow[doorToKey[enemyList[i].type]]--;
					else if (findpos(enemyList[i], enemyChooseList) != -1) cnt++;
				}
			//          console.log(exp);
			let result = bfsFlood1(set, s);
			let lv = hero.lv - 1;
			while (exp <= 0 && lv < need.length) {
				exp += need[lv];
				let tmp = lvUp[lv];
				result.hp += tmp.hp;
				result.atk += tmp.atk;
				result.def += tmp.def;
				result.mdef += tmp.mdef;
				lv++;
			}
			//          console.log(X);
			//          console.log(exp);
			if (cnt >= m) {
				if (memo[X] + result.hp > ans) {
					ans = memo[X] + result.hp;
					st = X;
				}
				continue;
			}
			let canMove = result.canMove;
			//          console.log(X);
			//          console.log(canMove);
			let atkk = hero.atk + result.atk;
			let deff = hero.def + result.def;
			let mdeff = hero.mdef + result.mdef;
			keyNow.yellowKey += result.yellowKey;
			keyNow.blueKey += result.blueKey;
			keyNow.redKey += result.redKey;
			keyNow.greenKey += result.greenKey;
			//          console.log(keyNow.yellowKey);
			//          console.log(keyNow.blueKey);
			//          console.log(result.yellowKey);
			//          console.log(result.blueKey);
			for (let i = 0; i < n; i++) {
				if (!((X >> i) & 1)) {
					let num = 0;
					let flag = 1;
					let x = id[i];
					for (let u of node[x].edge) {
						if (u in canMove) {
							flag = 0;
							num++;
						}
					}
					if (flag == 1) continue;
					let Y = X ^ (1 << i);
					if (enemyList[i].type === 'enemy') {
						if (core.getDamageInfo(enemyList[i].id, { hp: memo[X], atk: atkk, def: deff, mdef: mdeff }) === null) continue;
						let hpp = memo[X] - core.getDamageInfo(enemyList[i].id, { hp: memo[X], atk: atkk, def: deff, mdef: mdeff }).damage;
						if (hpp + result.hp <= 0) continue;
						//                      console.log(core.getDamageInfo(enemyList[i].id, { hp: memo[X], atk: atkk, def: deff, mdef: mdeff }).damage);
						if (!(Y in memo)) {
							memo[Y] = hpp;
							pathh[Y] = X;
						} else if (hpp > memo[Y]) {
							memo[Y] = hpp;
							pathh[Y] = X;
						}
					} else {
						let hpp = memo[X];
						if (keyNow[doorToKey[enemyList[i].type]] <= 0 || num == node[x].length) continue;
						if (!(Y in memo)) {
							memo[Y] = hpp;
							pathh[Y] = X;
						} else if (hpp > memo[Y]) {
							memo[Y] = hpp;
							pathh[Y] = X;
						}
					}
				}
			}
		}
		return { ans: ans, st: st };
	}

	//加点

	var option = {};

	this.getEnemyList2 = function () {
		var time1 = new Date().getTime();
		core.clearMap("fg")
		let text = '';
		let enemyList = core.getFlag('enemyList', []);

		if (enemyList.length === 0) text = '未选择敌人';
		let tmp = dp2();
		let ans = tmp.ans;
		console.log(ans);
		if (ans <= 0) {
			core.insertAction(text);
			core.insertAction("打不过！");
			return;
		}
		core.insertAction(text);
		core.insertAction("计算结果为：" + ans);
		sx = [];
		op = [];
		let resz = tmp.st;
		let j = 0;
		while (resz != '0,0,0,0') {
			let ss = ~~resz.split(",")[0],
				tt = ~~pathh[resz].split(",")[0];
			op[j] = option[resz];
			sx[j++] = Math.round(log2(ss - tt) + 1);
			resz = pathh[resz];
		}
		core.setFlag('pick_button', '1');
		var time2 = new Date().getTime();
		console.log("耗时：" + (time2 - time1) / 1000);
		console.log(sx);
		console.log(enemyList);
		drawAnswer(core.status.floorId);
		//      core.insertAction("击打的顺序为" + sx);
	}

	function __gcd(a, b) {
		if (b == 0) return a;
		else return __gcd(b, a % b);
	}

	function dp2() {
		dp_init();
		memo = {};
		pathh = {};
		option = {};
		let enemyList = core.getFlag('enemyList', []);
		let enemyChooseList = core.getFlag('enemyChooseList', []);
		var n = enemyList.length;
		let m = 0;
		let id = [];
		let set = new Set();
		let gcd = 0;
		for (let i = 0; i < n; i++) {
			if (enemyList[i].type === 'enemy' && findpos(enemyList[i], enemyChooseList) != -1) { m++; }
			set.add(enemyList[i].x + "," + enemyList[i].y + "," + enemyList[i].floorId);
			if (enemyList[i].type !== 'enemy') continue;
			let num = core.material.enemys[enemyList[i].id].point;
			if (num == 0) continue;
			if (gcd == 0) gcd = num;
			else gcd = __gcd(gcd, num);
		}
		console.log(set);
		bfsFlood(set);
		for (let i = 0; i < n; i++) {
			id[i] = visited[enemyList[i].x + "," + enemyList[i].y + "," + enemyList[i].floorId];
		}
		var s = visited[core.nextX(0) + "," + core.nextY(0) + "," + core.status.floorId];
		let all = Math.pow(2, n) - 1;
		memo['0,0,0,0'] = hero.hp;
		pathh['0,0,0,0'] = '0,0,0,0';
		var ans = -1;
		var st = 0;
		var step = 1;
		var full = 100;
		for (let S = 0; S <= all; S++) {
			if (S / all >= step / full) {
				console.log(step);
				step++;
			}
			let cnt = 0;
			let set = new Set();
			let keyNow = { yellowKey: core.itemCount('yellowKey'), blueKey: core.itemCount('blueKey'), redKey: core.itemCount('redKey'), greenKey: core.itemCount('greenKey') };
			let exp = core.getNextLvUpNeed();
			let sum = 0;
			for (let i = 0; i < n; i++)
				if ((S >> i) & 1) {
					if (enemyList[i].type === 'enemy') {
						exp -= core.material.enemys[enemyList[i].id].exp;
						sum += core.material.enemys[enemyList[i].id].point;
					}
					set.add(id[i]);
					if (enemyList[i].type !== 'enemy') keyNow[doorToKey[enemyList[i].type]]--;
					else if (findpos(enemyList[i], enemyChooseList) != -1) cnt++;
				}
			let mxpoint = sum / gcd;
			let result = bfsFlood1(set, s);
			let lv = hero.lv - 1;
			while (exp <= 0 && lv < need.length) {
				exp += need[lv];
				let tmp = lvUp[lv];
				result.hp += tmp.hp;
				result.atk += tmp.atk;
				result.def += tmp.def;
				result.mdef += tmp.mdef;
				lv++;
			}
			let canMove = result.canMove;
			let atkk = hero.atk + result.atk;
			let deff = hero.def + result.def;
			let mdeff = hero.mdef + result.mdef;
			keyNow.yellowKey += result.yellowKey;
			keyNow.blueKey += result.blueKey;
			keyNow.redKey += result.redKey;
			keyNow.greenKey += result.greenKey;
			for (let A = 0; A <= mxpoint; A++) {
				for (let B = 0; A + B <= mxpoint; B++) {
					for (let C = 0; A + B + C <= mxpoint; C++) {
						let X = S + "," + A + "," + B + "," + C;
						if (!(X in memo)) continue;
						if (cnt >= m) {
							if (memo[X] + result.hp > ans) {
								ans = memo[X] + result.hp;
								st = X;
							}
							continue;
						}
						for (let i = 0; i < n; i++) {
							if (!((S >> i) & 1)) {
								let num = 0;
								let flag = 1;
								let x = id[i];
								for (let u of node[x].edge) {
									if (u in canMove) {
										flag = 0;
										num++;
									}
								}
								if (flag == 1) continue;
								let S1 = S ^ (1 << i);
								for (let k = 0; k < 4; k++) {
									let a = A,
										b = B,
										c = C;
									let hpp = memo[X];
									if (enemyList[i].type === 'enemy') {
										let point = core.material.enemys[enemyList[i].id].point / gcd;
										if (core.getDamageInfo(enemyList[i].id, { hp: hpp, atk: atkk + a * gcd * pointList.atk, def: deff + b * gcd * pointList.def, mdef: mdeff + c * gcd * pointList.mdef }) === null) continue;
										hpp -= core.getDamageInfo(enemyList[i].id, { hp: hpp, atk: atkk + a * gcd * pointList.atk, def: deff + b * gcd * pointList.def, mdef: mdeff + c * gcd * pointList.mdef }).damage;
										if (hpp + result.hp <= 0) continue;
										if (k == 0) a += point;
										else if (k == 1) b += point;
										else if (k == 2) c += point;
										else hpp += point * gcd * pointList.hp;
										let Y = S1 + "," + a + "," + b + "," + c;
										if (!(Y in memo)) {
											memo[Y] = hpp;
											pathh[Y] = X;
											option[Y] = k + 1;
										} else if (hpp > memo[Y]) {
											memo[Y] = hpp;
											pathh[Y] = X;
											option[Y] = k + 1;
										}
									} else if (k == 0) {
										if (keyNow[doorToKey[enemyList[i].type]] <= 0 || num == node[x].length) continue;
										let Y = S1 + "," + a + "," + b + "," + c;
										if (!(Y in memo)) {
											memo[Y] = hpp;
											pathh[Y] = X;
											option[Y] = 0;
										} else if (hpp > memo[Y]) {
											memo[Y] = hpp;
											pathh[Y] = X;
											option[Y] = 0;
										}
									}
								}
							}
						}
					}
				}
			}
		}
		return { ans: ans, st: st };
	}
},
    "routeFixing": function () {
	// 是否开启本插件，true 表示启用，false 表示禁用。
	var __enable = true;
	if (!__enable) return;
	/*
		 使用说明：启用本插件后，录像回放时您可以用数字键1或6分别切换到原速或24倍速，
		 暂停播放时按数字键7（电脑按N）可以单步播放。（手机端可以点击难度单词切换出数字键）
		 数字键2-5可以进行录像自助精修，具体描述见下（实际弹窗请求您输入时不要带有任何空格）：
		 
		 up down left right 勇士向某个方向「行走一步或撞击」
		 item:ID 使用某件道具，如 item:bomb 表示使用炸弹
		 unEquip:n 卸掉身上第(n+1)件装备（n从0开始），如 unEquip:1 默认表示卸掉盾牌
		 equip:ID 穿上某件装备，如 equip:sword1 表示装上铁剑
		 saveEquip:n 将身上的当前套装保存到第n套快捷套装（n从0开始）
		 loadEquip:n 快捷换上之前保存好的第n套套装
		 fly:ID 使用楼传飞到某一层，如 fly:MT10 表示飞到主塔10层
		 choices:none 确认框/选择项「超时」（作者未设置超时时间则此项视为缺失）
		 choices:n 确认框/选择项选择第(n+1)项（选择项n从0开始，确认框n为0表示「确定」，1表示「取消」）
		 选择项n为负数时表示选择倒数第 -n 项，如 -1 表示最后一项（V2.8.2起标准全局商店的「离开」项）
		 此项缺失的话，确认框将选择作者指定的默认项（初始光标位置），选择项将弹窗请求补选（后台录像验证中选最后一项，可以复写函数来修改）
		 shop:ID 打开某个全局商店，如 shop:itemShop 表示打开道具商店。因此连载塔千万不要中途修改商店ID！
		 turn 单击勇士（Z键）转身，core.turnHero() 会产生此项，因此通过事件等方式强制让勇士转向应该用 core.setHeroLoc()
		 turn:dir 勇士转向某个方向，dir 可以为 up down left right（此项一般是读取自动存档产生的，属于样板的不良特性，请勿滥用）
		 getNext 轻按获得身边道具，优先获得面前的（面前没有则按上下左右顺序依次获得），身边如果没有道具则此项会被跳过
		 input:none “等待用户操作事件”中超时（作者未设置超时时间则此项会导致报错）
		 input:xxx 可能表示“等待用户操作事件”的一个操作（如按键操作将直接记录 input:keycode ），
		 也可能表示一个“接受用户输入数字”的输入，后者的情况下 xxx 为输入的整数。此项缺失的话前者将直接报错，后者将用0代替（后者现在支持负数了）
		 input2:xxx 可能表示“读取全局存储（core.getGlobal）”读取到的值，也可能表示一个“接受用户输入文本”的输入，
		 两种情况下 xxx 都为 base64 编码。此项缺失的话前者将重新现场读取，后者将用空字符串代替
		 no 走到可穿透的楼梯上不触发楼层切换事件，通过本插件可以让勇士停在旁边没有障碍物的楼梯上哦～
		 move:x:y 尝试瞬移到 [x,y] 点（不改变朝向），该点甚至可以和勇士相邻或者位于视野外
		 key:n 松开键值为n的键，如 key:49 表示松开大键盘数字键1，默认会触发使用破墙镐
		 click:n:px:py 点击自绘状态栏，n为0表示横屏1表示竖屏，[px,py] 为点击的像素坐标
		 random:n 生成了随机数n，即 core.rand2(num) 的返回结果，n必须在 [0,num-1] 范围，num必须为正整数。此项缺失将导致现场重新随机生成数值，可能导致回放结果不一致！
		 作者自定义的新项（一般为js对象，可以先JSON.stringify()再core.encodeBase64()得到纯英文数字的内容）需要用(半角圆括弧)括起来。
		 
		 当您使用数字键5将一些项追加到即将播放内容的开头时，请注意要逆序逐项追加，或者每追加一项就按下数字键7或字母键N单步播放一步。
		 但是【input input2 random choices】是被动读取的，单步播放如果触发了相应的事件就会连续读取，这时候只能提前逐项追加好。
		 电脑端熟练以后推荐直接在控制台操作 core.status.route 和 core.status.replay.toReplay（后者录像回放时才有），配合 core.push() 和 core.unshift() 更加灵活自由哦！
		 */
	core.actions.registerAction(
		"onkeyUp",
		"_sys_onkeyUp_replay",
		function (e) {
			if (this._checkReplaying()) {
				if (e.keyCode == 27)
					// ESCAPE
					core.stopReplay();
				else if (e.keyCode == 90)
					// Z
					core.speedDownReplay();
				else if (e.keyCode == 67)
					// C
					core.speedUpReplay();
				else if (e.keyCode == 32)
					// SPACE
					core.triggerReplay();
				else if (e.keyCode == 65)
					// A
					core.rewindReplay();
				else if (e.keyCode == 83)
					// S
					core.control._replay_SL();
				else if (e.keyCode == 88)
					// X
					core.control._replay_book();
				else if (e.keyCode == 33 || e.keyCode == 34)
					// PgUp/PgDn
					core.control._replay_viewMap();
				else if (e.keyCode == 78)
					// N
					core.stepReplay();
				else if (e.keyCode == 84)
					// T
					core.control._replay_toolbox();
				else if (e.keyCode == 81)
					// Q
					core.control._replay_equipbox();
				else if (e.keyCode == 66)
					// B
					core.ui._drawStatistics();
				else if (e.keyCode == 49 || e.keyCode == 54)
					// 1/6，原速/24倍速播放
					core.setReplaySpeed(e.keyCode == 49 ? 1 : 24);
				else if (e.keyCode > 49 && e.keyCode < 54) {
					// 2-5，录像精修
					switch (e.keyCode - 48) {
					case 2: // pop
						alert(
							"您已移除已录制内容的最后一项：" + core.status.route.pop()
						);
						break;
					case 3: // push
						core.utils.myprompt(
							"请输入您要追加到已录制内容末尾的项：",
							"",
							function (value) {
								if (value != null) core.status.route.push(value);
							}
						);
						break;
					case 4: // shift
						alert(
							"您已移除即将播放内容的第一项：" +
							core.status.replay.toReplay.shift()
						);
						break;
					case 5: // unshift
						core.utils.myprompt(
							"请输入您要追加到即将播放内容开头的项：",
							"",
							function (value) {
								if (value != null)
									core.status.replay.toReplay.unshift(value);
							}
						);
					}
				}
				return true;
			}
		},
		100
	);
},
    "animate": function () {
	// -------------------- 插件说明 -------------------- //

	// github仓库：https://github.com/unanmed/animate
	// npm包名：mutate-animate
	// npm地址：https://www.npmjs.com/package/mutate-animate

	// 不要去尝试读这个插件，这个插件是经过了打包的，不是人类可读的(
	// 想读的话可以去github读

	// 该插件是一个轻量型多功能动画插件，可以允许你使用内置或自定义的速率曲线或轨迹等
	// 除此之外，你还可以自定义绘制函数，来让你的动画可视化

	// -------------------- 安装说明 -------------------- //

	// 直接复制到插件中即可，注意所有插件中不能出现插件名为animate的插件
	// 该插件分为动画和渐变两部分，教程分开，动画在前，渐变在后

	// -------------------- 动画使用教程 -------------------- //

	// 1. 首先创建一个异步函数
	//   async function ani() { }

	// 2. 引入插件中的类和函数，引入内容要看个人需求，所有可用的函数在本插件末尾可以看到
	//   const { Animation, linear, bezier, circle, hyper, trigo, power, inverseTrigo, shake, sleep } = core.plugin.animate

	// 3. 在函数内部创建一个动画
	//   const animate = new Animation();

	// 4. 为动画创建一个绘制函数，这里以绘制一个矩形为例，当然也可以使用core.fillRect替代ctx.fillRect来绘制矩形
	//   const ctx = core.createCanvas('animate', 0, 0, 416, 416, 100);
	//   ctx.save();
	//   const fn = () => {
	//      ctx.restore();
	//      ctx.save();
	//      ctx.clearRect(0, 0, 800, 800);
	//      ctx.translate(animate.x, animate.y);
	//      ctx.rotate(animate.angle * Math.PI / 180);
	//      const size = animate.size;
	//      ctx.fillRect(-30 * size, -30 * size, 60 * size, 60 * size);
	//   }
	//   animate.ticker.add(fn);

	// 5. 执行动画

	//   下面先对一些概念进行解释

	//   动画分为很多种，内置的有move(移动至某一点)  rotate(旋转)  scale(放缩)  moveAs(以指定路径移动)  shake(震动)
	//   对于不同的动画种类，其所对应的属性也不同，move moveAs shake均对应x和y这两个属性
	//   rotate对应angle，scale对应size。你也可以自定义属性，这个之后会提到

	//   除了执行动画之外，这里还提供了三个等待函数，可以等待某个动画执行完毕，以及一个等待指定时长的函数
	//   分别是animate.n(等待指定数量的动画执行完毕)
	//   animate.w(等待指定类型的动画执行完毕，也可以是自定义类型)
	//   animate.all(等待所有动画执行完毕)
	//   sleep(等待指定时长)

	//   执行动画时，要求一个渐变函数，当然这个插件内置了非常丰富的渐变函数，也就是速率曲线。

	//   线性渐变函数  linear()，该函数返回一个线性变化函数

	//   三角渐变函数  trigo('sin' | 'sec', EaseMode)，该函数返回一个指定属性的三角函数变化函数
	//       其中EaseMode可以填'in' 'out' 'in-out' 'center'
	//       分别表示 慢-快  快-慢  慢-快-慢  快-慢-快

	//   幂函数渐变  power(n, EaseMode)，该函数返回一个以x^n变化的函数，n是指数

	//   双曲渐变函数  hyper('sin' | 'tan' | 'sec', EaseMode)，该函数返回一个双曲函数，分别是双曲正弦、双曲正切、双曲正割

	//   反三角渐变函数  inverseTrigo('sin' | 'tan', EaseMode)，该函数返回一个反三角函数

	//   贝塞尔曲线渐变函数  bezier(...cps)，参数为贝塞尔曲线的控制点纵坐标（横坐标不能自定义，毕竟一个时刻不能对应多个速率）
	//       示例：bezier(0.4, 0.2, 0.7); // 三个控制点的四次贝塞尔曲线渐变函数

	//   了解完渐变函数以后，这里还有一个特殊的渐变函数-shake
	//   shake(power, timing)，这个函数是一个震荡函数，会让一个值来回变化，实现震动的效果
	//       其中power是震动的最大值，timing是渐变函数，描述了power在震动时大小的变化

	//   下面，我们就可以进行动画的执行了，我们以 运动 + 旋转 + 放缩为例

	//   animate.mode(hyper('sin', 'out'))  // 设置渐变函数为 双曲正弦 快 -> 慢，注意不能加分号
	//       .time(1000)  // 设置动画的执行时间为1000毫秒
	//       .move(300, 300)  // 移动至[300, 300]的位置
	//       .relative()  // 设置相对模式为相对之前，与之前为相加的关系
	//       .mode(power(3, 'center'))  // 设置为 x^3 快-慢-快 的渐变函数
	//       .time(3000)
	//       .rotate(720)  // 旋转720度
	//       .absolute()  // 设置相对模式为绝对
	//       .mode(trigo('sin', 'in'))  // 设置渐变函数为 正弦 慢 -> 快
	//       .time(1500)
	//       .scale(3);  // 放缩大小至3倍

	//   这样，我们就把三种基础动画都执行了一遍，同时，这种写法非常直观，出现问题时也可以很快地找到问题所在
	//   下面，我们需要等待动画执行完毕，因为同一种动画不可能同时执行两个

	//   await animate.n(1); // 等待任意一个动画执行完毕，别把await忘了
	//   await animate.w('scale'); // 等待放缩动画执行完毕
	//   await animate.all(); // 等待所有动画执行完毕
	//   await sleep(1000); // 等待1000毫秒

	//   下面，还有一个特殊的动画函数-moveAs
	//   这是一个非常强大的函数，它允许你让你的物体按照指定路线运动
	//   说到这，我们需要先了解一下运动函数。
	//   该插件内置了两个运动函数，分别是圆形运动和贝塞尔曲线运动

	//   圆形运动 circle(r, n, timing, inverse)，r是圆的半径，n是圈数，timing描述半径大小的变化，inverse说明了是否翻转timing函数，后面三个可以不填

	//   贝塞尔曲线 bezierPath(start, end, ...cps)
	//       其中start和end是起点和结束点，应当填入[x, y]数组，cps是控制点，也是[x, y]数组
	//       示例：bezierPath([0, 0], [200, 200], [100, 50], [300, 150], [200, 180]);
	//       这是一个起点为 [0, 0]，终点为[200, 200]，有三个控制点的四次贝塞尔曲线

	//   下面，我们就可以使用路径函数了

	//   animate.mode(hyper('sin', 'in-out'))  // 设置渐变曲线
	//       .time(5000)
	//       .relative()  // 设置为相对模式，这个比较必要，不然的话很可能出现瞬移
	//       .moveAs(circle(100, 5, linear()))  // 创建一个5圈的半径从0至100逐渐变大的圆轨迹（是个螺旋线）并让物体沿着它运动
	//
	//   最后，还有一个震动函数 shake(x, y)，x和y表示了在横向上和纵向上的震动幅度，1表示为震动幅度的100%
	//   示例：
	//   animate.mode(shake(5, hyper('sin', 'in')), true) // 这里第二个参数说明是震动函数
	//       .time(2000)
	//       .shake(1, 0.5)

	//   这样，所有内置动画就已经介绍完毕

	// 6. 自定义动画属性

	//   本插件允许你自定义一个动画属性，但功能可能不会像自带的属性那么强大
	//   你可以在创建动画之后使用animate.register(key, init)来注册一个自定义属性
	//   其中key是自定义属性的名称，init是自定义属性的初始值，这个值应当在0-1之间变化

	//   你可以通过animate.value[key]来获取你注册的自定义属性

	//   对于自定义属性的动画，你应当使用animate.apply(key, n, first)
	//   其中，key是你的自定义属性的名称，n是其目标值，first是一个布尔值，说明了是否将该动画插入到目前所有的动画之前，即每帧会优先执行该动画

	//   下面是一个不透明度的示例

	//   animate.register('opacity', 1); // 这句话应该放到刚创建动画之后

	//   ctx.globalAlpha = animate.value.opacity; // 这句话应当放到每帧绘制的函数里面，放在绘制之前

	//   animate.mode(bezier(0.9, 0.1, 0.05))  // 设置渐变函数
	//       .time(2000)
	//       .absolute()
	//       .apply('opacity', 0.3);  // 将不透明度按照渐变曲线更改为0.3

	// 7. 运行动画

	//   还记得刚开始定义的async function 吗，直接调用它就能执行动画了！
	//   示例：ani(); // 执行刚刚写的所有动画

	// 8. 自定义速率曲线和路径

	//   该插件中，速率曲线和路径均可自定义

	//   对于速率曲线，其类型为  (input: number) => number
	//   它接受一个范围在 0-1 的值，输出一个 0-1 的值，表示了动画的完成度，1表示动画已完成，0表示动画刚开始（当前大于1小于0也不会报错，也会执行相应的动画）

	//   对于路径，其类型为  (input: number) => [number, number]
	//   它与速率曲线类似，接收一个 0-1 的值，输出一个坐标数组

	// 9. 多个属性绑定

	//   该插件中，你可以绑定多个动画属性，你可以使用ani.bind(...attr)来绑定。
	//   绑定之后，这三个动画属性可以被一个返回了长度为3的数组的渐变函数执行。
	//   绑定使用ani.bind，设置渐变函数仍然使用ani.mode，注意它与单个动画属性是分开的，也就是它不会影响正常的渐变函数。
	//   然后使用ani.applyMulti即可执行动画

	//   例如：
	//   // 自定义的一个三属性渐变函数
	//   function b(input) {
	//       return [input * 100, input ** 2 * 100, input ** 3 * 100];
	//   }
	//   ani.bind('a', 'b', 'c') // 这样会绑定abc这三个动画属性
	//       .mode(b) // 自定义的一个返回了长度为3的数组的函数
	//       .time(5000)
	//       .absolute()
	//       .applyMulti(); // 执行这个动画

	// 9. 监听  动画的生命周期钩子

	//   这个插件还允许你去监听动画的状态，可以监听动画的开始、结束、运行
	//   你可以使用 animate.listen(type, fn)来监听，fn的类型是 (a: Animation, type: string) => void
	//   当然，一般情况下你不会用到这个功能，插件中已经帮你包装了三个等待函数，他们就是以这些监听为基础的

	// 10. 自定义时间获取函数

	//   你可以修改ani.getTime来修改动画的时间获取函数，例如想让动画速度变成一半可以写ani.getTime = () => Date.now() / 2
	//   这样可以允许你随意控制动画的运行速度，暂停，甚至是倒退。该值默认为`Date.now`

	// -------------------- 渐变使用教程 -------------------- //

	// 相比于动画，渐变属于一种较为简便的动画，它可以让你在设置一个属性后使属性缓慢变化值目标值而不是突变至目标值
	// 现在假设你已经了解了动画的使用，下面我们来了解渐变。

	// 1. 创建一个渐变实例
	//   与动画类似，你需要使用new来实例化一个渐变，当然别忘了引入
	//   const { Transition } = core.plugin.animate;
	//   const tran = new Transition();

	// 2. 绘制
	//   const ctx = core.createCanvas('transition', 0, 0, 416, 416, 100);
	//   ctx.save();
	//   const fn = () => {
	//      ctx.restore();
	//      ctx.save();
	//      ctx.clearRect(0, 0, 800, 800);
	//      ctx.beginPath();
	//      ctx.arc(tran.value.x, tran.value.y, 50, 0, Math.PI * 2); // 使用tran.value.xxx获取当前的属性
	//      ctx.fill();
	//      // 当然也可以用样板的api，例如core.fillCircle();等
	//   }
	//   animate.ticker.add(fn);

	// 3. 设置渐变
	//   同样，与动画类似，你可以使用tran.time()设置渐变时间，使用tran.mode()设置渐变函数，使用tran.absolute()和tran.relative()设置相对模式
	//   例如：
	//   tran.time(1000)
	//       .mode(hyper('sin', 'out'))
	//       .absolute();

	// 4. 初始化渐变属性
	//   与动画不同的是，动画在执行一个自定义属性前都需要register，而渐变不需要。
	//   你可以通过tran.value.xxx = yyy来设置动画属性或使用tran.transition('xxx', yyy)来设置
	//   你的首次赋值即是初始化了渐变属性，这时是不会执行渐变的，例如：
	//   tran.value.x = 200;
	//   tran.transition('y', 200);
	//   上述例子便是将 x 和 y 初始化成了200

	// 5. 执行渐变
	//   初始化完成后，便可以直接执行渐变了，有两种方法
	//   tran.value.x = 400; // 将 x 缓慢移动至400
	//   tran.transition('y', 400); // 将 y 缓慢移动至400

	// 6. 自定义时间获取函数
	//   与动画类似，你依然可以通过修改tran.getTime来修改时间获取函数

	if (main.replayChecking) return (core.plugin.animate = {});

	var M = Object.defineProperty;
	var E = (n, s, t) =>
		s in n ?
		M(n, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) :
		(n[s] = t);
	var o = (n, s, t) => (E(n, typeof s != "symbol" ? s + "" : s, t), t);
	let b = [];
	const k = (n) => {
		for (const s of b)
			if (s.status === "running")
				try {
					for (const t of s.funcs) t(n - s.startTime);
				} catch (t) {
					s.destroy(), console.error(t);
				}
		requestAnimationFrame(k);
	};
	requestAnimationFrame(k);
	class I {
		constructor() {
			o(this, "funcs", []);
			o(this, "status", "stop");
			o(this, "startTime", 0);
			(this.status = "running"),
			b.push(this),
				requestAnimationFrame((s) => (this.startTime = s));
		}
		add(s, t = !1) {
			return t ? this.funcs.unshift(s) : this.funcs.push(s), this;
		}
		remove(s) {
			const t = this.funcs.findIndex((e) => e === s);
			if (t === -1)
				throw new ReferenceError(
					"You are going to remove nonexistent ticker function."
				);
			return this.funcs.splice(t, 1), this;
		}
		clear() {
			this.funcs = [];
		}
		destroy() {
			this.clear(), this.stop();
		}
		stop() {
			(this.status = "stop"), (b = b.filter((s) => s !== this));
		}
	}
	class F {
		constructor() {
			o(this, "timing");
			o(this, "relation", "absolute");
			o(this, "easeTime", 0);
			o(this, "applying", {});
			o(this, "getTime", Date.now);
			o(this, "ticker", new I());
			o(this, "value", {});
			o(this, "listener", {});
			this.timing = (s) => s;
		}
		async all() {
			if (Object.values(this.applying).every((s) => s === !0))
				throw new ReferenceError("There is no animates to be waited.");
			await new Promise((s) => {
				const t = () => {
					Object.values(this.applying).every((e) => e === !1) &&
						(this.unlisten("end", t), s("all animated."));
				};
				this.listen("end", t);
			});
		}
		async n(s) {
			const t = Object.values(this.applying).filter((i) => i === !0).length;
			if (t < s)
				throw new ReferenceError(
					`You are trying to wait ${s} animate, but there are only ${t} animate animating.`
				);
			let e = 0;
			await new Promise((i) => {
				const r = () => {
					e++, e === s && (this.unlisten("end", r), i(`${s} animated.`));
				};
				this.listen("end", r);
			});
		}
		async w(s) {
			if (this.applying[s] === !1)
				throw new ReferenceError(`The ${s} animate is not animating.`);
			await new Promise((t) => {
				const e = () => {
					this.applying[s] === !1 &&
						(this.unlisten("end", e), t(`${s} animated.`));
				};
				this.listen("end", e);
			});
		}
		listen(s, t) {
			var e, i;
			(i = (e = this.listener)[s]) != null || (e[s] = []),
				this.listener[s].push(t);
		}
		unlisten(s, t) {
			const e = this.listener[s].findIndex((i) => i === t);
			if (e === -1)
				throw new ReferenceError(
					"You are trying to remove a nonexistent listener."
				);
			this.listener[s].splice(e, 1);
		}
		hook(...s) {
			const t = Object.entries(this.listener).filter((e) => s.includes(e[0]));
			for (const [e, i] of t)
				for (const r of i) r(this, e);
		}
	}

	function T(n) {
		return n != null;
	}
	async function R(n) {
		return new Promise((s) => setTimeout(s, n));
	}
	class Y extends F {
		constructor() {
			super();
			o(this, "shakeTiming");
			o(this, "path");
			o(this, "multiTiming");
			o(this, "value", {});
			o(this, "size", 1);
			o(this, "angle", 0);
			o(this, "targetValue", {
				system: {
					move: [0, 0],
					moveAs: [0, 0],
					resize: 0,
					rotate: 0,
					shake: 0,
					"@@bind": [],
				},
				custom: {},
			});
			o(this, "animateFn", {
				system: {
					move: [() => 0, () => 0],
					moveAs: () => 0,
					resize: () => 0,
					rotate: () => 0,
					shake: () => 0,
					"@@bind": () => 0,
				},
				custom: {},
			});
			o(this, "ox", 0);
			o(this, "oy", 0);
			o(this, "sx", 0);
			o(this, "sy", 0);
			o(this, "bindInfo", []);
			(this.timing = (t) => t),
			(this.shakeTiming = (t) => t),
			(this.multiTiming = (t) => [t, t]),
			(this.path = (t) => [t, t]),
			(this.applying = {
				move: !1,
				scale: !1,
				rotate: !1,
				shake: !1,
			}),
			this.ticker.add(() => {
				const { running: t } = this.listener;
				if (T(t))
					for (const e of t) e(this, "running");
			});
		}
		get x() {
			return this.ox + this.sx;
		}
		get y() {
			return this.oy + this.sy;
		}
		mode(t, e = !1) {
			return (
				typeof t(0) == "number" ?
				e ?
				(this.shakeTiming = t) :
				(this.timing = t) :
				(this.multiTiming = t),
				this
			);
		}
		time(t) {
			return (this.easeTime = t), this;
		}
		relative() {
			return (this.relation = "relative"), this;
		}
		absolute() {
			return (this.relation = "absolute"), this;
		}
		bind(...t) {
			return (
				this.applying["@@bind"] === !0 && this.end(!1, "@@bind"),
				(this.bindInfo = t),
				this
			);
		}
		unbind() {
			return (
				this.applying["@@bind"] === !0 && this.end(!1, "@@bind"),
				(this.bindInfo = []),
				this
			);
		}
		move(t, e) {
			return (
				this.applying.move && this.end(!0, "move"),
				this.applySys("ox", t, "move"),
				this.applySys("oy", e, "move"),
				this
			);
		}
		rotate(t) {
			return this.applySys("angle", t, "rotate"), this;
		}
		scale(t) {
			return this.applySys("size", t, "resize"), this;
		}
		shake(t, e) {
			this.applying.shake === !0 && this.end(!0, "shake"),
				(this.applying.shake = !0);
			const { easeTime: i, shakeTiming: r } = this,
			h = this.getTime();
			if ((this.hook("start", "shakestart"), i <= 0))
				return this.end(!1, "shake"), this;
			const l = () => {
				const c = this.getTime() - h;
				if (c > i) {
					this.ticker.remove(l),
						(this.applying.shake = !1),
						(this.sx = 0),
						(this.sy = 0),
						this.hook("end", "shakeend");
					return;
				}
				const a = c / i,
					m = r(a);
				(this.sx = m * t), (this.sy = m * e);
			};
			return this.ticker.add(l), (this.animateFn.system.shake = l), this;
		}
		moveAs(t) {
			this.applying.moveAs && this.end(!0, "moveAs"),
				(this.applying.moveAs = !0),
				(this.path = t);
			const { easeTime: e, relation: i, timing: r } = this,
			h = this.getTime(),
				[l, u] = [this.x, this.y],
				[c, a] = (() => {
					if (i === "absolute") return t(1); {
						const [d, f] = t(1);
						return [l + d, u + f];
					}
				})();
			if ((this.hook("start", "movestart"), e <= 0))
				return this.end(!1, "moveAs"), this;
			const m = () => {
				const f = this.getTime() - h;
				if (f > e) {
					this.end(!0, "moveAs");
					return;
				}
				const v = f / e,
					[g, w] = t(r(v));
				i === "absolute" ?
					((this.ox = g), (this.oy = w)) :
					((this.ox = l + g), (this.oy = u + w));
			};
			return (
				this.ticker.add(m, !0),
				(this.animateFn.system.moveAs = m),
				(this.targetValue.system.moveAs = [c, a]),
				this
			);
		}
		register(t, e) {
			if (typeof this.value[t] == "number")
				return this.error(
					`Property ${t} has been regietered twice.`,
					"reregister"
				);
			(this.value[t] = e), (this.applying[t] = !1);
		}
		apply(t, e, i = !1) {
			this.applying[t] === !0 && this.end(!1, t),
				t in this.value ||
				this.error(`You are trying to execute nonexistent property ${t}.`),
				(this.applying[t] = !0);
			const r = this.value[t],
				h = this.getTime(),
				{ timing: l, relation: u, easeTime: c } = this,
				a = u === "absolute" ? e - r : e;
			if ((this.hook("start"), c <= 0)) return this.end(!1, t), this;
			const m = () => {
				const f = this.getTime() - h;
				if (f > c) {
					this.end(!1, t);
					return;
				}
				const v = f / c,
					g = l(v);
				this.value[t] = r + g * a;
			};
			return (
				this.ticker.add(m, i),
				(this.animateFn.custom[t] = m),
				(this.targetValue.custom[t] = a + r),
				this
			);
		}
		applyMulti(t = !1) {
			this.applying["@@bind"] === !0 && this.end(!1, "@@bind"),
				(this.applying["@@bind"] = !0);
			const e = this.bindInfo,
				i = e.map((m) => this.value[m]),
				r = this.getTime(),
				{ multiTiming: h, relation: l, easeTime: u } = this,
				c = h(1);
			if (c.length !== i.length)
				throw new TypeError(
					`The number of binded animate attributes and timing function returns's length does not match. binded: ${e.length}, timing: ${c.length}`
				);
			if ((this.hook("start"), u <= 0)) return this.end(!1, "@@bind"), this;
			const a = () => {
				const d = this.getTime() - r;
				if (d > u) {
					this.end(!1, "@@bind");
					return;
				}
				const f = d / u,
					v = h(f);
				e.forEach((g, w) => {
					l === "absolute" ?
						(this.value[g] = v[w]) :
						(this.value[g] = i[w] + v[w]);
				});
			};
			return (
				this.ticker.add(a, t),
				(this.animateFn.custom["@@bind"] = a),
				(this.targetValue.system["@@bind"] = c),
				this
			);
		}
		applySys(t, e, i) {
			i !== "move" && this.applying[i] === !0 && this.end(!0, i),
				(this.applying[i] = !0);
			const r = this[t],
				h = this.getTime(),
				l = this.timing,
				u = this.relation,
				c = this.easeTime,
				a = u === "absolute" ? e - r : e;
			if ((this.hook("start", `${i}start`), c <= 0)) return this.end(!1, i);
			const m = () => {
				const f = this.getTime() - h;
				if (f > c) {
					this.end(!0, i);
					return;
				}
				const v = f / c,
					g = l(v);
				(this[t] = r + a * g), t !== "oy" && this.hook(i);
			};
			this.ticker.add(m, !0),
				t === "ox" ?
				(this.animateFn.system.move[0] = m) :
				t === "oy" ?
				(this.animateFn.system.move[1] = m) :
				(this.animateFn.system[i] = m),
				i === "move" ?
				(t === "ox" && (this.targetValue.system.move[0] = a + r),
					t === "oy" && (this.targetValue.system.move[1] = a + r)) :
				i !== "shake" && (this.targetValue.system[i] = a + r);
		}
		error(t, e) {
			throw e === "repeat" ?
				new Error(`Cannot execute the same animation twice. Info: ${t}`) :
				e === "reregister" ?
				new Error(`Cannot register an animated property twice. Info: ${t}`) :
				new Error(t);
		}
		end(t, e) {
			if (t === !0)
				if (
					((this.applying[e] = !1),
						e === "move" ?
						(this.ticker.remove(this.animateFn.system.move[0]),
							this.ticker.remove(this.animateFn.system.move[1])) :
						e === "moveAs" ?
						this.ticker.remove(this.animateFn.system.moveAs) :
						e === "@@bind" ?
						this.ticker.remove(this.animateFn.system["@@bind"]) :
						this.ticker.remove(this.animateFn.system[e]),
						e === "move")
				) {
					const [i, r] = this.targetValue.system.move;
					(this.ox = i), (this.oy = r), this.hook("moveend", "end");
				} else if (e === "moveAs") {
				const [i, r] = this.targetValue.system.moveAs;
				(this.ox = i), (this.oy = r), this.hook("moveend", "end");
			} else
				e === "rotate" ?
				((this.angle = this.targetValue.system.rotate),
					this.hook("rotateend", "end")) :
				e === "resize" ?
				((this.size = this.targetValue.system.resize),
					this.hook("resizeend", "end")) :
				e === "@@bind" ?
				this.bindInfo.forEach((r, h) => {
					this.value[r] = this.targetValue.system["@@bind"][h];
				}) :
				((this.sx = 0), (this.sy = 0), this.hook("shakeend", "end"));
			else
				(this.applying[e] = !1),
				this.ticker.remove(this.animateFn.custom[e]),
				(this.value[e] = this.targetValue.custom[e]),
				this.hook("end");
		}
	}
	class j extends F {
		constructor() {
			super();
			o(this, "now", {});
			o(this, "target", {});
			o(this, "transitionFn", {});
			o(this, "value");
			o(this, "handleSet", (t, e, i) => (this.transition(e, i), !0));
			o(this, "handleGet", (t, e) => this.now[e]);
			(this.timing = (t) => t),
			(this.value = new Proxy(this.target, {
				set: this.handleSet,
				get: this.handleGet,
			}));
		}
		mode(t) {
			return (this.timing = t), this;
		}
		time(t) {
			return (this.easeTime = t), this;
		}
		relative() {
			return (this.relation = "relative"), this;
		}
		absolute() {
			return (this.relation = "absolute"), this;
		}
		transition(t, e) {
			if (e === this.target[t]) return this;
			if (!T(this.now[t])) return (this.now[t] = e), this;
			this.applying[t] && this.end(t, !0),
				(this.applying[t] = !0),
				this.hook("start");
			const i = this.getTime(),
				r = this.easeTime,
				h = this.timing,
				l = this.now[t],
				u = e + (this.relation === "absolute" ? 0 : l),
				c = u - l;
			this.target[t] = u;
			const a = () => {
				const d = this.getTime() - i;
				if (d >= r) {
					this.end(t);
					return;
				}
				const f = d / r;
				(this.now[t] = h(f) * c + l), this.hook("running");
			};
			return (
				(this.transitionFn[t] = a),
				r <= 0 ? (this.end(t), this) : (this.ticker.add(a), this)
			);
		}
		end(t, e = !1) {
			const i = this.transitionFn[t];
			if (!T(i))
				throw new ReferenceError(
					`You are trying to end an ended transition: ${t}`
				);
			this.ticker.remove(this.transitionFn[t]),
				delete this.transitionFn[t],
				(this.applying[t] = !1),
				this.hook("end"),
				e || (this.now[t] = this.target[t]);
		}
	}
	const x = (...n) => n.reduce((s, t) => s + t, 0),
		y = (n) => {
			if (n === 0) return 1;
			let s = n;
			for (; n > 1;) n--, (s *= n);
			return s;
		},
		A = (n, s) => Math.round(y(s) / (y(n) * y(s - n))),
		p = (n, s, t = (e) => 1 - s(1 - e)) =>
		n === "in" ?
		s :
		n === "out" ?
		t :
		n === "in-out" ?
		(e) => (e < 0.5 ? s(e * 2) / 2 : 0.5 + t((e - 0.5) * 2) / 2) :
		(e) => (e < 0.5 ? t(e * 2) / 2 : 0.5 + s((e - 0.5) * 2) / 2),
		$ = Math.cosh(2),
		z = Math.acosh(2),
		V = Math.tanh(3),
		P = Math.atan(5);

	function O() {
		return (n) => n;
	}

	function q(...n) {
		const s = [0].concat(n);
		s.push(1);
		const t = s.length,
			e = Array(t)
			.fill(0)
			.map((i, r) => A(r, t - 1));
		return (i) => {
			const r = e.map((h, l) => h * s[l] * (1 - i) ** (t - l - 1) * i ** l);
			return x(...r);
		};
	}

	function U(n, s) {
		if (n === "sin") {
			const t = (i) => Math.sin((i * Math.PI) / 2);
			return p(s, (i) => 1 - t(1 - i), t);
		}
		if (n === "sec") {
			const t = (i) => 1 / Math.cos(i);
			return p(s, (i) => t((i * Math.PI) / 3) - 1);
		}
		throw new TypeError(
			"Unexpected parameters are delivered in trigo timing function."
		);
	}

	function C(n, s) {
		if (!Number.isInteger(n))
			throw new TypeError(
				"The first parameter of power timing function only allow integer."
			);
		return p(s, (e) => e ** n);
	}

	function G(n, s) {
		if (n === "sin") return p(s, (e) => (Math.cosh(e * 2) - 1) / ($ - 1));
		if (n === "tan") {
			const t = (i) => (Math.tanh(i * 3) * 1) / V;
			return p(s, (i) => 1 - t(1 - i), t);
		}
		if (n === "sec") {
			const t = (i) => 1 / Math.cosh(i);
			return p(s, (i) => 1 - (t(i * z) - 0.5) * 2);
		}
		throw new TypeError(
			"Unexpected parameters are delivered in hyper timing function."
		);
	}

	function N(n, s) {
		if (n === "sin") {
			const t = (i) => (Math.asin(i) / Math.PI) * 2;
			return p(s, (i) => 1 - t(1 - i), t);
		}
		if (n === "tan") {
			const t = (i) => Math.atan(i * 5) / P;
			return p(s, (i) => 1 - t(1 - i), t);
		}
		throw new TypeError(
			"Unexpected parameters are delivered in inverse trigo timing function."
		);
	}

	function B(n, s = () => 1) {
		let t = -1;
		return (e) => (
			(t *= -1), e < 0.5 ? n * s(e * 2) * t : n * s((1 - e) * 2) * t
		);
	}

	function D(n, s = 1, t = [0, 0], e = 0, i = (h) => 1, r = !1) {
		return (h) => {
			const l = s * h * Math.PI * 2 + (e * Math.PI) / 180,
				u = Math.cos(l),
				c = Math.sin(l),
				a = n * i(i(r ? 1 - h : h));
			return [a * u + t[0], a * c + t[1]];
		};
	}

	function H(n, s, ...t) {
		const e = [n].concat(t);
		e.push(s);
		const i = e.length,
			r = Array(i)
			.fill(0)
			.map((h, l) => A(l, i - 1));
		return (h) => {
			const l = r.map(
					(c, a) => c * e[a][0] * (1 - h) ** (i - a - 1) * h ** a
				),
				u = r.map((c, a) => c * e[a][1] * (1 - h) ** (i - a - 1) * h ** a);
			return [x(...l), x(...u)];
		};
	}

	if ("animate" in core.plugin)
		throw new ReferenceError(`插件中已存在名为animate的属性！`);

	core.plugin.animate = {
		Animation: Y,
		AnimationBase: F,
		Ticker: I,
		Transition: j,
		sleep: R,
		circle: D,
		bezierPath: H,
		linear: O,
		bezier: q,
		trigo: U,
		power: C,
		hyper: G,
		inverseTrigo: N,
		shake: B,
	};
},
    "老虎机": function () {
	// 老虎机小游戏 - 精准布局修复版（结果位置修正）并合并开始/停止按钮
	this.initSlotGame = function () {
		const bgCtx = core.createCanvas('slot_bg', 0, 0, 352, 352, 132);
		const reelsCtx = core.createCanvas('slot_reels', 0, 0, 352, 352, 133);
		const uiCtx = core.createCanvas('slot_ui', 0, 0, 352, 352, 134);

		// 物品价值从低到高排序
		const itemTiers = [
			'coin',
			'yellowKey',
			'redPotion',
			'blueGem',
			'redGem',
			'bluePotion',
			'blueKey',
			'redKey',
			'bigKey',
			'centerFly',
			'bomb',
			'pickaxe',
			'earthquake',
			'superPotion'
		];

		const weightMap = {
			'coin': 20,
			'yellowKey': 25,
			'redPotion': 20,
			'blueGem': 15,
			'redGem': 12,
			'bluePotion': 10,
			'blueKey': 8,
			'redKey': 6,
			'bigKey': 5,
			'centerFly': 4,
			'bomb': 3,
			'pickaxe': 2,
			'earthquake': 1,
			'superPotion': 1
		};

		const weights = itemTiers.map(item => weightMap[item]);
		core.setFlag('itemValues', itemTiers.reduce((obj, item, idx) => {
			obj[item] = idx + 1;
			return obj;
		}, {}));

		const state = {
			spinning: false,
			stopping: [false, false, false],
			stopped: [false, false, false],
			reels: [
				[],
				[],
				[]
			],
			results: [],
			winInfo: null,
			reelPositions: [70, 70, 70], // 初始位置改为70（中间行）
			reelSpeeds: [0, 0, 0],
			gameHistory: [],
			stopSequence: 0,
			hoverButton: null,
			config: {
				reelCount: 3,
				symbolCountPerReel: 5,
				spinCost: core.getFlag('投注金币', 25),
				basePayoutMultiplier: 25
			},
			sessionRecords: [] // 存储本局游戏的操作记录
		};

		// 带权重的随机物品选择
		function getRandomItemByWeight() {
			const totalWeight = weights.reduce((a, b) => a + b, 0);
			let random = Math.floor(Math.random() * totalWeight);
			for (let i = 0; i < itemTiers.length; i++) {
				random -= weights[i];
				if (random < 0) {
					return itemTiers[i];
				}
			}
			return itemTiers[0];
		}

		function drawBackground() {
			const bg = core.getContextByName('slot_bg');
			core.drawWindowSkin('winskin.png', bg, 0, 0, 352, 352);

			core.setTextBaseline(bg, 'middle');
			core.setTextAlign(bg, 'center');

			// 标题居中
			core.fillBoldText(
				bg, '物品老虎机', 176, 30,
				'#FFD700', 'black', 'bold 18px Arial'
			);
		}

		function initReels() {
			for (let i = 0; i < 3; i++) {
				state.reels[i] = [];
				for (let j = 0; j < 5; j++) {
					state.reels[i].push(getRandomItemByWeight());
				}
			}
			drawReels();
		}

		function drawReels() {
			const ctx = core.getContextByName('slot_reels');
			core.clearMap(ctx);
			const reelWidth = 90,
				symbolHeight = 70,
				gap = 10;

			// 精确计算转轴位置 - 居中显示
			const reelTopY = 71; // 转轴顶部位置
			const reelHeight = symbolHeight * 3; // 三个图标高度

			for (let i = 0; i < 3; i++) {
				let reelX = 31 + i * (reelWidth + gap); // 水平居中位置

				// 绘制转轴背景
				ctx.fillStyle = '#333';
				ctx.fillRect(reelX, reelTopY, reelWidth, reelHeight);
				ctx.strokeStyle = '#FFD700';
				ctx.strokeRect(reelX, reelTopY, reelWidth, reelHeight);

				// +++ 新增：绘制中间行高亮框 +++
				const highlightY = reelTopY + symbolHeight; // 中间行位置
				ctx.fillStyle = 'rgba(255, 215, 0, 0.3)'; // 半透明金色
				ctx.fillRect(reelX, highlightY, reelWidth, symbolHeight);
				ctx.strokeStyle = '#FFFF00'; // 亮黄色边框
				ctx.lineWidth = 2;
				ctx.strokeRect(reelX, highlightY, reelWidth, symbolHeight);
				ctx.lineWidth = 1; // 恢复默认线宽

				// === 关键修复：添加裁剪区域 ===
				ctx.save();
				ctx.beginPath();
				ctx.rect(reelX, reelTopY, reelWidth, reelHeight);
				ctx.clip();

				// 循环滚动逻辑 - 只绘制3个可见物品
				const visibleSymbols = 3; // 只显示3个物品
				const totalOffset = state.reelPositions[i];
				const iconSize = 32;

				for (let j = 0; j < visibleSymbols; j++) {
					const y = reelTopY + j * symbolHeight - (totalOffset % symbolHeight);
					const symbolIndex = Math.floor((totalOffset + j * symbolHeight) / symbolHeight) % 5;
					core.drawIcon(ctx, state.reels[i][symbolIndex],
						reelX + (reelWidth - iconSize) / 2,
						y + (symbolHeight - iconSize) / 2,
						iconSize, iconSize);
				}

				// 结束裁剪
				ctx.restore();
			}
		}

		const CELL = 32;
		const BTN_LAYOUT = {
			// 合并开始和停止按钮为同一个按钮
			action: { x: 2, y: 10, w: 2, h: 1 },
			cancel: { x: 9, y: 10, w: 2, h: 1 }
		};

		function drawUI() {
			const ctx = core.getContextByName('slot_ui');
			core.clearMap(ctx);

			core.setTextBaseline(ctx, 'middle');
			core.setTextAlign(ctx, 'center');

			// 画按钮
			for (const [key, btn] of Object.entries(BTN_LAYOUT)) {
				const px = (btn.x - 1) * CELL;
				const py = (btn.y - 1) * CELL;
				const pw = btn.w * CELL;
				const ph = btn.h * CELL;

				let label, active;

				if (key === 'action') {
					// 根据游戏状态显示不同文本
					label = state.spinning ? '停止' : '开始';
					// 激活条件：
					// 非旋转状态：金币足够
					// 旋转状态：转轴未全部停止
					active = state.spinning ?
						!state.stopped.every(s => s) :
						core.status.hero.money >= state.config.spinCost;
				} else { // cancel按钮
					label = '离开';
					active = true;
				}

				if (state.hoverButton === key && active) {
					ctx.fillStyle = '#3E8E41';
				} else {
					ctx.fillStyle = active ? '#4CAF50' : '#777';
				}

				if (!active) ctx.globalAlpha = 0.6;
				ctx.fillRect(px, py, pw, ph);
				ctx.globalAlpha = 1.0;

				ctx.fillStyle = '#FFF';
				ctx.textAlign = 'center';
				ctx.font = 'bold 16px Arial';
				ctx.fillText(label, px + pw / 2, py + ph / 2);

				// 显示金币不足提示
				if (key === 'action' && !state.spinning && !active) {
					core.fillBoldText(
						ctx,
						`需要${state.config.spinCost}金币`,
						px + pw / 2, py + ph + 5,
						'#FF5555', 'black', '12px Arial'
					);
				}
			}

			// 结果信息位置优化 - 避免与按钮重叠
			if (state.winInfo) {
				const winY = 300;
				const color = state.winInfo.won ? '#00FF00' : '#FF5555';
				const text = state.winInfo.won ?
					`恭喜！获得 ${state.winInfo.item === '金币' ? '金币': core.material.items[state.winInfo.item].name}×${state.winInfo.amount}` :
					'未中奖';

				core.fillBoldText(
					ctx,
					text,
					176, winY,
					color, 'black', 'bold 14px Arial'
				);
			}

			// 转轴状态位置优化
			if (state.spinning) {
				const statusY = 320;
				const stoppedCount = state.stopped.filter(s => s).length;
				const statusText = state.stopped.every(s => s) ?
					"已停止" :
					`旋转中 ${stoppedCount}/3 已停止`;

				core.fillBoldText(
					ctx,
					statusText,
					176, statusY,
					state.stopped.every(s => s) ? '#00FF00' : '#FFD700',
					'black', 'bold 16px Arial'
				);
			}
		}

		function startSpin() {
			const betAmount = state.config.spinCost;

			// 检查金币是否足够
			if (state.spinning || core.status.hero.money < betAmount) {
				core.drawTip(`金币不足，需要${betAmount}金币`);
				return;
			}

			// 扣除金币
			core.status.hero.money -= betAmount;
			core.updateStatusBar();

			// 记录消耗金币操作
			state.sessionRecords.push(`game:money:-${betAmount}`);

			core.playSound('Coin.ogg');

			// 重置状态
			state.spinning = true;
			state.stopping = [false, false, false];
			state.stopped = [false, false, false];
			state.winInfo = null;
			state.stopSequence = 0;

			// 生成结果（使用权重随机）
			state.results = [
				getRandomItemByWeight(),
				getRandomItemByWeight(),
				getRandomItemByWeight()
			];

			// 生成完整转轴序列
			for (let i = 0; i < 3; i++) {
				const reelItems = [];
				for (let j = 0; j < 5; j++) {
					reelItems.push(getRandomItemByWeight());
				}
				reelItems[2] = state.results[i]; // 中间位置设为结果
				state.reels[i] = reelItems;
				state.reelSpeeds[i] = 12 + Math.random() * 8;
				// 保持初始位置为70（中间行）
			}

			drawUI();
			animateSpin();
		}

		function stopSpin() {
			if (!state.spinning || state.stopSequence > 0) return;

			// 开始从左到右依次停止
			state.stopSequence = 1;
			stopNextReel(0);
		}

		function stopNextReel(index) {
			if (index >= 3) return;

			// 停止当前转轴
			state.stopping[index] = true;

			// 播放停止音效
			core.playSound('stop.mp3');

			// 设置下一个转轴的停止（间隔500毫秒）
			if (index < 2) {
				setTimeout(() => {
					stopNextReel(index + 1);
				}, 500);
			}
		}

		function animateSpin() {
			if (!state.spinning) return;

			let allStopped = true;
			let anyMoving = false;

			for (let i = 0; i < 3; i++) {
				if (!state.stopped[i]) {
					allStopped = false;

					// 更新位置
					state.reelPositions[i] += state.reelSpeeds[i];

					// 循环滚动（350=5个图标*70高度）
					if (state.reelPositions[i] >= 350) {
						state.reelPositions[i] -= 350;
					}

					// 停止处理
					if (state.stopping[i]) {
						const symbolHeight = 70;
						const targetPosition = 70; // 修正：精确停止在中间位置 (1*70)
						const currentInCycle = state.reelPositions[i] % 350;

						// 计算到目标位置的距离
						let distance = targetPosition - currentInCycle;
						if (distance < 0) distance += 350;

						// 动态减速（距离越近速度越慢）
						state.reelSpeeds[i] = distance * 0.1;

						// 精确停止检测
						if (distance < 1 && Math.abs(state.reelSpeeds[i]) < 0.1) {
							state.reelSpeeds[i] = 0;
							state.reelPositions[i] = currentInCycle + distance;
							if (state.reelPositions[i] >= 350) state.reelPositions[i] -= 350;
							state.stopped[i] = true;
						}
					} else {
						anyMoving = true;
					}
				}
			}

			drawReels();
			drawUI();

			if (allStopped) {
				// 所有转轴都停止后检查结果
				checkResult();
			} else {
				requestAnimationFrame(animateSpin);
			}
		}

		function checkResult() {
			const betAmount = state.config.spinCost;
			const multiplier = Math.max(1, Math.ceil(betAmount / state.config.basePayoutMultiplier));
			const [a, b, c] = state.results;

			let won = false;
			let winItem = null;
			let winAmount = 0;

			// 检查中奖情况
			if (a === b && b === c) {
				core.playSound('Applause1.ogg');
				// 三个相同
				won = true;
				winItem = a;
				winAmount = 10 * multiplier;
			} else if (a === b || a === c || b === c) {
				core.playSound('Shop2.ogg');
				// 两个相同
				won = true;
				winItem = a === b ? a : (a === c ? a : b);
				winAmount = 1 * multiplier;
			}

			// 特殊处理coin奖励
			if (won && winItem === 'coin') {
				let coinReward = 0;
				if (winAmount === 10 * multiplier) {
					coinReward = betAmount * 10; // 三连coin返还10倍投注
				} else {
					coinReward = Math.floor(betAmount / 2); // 二连coin返还一半
				}
				core.status.hero.money += coinReward;
				core.updateStatusBar();
				state.winInfo = {
					won: true,
					item: '金币',
					amount: coinReward
				};

				// 记录获得金币操作
				state.sessionRecords.push(`game:money:${coinReward}`);
			}
			// 其他物品奖励
			else if (won) {
				core.getItem(winItem, winAmount);
				state.winInfo = {
					won: true,
					item: winItem,
					amount: winAmount
				};

				// 记录获得物品操作
				state.sessionRecords.push(`game:${winItem}:${winAmount}`);
			} else {
				state.winInfo = { won: false };
				core.playSound('Disappointment.ogg');
			}

			// 记录本次游戏
			state.gameHistory.push({
				results: [...state.results],
				winInfo: state.winInfo ? {
					won: state.winInfo.won,
					item: state.winInfo.item,
					amount: state.winInfo.amount
				} : null,
				timestamp: Date.now()
			});

			// 重置旋转状态
			state.spinning = false;
			state.stopping = [false, false, false];
			state.stopped = [false, false, false];
			state.stopSequence = 0;

			drawUI();
		}

		function exitGame() {
			if (state.spinning) {
				// 强制停止并检查结果
				state.spinning = false;
				for (let i = 0; i < 3; i++) {
					state.stopping[i] = true;
					state.reelSpeeds[i] = 0;
					state.reelPositions[i] = 70; // 停止在中间位置
					state.stopped[i] = true;
				}
				drawReels();
				checkResult();
			}

			// 显示结束信息
			let message = `游戏结束！\n共游玩${state.gameHistory.length}局`;
			core.drawTip(message);

			core.status.route = core.getFlag('存储录像', []).concat(state.sessionRecords);

			// 清理资源
			core.clearMap('slot_bg');
			core.clearMap('slot_reels');
			core.clearMap('slot_ui');
			core.unregisterAction('onclick', 'slot_click');
			core.unregisterAction('keyUp', 'slot_keyup');
			core.unregisterAction('mousemove', 'slot_mousemove');
			core.doAction();
		}

		function handleClick(gridX, gridY) {
			gridX += 1; // 转换为1-based坐标
			gridY += 1; // 转换为1-based坐标

			// 检查按钮点击（使用优化后的布局）
			for (const [action, btn] of Object.entries(BTN_LAYOUT)) {
				if (
					gridX >= btn.x && gridX < btn.x + btn.w &&
					gridY >= btn.y && gridY < btn.y + btn.h
				) {
					if (action === 'action') {
						if (!state.spinning) {
							startSpin();
						} else if (!state.stopped.every(s => s)) {
							stopSpin();
						}
					}
					if (action === 'cancel') {
						exitGame();
					}
					return;
				}
			}
		}

		function handleKeyUp(code) {
			// 空格键：开始/停止
			if (code === 32) {
				if (!state.spinning) {
					startSpin();
				} else if (!state.stopped.every(s => s)) {
					stopSpin();
				}
			}
			// ESC键退出
			if (code === 27) {
				exitGame();
			}
		}

		function handleMouseMove(gridX, gridY) {
			gridX += 1; // 转换为1-based坐标
			gridY += 1; // 转换为1-based坐标
			let newHover = null;

			for (const [action, btn] of Object.entries(BTN_LAYOUT)) {
				if (
					gridX >= btn.x && gridX < btn.x + btn.w &&
					gridY >= btn.y && gridY < btn.y + btn.h
				) {
					newHover = action;
					break;
				}
			}

			if (newHover !== state.hoverButton) {
				state.hoverButton = newHover;
				drawUI(); // 重绘UI更新按钮状态
			}
		}

		drawBackground();
		initReels();
		drawUI();
		core.lockControl();

		core.registerAction('onclick', 'slot_click', handleClick, 100);
		core.registerAction('keyUp', 'slot_keyup', handleKeyUp, 100);
		core.registerAction('mousemove', 'slot_mousemove', handleMouseMove, 100);
	};
},
    "Flappy Hero": function () {
	// 管道小鸟小游戏 - 功能增强最终版 (性能优化)
	this.startPipeBirdGame = function () {

		// ===================================================================
		// I. 配置 (CONFIG)
		// ===================================================================
		const CONFIG = {
			CANVAS_WIDTH: 352,
			CANVAS_HEIGHT: 352,
			TILE_SIZE: 32,
			MAX_FPS: 60,
			BIRD: {
				START_X: 50,
				START_Y: 150,
				WIDTH: 15,
				HEIGHT: 30,
				GRAVITY: 1500,
				JUMP_FORCE: -300,
				ANIMATION_SPEED: 4
			},
			PIPES: {
				WIDTH: 64,
				GAP: 96,
				MIN_HEIGHT: 1,
				MAX_HEIGHT: 7,
				MOVE_SPEED: 90
			},
			UI: {
				SCORE_FONT: "bold 20px Arial",
				TITLE_FONT: "bold 24px Arial",
				BUTTON_FONT: "bold 16px Arial",
				BUTTON_RADIUS: 8,
				BUTTON_DEFS: {
					START: { x: 4, y: 9, width: 5, height: 1 },
					RESTART: { x: 4, y: 7, width: 5, height: 1 },
					EXIT: { x: 10, y: 1, width: 2, height: 1 },
					CLASSIC: { x: 4, y: 4, width: 5, height: 1 },
					STORY: { x: 4, y: 6, width: 5, height: 1 },
					VICTORY_NEXT: { x: 4, y: 7, width: 5, height: 1 },
					VICTORY_FINISH: { x: 4, y: 9, width: 5, height: 1 }
				}
			},
			BOSS_LIST: [{
					name: "骷髅队长",
					type: "SkeletonCaptain",
					pipeSkin: 'yellowWall',
					config: {
						BONEWALL: {
							SPAWN_DELAY: 0.1,
							MIN_BOSS_HEIGHT: 8,
							MAX_BOSS_HEIGHT: 20,
							MIN_BOSS_WIDTH: 3,
							MAX_BOSS_WIDTH: 10,
							GROWTH_SPEED: 2,
							MAX_STACK_LAYERS: 10,
							SKELETON_HEIGHTS: [16, 23, 28, 30, 32],
							GROWTH_STAGES: 5,
							FRENZY_HEALTH_THRESHOLD: 25,
							FRENZY_MIN_HEIGHT: 12,
							FRENZY_MIN_WIDTH: 6,
						},
						PLATFORM_INTERVAL: {
							MIN_OBSTACLES_LOW_HP: 2,
							MAX_OBSTACLES_LOW_HP: 4,
							MIN_OBSTACLES_HIGH_HP: 6,
							MAX_OBSTACLES_HIGH_HP: 8
						}
					}
				},
				{ name: "吸血鬼", type: "Vampire", pipeSkin: '', config: {} },
				{ name: "骑士队长", type: "YellowKnight", pipeSkin: '', config: {} },
				{ name: "大法师", type: "BlackMagician", pipeSkin: '', config: {} },
				{ name: "魔王", type: "RedKing", pipeSkin: '', config: {} },
			],
			OBSTACLE_INTERVAL: {
				BASE: 2000,
				PLATFORM_GAP_MULTIPLIER: 0.75,
			}
		};

		// ===================================================================
		// II. [新增] 性能优化：对象池 (Object Pooling)
		// ===================================================================
		class PoolManager {
			constructor() {
				this.pools = {};
			}

			_getPool(type) {
				if (!this.pools[type]) {
					this.pools[type] = [];
				}
				return this.pools[type];
			}

			get(type) {
				const pool = this._getPool(type);
				if (pool.length > 0) {
					return pool.pop();
				}
				return { type: type };
			}

			release(obj) {
				const pool = this._getPool(obj.type);
				pool.push(obj);
			}
		}

		// ===================================================================
		// III. 游戏主引擎 (Game Class)
		// ===================================================================
		class Game {
			constructor() {
				this.canvases = {
					bg: core.createCanvas('pipe_bird_bg', 0, 0, CONFIG.CANVAS_WIDTH, CONFIG.CANVAS_HEIGHT, 132),
					game: core.createCanvas('pipe_bird_game', 0, 0, CONFIG.CANVAS_WIDTH, CONFIG.CANVAS_HEIGHT, 134),
					ui: core.createCanvas('pipe_bird_ui', 0, 0, CONFIG.CANVAS_WIDTH, CONFIG.CANVAS_HEIGHT, 135)
				};
				this.ctx = { bg: this.canvases.bg, game: this.canvases.game, ui: this.canvases.ui };
				this.state = {
					gameStarted: false,
					gameOver: false,
					victory: false,
					score: 0,
					betAmount: core.getFlag('投注金币', 0),
					insufficientFunds: false,
					lastFrameTime: 0,
					sessionRecords: [],
					level: 0,
					totalReward: 0,
					mode: 'Classic',
					bossName: ''
				};
				// 新增：游戏退出标志
				this.isExited = false;
				this.poolManager = new PoolManager();
				this.bird = new Bird(this);
				this.obstacleManager = new ObstacleManager(this, this.poolManager);
				this.uiManager = new UIManager(this);
				this.inputHandler = new InputHandler(this);
				this.gameMode = null;
				this.active = false;
				this.animationTimer = 0;
			}
			init() {
				this.drawBackground();
				this.uiManager.draw();
				this.inputHandler.register();
				core.lockControl();
			}
			startGame(modeType, level = 0) {
				if (core.status.hero.money < this.state.betAmount) {
					this.state.insufficientFunds = true;
					this.uiManager.draw();
					return;
				}
				this.uiManager.selectedLevel = level;
				this.gameMode = modeType === 'Classic' ? new ClassicMode(this) : new StoryMode(this);
				this.gameMode.init();
				this.active = true;
				this.gameLoop();
			}
			update(dt) {
				if (!this.active) return;
				this.animationTimer += dt;
				this.bird.update(dt);
				this.obstacleManager.update(dt, this.gameMode.getSpeedMultiplier());
				this.gameMode.update(dt);
			}
			draw() {
				if (!this.active) return;
				core.clearMap(this.ctx.game);
				this.obstacleManager.draw(this.ctx.game, this.animationTimer);
				this.bird.draw(this.ctx.game);
				this.gameMode.draw(this.ctx.game);
			}
			drawBackground() {
				const cols = Math.ceil(CONFIG.CANVAS_WIDTH / CONFIG.TILE_SIZE),
					rows = Math.ceil(CONFIG.CANVAS_HEIGHT / CONFIG.TILE_SIZE);
				for (let y = 0; y < rows; y++)
					for (let x = 0; x < cols; x++) core.drawIcon(this.ctx.bg, 'ground', x * CONFIG.TILE_SIZE, y * CONFIG.TILE_SIZE);
			}
			gameLoop() {
				if (!this.active) return;
				const now = Date.now(),
					dt = this.state.lastFrameTime > 0 ? Math.min((now - this.state.lastFrameTime) / 1000, 1 / CONFIG.MAX_FPS) : 0;
				this.state.lastFrameTime = now;
				this.update(dt);
				this.draw();
				this.uiManager.draw();
				requestAnimationFrame(() => this.gameLoop());
			}
			setGameOver() {
				this.active = false;
				this.state.gameOver = true;
				this.gameMode.onGameOver();
				this.uiManager.draw();
			}
			setVictory() {
				this.active = false;
				this.state.victory = true;
				this.gameMode.onVictory();
				this.uiManager.draw();
			}
			exit() {
				// 设置退出标志
				this.isExited = true;
				const tempRecords = core.getFlag('存储录像', []);
				core.status.route = tempRecords.concat(this.state.sessionRecords);
				Object.values(this.canvases).forEach(canvas => core.deleteCanvas(canvas.canvas.id));
				this.inputHandler.unregister();
				core.clearUIEventSelector();
				core.closePanel();
				core.unlockControl();
			}
		}

		// ===================================================================
		// IV. 游戏模式策略 (Game Mode Strategies)
		// ===================================================================
		class GameModeBase {
			constructor(game) {
				this.game = game;
				this.state = game.state;
			}
			init() {
				Object.assign(this.state, {
					gameStarted: true,
					gameOver: false,
					victory: false,
					score: 0,
					lastFrameTime: 0,
					insufficientFunds: false,
					level: this.game.uiManager.selectedLevel,
					totalReward: this.game.uiManager.totalReward,
				});
				core.status.hero.money -= this.state.betAmount;
				core.updateStatusBar();
				this.state.sessionRecords.push(`game:money:-${this.state.betAmount}`);
				this.game.bird.reset();
				this.game.obstacleManager.reset();
			}
			update(dt) {}
			draw(ctx) {}
			onGameOver() {}
			onVictory() {}
			getPipeSkin() { return 'yellowWall'; }
			getSpeedMultiplier() { return 1.0; }
		}

		class ClassicMode extends GameModeBase {
			constructor(game) {
				super(game);
				this.state.mode = 'Classic';
			}
			init() {
				super.init();
				this.game.obstacleManager.startSpawning('PipeOnly');
			}
			onGameOver() {
				core.playSound('炸弹');
				const reward = this.state.score * this.state.betAmount;
				core.status.hero.money += reward;
				core.updateStatusBar();
				core.drawTip(`游戏结束！获得${reward}金币！`);
				this.state.sessionRecords.push(`game:money:${reward}`);
			}
		}

		class StoryMode extends GameModeBase {
			constructor(game) {
				super(game);
				this.state.mode = 'Story';
				this.currentBoss = null;
				// 【新增】玩法提示状态
				this.showLevelHint = false;
				this.hintTimer = 0;
			}
			init() {
				super.init();
				this.loadLevel(this.state.level);
			}
			loadLevel(levelIndex) {
				const bossInfo = CONFIG.BOSS_LIST[levelIndex];
				if (!bossInfo) { this.game.exit(); return; }
				const BossClass = bossClasses[bossInfo.type];
				this.currentBoss = new BossClass(this.game, this, bossInfo.config);
				this.state.bossName = bossInfo.name;
				this.game.obstacleManager.startSpawning('Boss');
				core.playSound('10F.mp3');
				core.drawTip(`${this.state.bossName}出现了！`);

				// 【新增】如果是第一关，则显示玩法提示
				if (levelIndex === 0) {
					this.showLevelHint = true;
					this.hintTimer = 5; // 提示显示5秒
				}
			}
			update(dt) {
				// 【新增】更新提示计时器
				if (this.showLevelHint) {
					this.hintTimer -= dt;
					if (this.hintTimer <= 0) {
						this.showLevelHint = false;
					}
				}

				if (this.currentBoss) {
					this.currentBoss.update(dt);
					if (this.currentBoss.isDefeated()) this.game.setVictory();
				}
			}
			draw(ctx) { if (this.currentBoss) this.currentBoss.draw(ctx); }
			onGameOver() {
				core.playSound('炸弹');
				core.drawTip(`故事模式中途失败无金币奖励！`);
			}
			onVictory() {
				core.playSound('成就');
				const levelReward = this.state.score * this.state.betAmount;
				this.state.totalReward += levelReward;
				this.game.uiManager.totalReward = this.state.totalReward;
				const newUnlocked = Math.max(core.getFlag('unlocked_level', 0), this.state.level + 1);
				core.setFlag('unlocked_level', newUnlocked);
				core.status.hero.money += levelReward;
				core.updateStatusBar();
				core.drawTip(`击败${this.state.bossName}获得${levelReward}金币！`);
				this.state.sessionRecords.push(`game:boss:${levelReward}`);
			}
			proceedToNextLevel() {
				this.game.uiManager.selectedLevel++;
				if (this.game.uiManager.selectedLevel < CONFIG.BOSS_LIST.length) this.game.startGame('Story', this.game.uiManager.selectedLevel);
				else this.game.exit();
			}
			getPipeSkin() { return CONFIG.BOSS_LIST[this.state.level].pipeSkin || 'yellowWall'; }
			getSpeedMultiplier() { return this.currentBoss ? 1 + (1 - this.currentBoss.health / 100) : 1.0; }
		}

		// ===================================================================
		// V. Boss 策略 (Boss Strategies)
		// ===================================================================
		class BossBase {
			constructor(game, storyMode, config) {
				this.game = game;
				this.storyMode = storyMode;
				this.config = config;
				this.health = 100;
				this.state = game.state;
				this.prevHealth = 100;
			}
			update(dt) {
				if (this.health < this.prevHealth) {
					const frenzyThreshold = this.config.BONEWALL?.FRENZY_HEALTH_THRESHOLD || 20;
					if (this.health <= frenzyThreshold && this.prevHealth > frenzyThreshold) {
						core.playSound('10F.mp3');
						core.drawTip(`${this.state.bossName}进入狂暴状态！`);
					} else if (this.prevHealth - this.health > 5) { core.playSound('骷髅死亡'); }
					this.prevHealth = this.health;
				}
			}
			draw(ctx) {}
			createSpecialObstacle() { return false; }
			spawnPlatformIfNeeded() {}
			onCollision(obstacle) { return false; }
			isDefeated() { return this.health <= 0; }
			takeDamage(amount) { this.health = Math.max(0, this.health - amount); }
		}

		class SkeletonCaptain extends BossBase {
			constructor(game, storyMode, config) {
				super(game, storyMode, config);
				this.bonewall = null;
				this.obstacleCounter = 0;
				this.nextPlatformThreshold = this._randInt(this.config.PLATFORM_INTERVAL.MIN_OBSTACLES_HIGH_HP, this.config.PLATFORM_INTERVAL.MAX_OBSTACLES_HIGH_HP);
			}
			update(dt) { super.update(dt); if (this.bonewall) this._updateBoneWall(dt); }

			createSpecialObstacle() {
				this.obstacleCounter++;
				const rand = Math.random();
				const healthPercent = this.health / 100;
				const bonewallChance = healthPercent <= 0.2 ? 0.9 : (healthPercent <= 0.5 ? 0.7 : 0.5);
				if (rand < bonewallChance && !this.bonewall) {
					const position = Math.random() < 0.5 ? 'top' : 'bottom';
					const size = this._getBonewallSize();
					this._startBoneWall(position, this._randInt(size.minHeight, size.maxHeight), this._randInt(size.minWidth, size.maxWidth));
					return true;
				}
				return false;
			}

			spawnPlatformIfNeeded() {
				if (this.obstacleCounter >= this.nextPlatformThreshold) {
					this.game.obstacleManager.scheduleNextSpawnAsPlatform();
					this.obstacleCounter = 0;
					const C = this.config.PLATFORM_INTERVAL,
						healthPercent = Math.max(0, this.health / 100);
					const minObstacles = C.MIN_OBSTACLES_LOW_HP + healthPercent * (C.MIN_OBSTACLES_HIGH_HP - C.MIN_OBSTACLES_LOW_HP);
					const maxObstacles = C.MAX_OBSTACLES_LOW_HP + healthPercent * (C.MAX_OBSTACLES_HIGH_HP - C.MAX_OBSTACLES_LOW_HP);
					this.nextPlatformThreshold = this._randInt(Math.round(minObstacles), Math.round(maxObstacles));
				}
			}

			onCollision(obstacle) {
				if (obstacle.type === 'platform') {
					core.playSound('攻击');
					core.drawTip(`撞击${this.state.bossName}，对其造成5点伤害！`);
					this.takeDamage(5);
					this.game.obstacleManager.remove(obstacle);
					return true;
				}
				return false;
			}

			_randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }

			_getBonewallSize() {
				const difficulty = 1 - (this.health / 100);
				const C = this.config.BONEWALL;
				const isFrenzy = this.health <= C.FRENZY_HEALTH_THRESHOLD;
				const minHeight = isFrenzy ? C.FRENZY_MIN_HEIGHT : C.MIN_BOSS_HEIGHT;
				const minWidth = isFrenzy ? C.FRENZY_MIN_WIDTH : C.MIN_BOSS_WIDTH;
				let maxHeight = C.MIN_BOSS_HEIGHT + Math.round(difficulty * (C.MAX_BOSS_HEIGHT - C.MIN_BOSS_HEIGHT));
				let maxWidth = C.MIN_BOSS_WIDTH + Math.round(difficulty * (C.MAX_BOSS_WIDTH - C.MIN_BOSS_WIDTH));
				maxHeight = Math.max(minHeight, maxHeight);
				maxWidth = Math.max(minWidth, maxWidth);
				return { minHeight, maxHeight, minWidth, maxWidth };
			}

			_startBoneWall(position, targetHeight, width) {
				const bonewallId = Date.now();
				this.bonewall = {
					position,
					targetHeight,
					width,
					id: bonewallId,
					currentHeight: 0,
					timer: 0,
					x: CONFIG.CANVAS_WIDTH,
					columns: Array.from({ length: width }, () => []),
					growthProgress: new Array(width).fill(0),
				};
				const scorer = this.game.poolManager.get('bonewall_scorer');
				scorer.x = CONFIG.CANVAS_WIDTH;
				scorer.y = 0;
				scorer.width = width * CONFIG.TILE_SIZE;
				scorer.height = CONFIG.CANVAS_HEIGHT;
				scorer.isScorable = true;
				scorer.passed = false;
				scorer.bonewallId = bonewallId;
				this.game.obstacleManager.add(scorer);
			}

			_updateBoneWall(dt) {
				const bw = this.bonewall,
					moveSpeed = CONFIG.PIPES.MOVE_SPEED * this.storyMode.getSpeedMultiplier();
				bw.x -= moveSpeed * dt;

				if (bw.x + bw.width * CONFIG.TILE_SIZE < 0) {
					this.game.obstacleManager.releaseBonewall(bw.id);
					this.bonewall = null;
					return;
				}

				const C = this.config.BONEWALL;
				bw.timer += dt;
				if (bw.timer >= C.SPAWN_DELAY && bw.currentHeight < bw.targetHeight) {
					bw.timer = 0;
					const numCols = this._randInt(1, Math.min(3, bw.width)),
						growthCols = [];
					while (growthCols.length < numCols) { const col = this._randInt(0, bw.width - 1); if (!growthCols.includes(col)) growthCols.push(col); }
					for (const col of growthCols) {
						const layers = bw.columns[col];
						if (layers.length >= C.MAX_STACK_LAYERS) continue;
						const yPos = (bw.position === 'top') ? (layers.length > 0 ? layers[layers.length - 1].y + layers[layers.length - 1].height : 0) : (layers.length > 0 ? layers[layers.length - 1].y - C.SKELETON_HEIGHTS[0] : CONFIG.CANVAS_HEIGHT - C.SKELETON_HEIGHTS[0]);

						const block = this.game.poolManager.get('bonewall');
						block.x = bw.x + col * CONFIG.TILE_SIZE;
						block.y = yPos;
						block.width = CONFIG.TILE_SIZE;
						block.height = C.SKELETON_HEIGHTS[0];
						block.growthStage = 0;
						block.position = bw.position;
						block.bonewallId = bw.id;
						block.passed = true;

						layers.push(block);
						this.game.obstacleManager.add(block);
					}
					bw.currentHeight++;
				}
				for (let col = 0; col < bw.width; col++) {
					if (!bw.columns[col]) continue;
					bw.columns[col].forEach((block, i) => {
						if (block.growthStage < C.GROWTH_STAGES - 1) {
							bw.growthProgress[col] += dt * (i === 0 ? 2.5 : 1.8) * C.GROWTH_SPEED;
							if (bw.growthProgress[col] >= 1) {
								bw.growthProgress[col] = 0;
								block.growthStage++;
								const oldHeight = block.height;
								block.height = C.SKELETON_HEIGHTS[block.growthStage];
								const pushHeight = block.height - oldHeight;
								for (let j = i + 1; j < bw.columns[col].length; j++) bw.columns[col][j].y += (bw.position === 'top' ? pushHeight : -pushHeight);
							}
						}
						block.x = bw.x + col * CONFIG.TILE_SIZE;
					});
				}
			}
		}

		class Vampire extends BossBase {
			constructor(game, storyMode, config) { super(game, storyMode, config); }
			update(dt) { super.update(dt); }
			createSpecialObstacle() { return false; }
			onCollision(obstacle) { return false; }
		}

		const bossClasses = { SkeletonCaptain, Vampire };

		// ===================================================================
		// VI. 实体与管理器 (Entities & Managers)
		// ===================================================================
		class Bird {
			constructor(game) {
				this.game = game;
				this.reset();
			}
			reset() {
				this.x = CONFIG.BIRD.START_X;
				this.y = CONFIG.BIRD.START_Y;
				this.velocity = 0;
				this.frame = 0;
				this.frameTimer = 0;
			}
			update(dt) {
				this.velocity += CONFIG.BIRD.GRAVITY * dt;
				this.y += this.velocity * dt;
				this.frameTimer += dt;
				if (this.frameTimer >= 1 / CONFIG.BIRD.ANIMATION_SPEED) {
					this.frame = (this.frame + 1) % 4;
					this.frameTimer = 0;
				}
				if (this.y < 0) {
					this.y = 0;
					this.velocity = 0;
				}
				if (this.y + CONFIG.BIRD.HEIGHT > CONFIG.CANVAS_HEIGHT) this.game.setGameOver();
			}
			draw(ctx) {
				const sx = (Math.floor(this.frame) % 4) * CONFIG.TILE_SIZE,
					sy = 2 * CONFIG.TILE_SIZE;
				const offsetX = (32 - CONFIG.BIRD.WIDTH) / 2,
					offsetY = 32 - CONFIG.BIRD.HEIGHT;
				core.drawImage(ctx, 'hero.png', sx, sy, CONFIG.TILE_SIZE, CONFIG.TILE_SIZE, this.x - offsetX, this.y - offsetY, 32, 32);
			}
			jump() {
				if (this.game.active) {
					core.playSound('跳跃');
					this.velocity = CONFIG.BIRD.JUMP_FORCE;
				}
			}
		}

		class ObstacleManager {
			constructor(game, poolManager) {
				this.game = game;
				this.pool = poolManager;
				this.obstacles = [];
				this.spawnTimer = 0;
				this.spawnMode = 'None';
				this.isNextSpawnPlatform = false;
			}
			reset() {
				this.obstacles.forEach(obs => this.pool.release(obs));
				this.obstacles = [];
				this.spawnTimer = 0;
				this.isNextSpawnPlatform = false;
			}
			startSpawning(mode) {
				this.spawnMode = mode;
				this.spawnTimer = 1500;
			}
			add(obstacle) { this.obstacles.push(obstacle); }
			remove(obstacle) {
				this.obstacles = this.obstacles.filter(o => o !== obstacle);
				this.pool.release(obstacle);
			}
			releaseBonewall(bonewallId) {
				const toRelease = this.obstacles.filter(o => o.bonewallId === bonewallId);
				toRelease.forEach(obs => this.pool.release(obs));
				this.obstacles = this.obstacles.filter(o => o.bonewallId !== bonewallId);
			}

			scheduleNextSpawnAsPlatform() {
				this.isNextSpawnPlatform = true;
			}

			update(dt, speedMultiplier) {
				const moveSpeed = CONFIG.PIPES.MOVE_SPEED * speedMultiplier;
				for (let i = this.obstacles.length - 1; i >= 0; i--) {
					const obs = this.obstacles[i];
					if (obs.type === 'bonewall_scorer') {
						const boss = this.game.gameMode.currentBoss;
						if (boss && boss.bonewall && boss.bonewall.id === obs.bonewallId) obs.x = boss.bonewall.x;
					} else if (obs.type !== 'bonewall') {
						obs.x -= moveSpeed * dt;
					}

					if (obs.isScorable && !obs.passed && obs.x + obs.width < this.game.bird.x) {
						core.playSound('Shop2.ogg');
						this.game.state.score++;
						obs.passed = true;
					}

					if (this.isColliding(this.game.bird, obs)) {
						let handled = this.game.gameMode.currentBoss ? this.game.gameMode.currentBoss.onCollision(obs) : false;
						if (!handled) { this.game.setGameOver(); return; }
					}
					if (obs.x + obs.width < 0 && obs.type !== 'bonewall') {
						this.pool.release(obs);
						this.obstacles.splice(i, 1);
					}
				}

				this.spawnTimer += dt * 1000;
				let currentInterval = CONFIG.OBSTACLE_INTERVAL.BASE / speedMultiplier;

				if (this.isNextSpawnPlatform) {
					currentInterval *= CONFIG.OBSTACLE_INTERVAL.PLATFORM_GAP_MULTIPLIER;
				}

				if (this.spawnTimer > currentInterval) {
					this.spawnTimer -= currentInterval;

					if (this.spawnMode === 'PipeOnly') {
						this.createPipe();
					} else if (this.spawnMode === 'Boss') {
						if (this.isNextSpawnPlatform) {
							this.createPlatform();
							this.isNextSpawnPlatform = false;
						} else {
							const created = this.game.gameMode.currentBoss.createSpecialObstacle();
							if (!created) {
								this.createPipe();
							}
						}
						this.game.gameMode.currentBoss.spawnPlatformIfNeeded();
					}
				}
			}
			draw(ctx, animationTimer) {
				const frameIndex = Math.floor(animationTimer * 5) % 2;
				this.obstacles.forEach(p => {
					if (p.type === 'platform') p.parts.forEach(part => core.drawIcon(ctx, part.type, p.x + part.offsetX, p.y + part.offsetY, part.width, part.height, frameIndex));
					else if (p.type === 'bonewall') {
						const C = CONFIG.BOSS_LIST[0].config.BONEWALL;
						const spriteIndex = C.GROWTH_STAGES - 1 - p.growthStage;
						const sourceX = spriteIndex * CONFIG.TILE_SIZE;
						const imgName = p.position === 'bottom' ? 'skeleton.png' : 'skeleton.png:o';
						const drawHeight = C.SKELETON_HEIGHTS[p.growthStage];
						let drawY = p.position === 'bottom' ? p.y - drawHeight + CONFIG.TILE_SIZE : p.y;
						core.drawImage(ctx, imgName, sourceX, CONFIG.TILE_SIZE - drawHeight, CONFIG.TILE_SIZE, drawHeight, p.x, drawY, CONFIG.TILE_SIZE, drawHeight);
					} else if (p.type === 'pipe') {
						const cols = Math.ceil(p.width / CONFIG.TILE_SIZE),
							rows = Math.ceil(p.height / CONFIG.TILE_SIZE);
						const skin = this.game.gameMode.getPipeSkin();
						for (let y = 0; y < rows; y++)
							for (let x = 0; x < cols; x++) core.drawIcon(ctx, skin, p.x + x * CONFIG.TILE_SIZE, p.y + y * CONFIG.TILE_SIZE);
					}
				});
			}

			createPipe() {
				const h = Math.floor(Math.random() * (CONFIG.PIPES.MAX_HEIGHT - CONFIG.PIPES.MIN_HEIGHT + 1)) + CONFIG.PIPES.MIN_HEIGHT;

				const pipeTop = this.pool.get('pipe');
				pipeTop.x = CONFIG.CANVAS_WIDTH;
				pipeTop.y = 0;
				pipeTop.width = CONFIG.PIPES.WIDTH;
				pipeTop.height = h * CONFIG.TILE_SIZE;
				pipeTop.passed = false;
				pipeTop.isScorable = false;
				this.add(pipeTop);

				const pipeBottom = this.pool.get('pipe');
				pipeBottom.x = CONFIG.CANVAS_WIDTH;
				pipeBottom.y = h * CONFIG.TILE_SIZE + CONFIG.PIPES.GAP;
				pipeBottom.width = CONFIG.PIPES.WIDTH;
				pipeBottom.height = CONFIG.CANVAS_HEIGHT - pipeBottom.y;
				pipeBottom.passed = false;
				pipeBottom.isScorable = true;
				this.add(pipeBottom);
			}

			createPlatform() {
				let lastObstacleEndX = 0;
				const relevantObstacles = this.obstacles.filter(o => o.type === 'pipe' || o.type === 'bonewall_scorer');
				if (relevantObstacles.length > 0) {
					lastObstacleEndX = Math.max(...relevantObstacles.map(o => o.x + o.width));
				}

				const platform = this.pool.get('platform');
				platform.x = Math.max(CONFIG.CANVAS_WIDTH, lastObstacleEndX);
				platform.y = Math.floor(Math.random() * (CONFIG.CANVAS_HEIGHT - CONFIG.TILE_SIZE * 5)) + CONFIG.TILE_SIZE * 2;
				platform.width = CONFIG.PIPES.WIDTH;
				platform.height = CONFIG.TILE_SIZE;
				platform.passed = true;
				platform.isScorable = false;
				platform.parts = [
					{ offsetX: 0, offsetY: 0, width: CONFIG.TILE_SIZE, height: CONFIG.TILE_SIZE, type: 'skeletonSoldier' },
					{ offsetX: CONFIG.TILE_SIZE, offsetY: 0, width: CONFIG.TILE_SIZE, height: CONFIG.TILE_SIZE, type: 'skeletonSoldier' },
					{ offsetX: CONFIG.TILE_SIZE / 2, offsetY: -CONFIG.TILE_SIZE + 11, width: CONFIG.TILE_SIZE, height: CONFIG.TILE_SIZE, type: 'skeletonCaptain' }
				];
				this.add(platform);
			}

			isColliding(bird, obstacle) {
				if (obstacle.type === 'bonewall_scorer') return false;
				const birdRect = { x: bird.x, y: bird.y, width: CONFIG.BIRD.WIDTH, height: CONFIG.BIRD.HEIGHT };
				if (obstacle.type === 'platform') {
					for (const part of obstacle.parts) { const partRect = { x: obstacle.x + part.offsetX, y: obstacle.y + part.offsetY, width: part.width, height: part.height }; if (this._rectCollision(birdRect, partRect)) return true; }
					return false;
				}
				return this._rectCollision(birdRect, obstacle);
			}
			_rectCollision(r1, r2) { return r1.x < r2.x + r2.width && r1.x + r1.width > r2.x && r1.y < r2.y + r2.height && r1.y + r1.height > r2.y; }
		}

		// ===================================================================
		// VII. UI 和输入 (UI & Input)
		// ===================================================================
		class UIManager {
			constructor(game) {
				this.game = game;
				this.ctx = game.ctx.ui;
				this.selectedMode = 'Classic';
				this.view = 'main';
				this.selectedLevel = 0;
				this.totalReward = 0;
				this.unlockedLevel = 0;
				this.buttons = {};
				for (const key in CONFIG.UI.BUTTON_DEFS) {
					const def = CONFIG.UI.BUTTON_DEFS[key];
					this.buttons[key] = { x: (def.x - 1) * CONFIG.TILE_SIZE, y: (def.y - 1) * CONFIG.TILE_SIZE, width: def.width * CONFIG.TILE_SIZE, height: def.height * CONFIG.TILE_SIZE };
				}
			}
			draw() {
				const state = this.game.state;
				core.clearMap(this.ctx);
				if (state.victory) this.drawVictoryScreen(state);
				else if (state.gameOver) this.drawGameOverScreen(state);
				else if (!state.gameStarted) this.drawStartScreen(state);
				else this.drawHUD(state);
				this.drawButton(this.buttons.EXIT, "×", "#e74c3c", 99);
			}
			drawStartScreen(state) {
				core.setFillStyle(this.ctx, "#f1c40f");
				core.setTextAlign(this.ctx, "center");
				core.setFont(this.ctx, CONFIG.UI.TITLE_FONT);
				core.setTextBaseline(this.ctx, "top");
				core.fillText(this.ctx, "魔塔小勇", CONFIG.CANVAS_WIDTH / 2, 20);
				if (this.view === 'main') {
					this.drawButton(this.buttons.CLASSIC, "经典模式", this.selectedMode === 'Classic' ? '#3498db' : '#2980b9', 1);
					this.drawButton(this.buttons.STORY, "故事模式", this.selectedMode === 'Story' ? '#9b59b6' : '#8e44ad', 2);
					const canStart = core.status.hero.money >= state.betAmount;
					if (this.selectedMode === 'Classic') {
						this.drawButton(this.buttons.START, "开始经典模式", canStart ? "#2ecc71" : "#95a5a6", canStart ? 3 : null);
						if (!canStart) {
							core.setFillStyle(this.ctx, "#e74c3c");
							core.setFont(this.ctx, "14px Arial");
							core.fillText(this.ctx, `金币不足！`, CONFIG.CANVAS_WIDTH / 2, this.buttons.START.y + this.buttons.START.height + 15);
						}
					}
				} else if (this.view === 'level_select') {
					core.setFillStyle(this.ctx, "#ffffff");
					core.setFont(this.ctx, "bold 18px Arial");
					core.fillText(this.ctx, "选择关卡", CONFIG.CANVAS_WIDTH / 2, 80);
					this.unlockedLevel = core.getFlag('unlocked_level', 0);
					CONFIG.BOSS_LIST.forEach((boss, index) => {
						const isUnlocked = index <= this.unlockedLevel;
						const btnRect = { x: 100, y: 120 + index * 40, width: 152, height: 32 };
						const text = isUnlocked ? boss.name : ' locked ';
						this.drawButton(btnRect, text, isUnlocked ? "#3498db" : "#7f8c8d", isUnlocked ? 10 + index : null);
					});
					this.drawButton({ x: 4, y: 280, width: 80, height: 32 }, '返回', '#e67e22', 4);
				}
			}
			drawGameOverScreen(state) {
				core.setFillStyle(this.ctx, "#e74c3c");
				core.setTextAlign(this.ctx, "center");
				core.setFont(this.ctx, CONFIG.UI.TITLE_FONT);
				core.setTextBaseline(this.ctx, "top");
				core.fillText(this.ctx, "游戏结束!", CONFIG.CANVAS_WIDTH / 2, 80);
				core.setFillStyle(this.ctx, "#ffffff");
				core.setFont(this.ctx, "20px Arial");
				core.fillText(this.ctx, `得分: ${state.score}`, CONFIG.CANVAS_WIDTH / 2, 110);
				const rewardText = state.mode === 'Story' ? "故事模式中途失败无金币奖励" : `获得金币: ${state.score * state.betAmount}`;
				core.fillText(this.ctx, rewardText, CONFIG.CANVAS_WIDTH / 2, 140);
				const canRestart = core.status.hero.money >= state.betAmount;
				this.drawButton(this.buttons.RESTART, "重新开始", canRestart ? "#2ecc71" : "#95a5a6", canRestart ? 1 : null);
			}
			drawVictoryScreen(state) {
				const isFinalBoss = state.level === CONFIG.BOSS_LIST.length - 1;
				core.setFillStyle(this.ctx, "#27ae60");
				core.setTextAlign(this.ctx, "center");
				core.setFont(this.ctx, CONFIG.UI.TITLE_FONT);
				core.setTextBaseline(this.ctx, "top");
				core.fillText(this.ctx, "胜利!", CONFIG.CANVAS_WIDTH / 2, 80);
				core.setFillStyle(this.ctx, "#ffffff");
				core.setFont(this.ctx, "20px Arial");
				core.fillText(this.ctx, `击败了${state.bossName}!`, CONFIG.CANVAS_WIDTH / 2, 110);
				core.fillText(this.ctx, `得分: ${state.score}`, CONFIG.CANVAS_WIDTH / 2, 140);
				core.fillText(this.ctx, `获得金币: ${state.score * state.betAmount}`, CONFIG.CANVAS_WIDTH / 2, 170);
				core.fillText(this.ctx, `累计金币: ${state.totalReward}`, CONFIG.CANVAS_WIDTH / 2, 200);
				if (!isFinalBoss) this.drawButton(this.buttons.VICTORY_NEXT, "下一关", "#3498db", 1);
				this.drawButton(this.buttons.VICTORY_FINISH, isFinalBoss ? "完成游戏" : "退出", isFinalBoss ? "#9b59b6" : "#e74c3c", 2);
			}
			drawHUD(state) {
				core.setFillStyle(this.ctx, "#ffffff");
				core.setFont(this.ctx, CONFIG.UI.SCORE_FONT);
				core.setTextAlign(this.ctx, "left");
				core.setTextBaseline(this.ctx, "top");
				core.fillText(this.ctx, `分数: ${state.score}`, 12, 10);
				core.fillText(this.ctx, `模式: ${state.mode === 'Classic' ? '经典' : '故事'}`, 12, 40);
				if (state.mode === 'Story') {
					core.fillText(this.ctx, `关卡: ${state.level + 1}/${CONFIG.BOSS_LIST.length}`, 12, 70);
					const boss = this.game.gameMode.currentBoss;
					if (boss) {
						const barWidth = 150,
							healthPercent = boss.health / 100;
						const frenzyThreshold = boss.config.BONEWALL?.FRENZY_HEALTH_THRESHOLD || 20;
						const color = healthPercent <= (frenzyThreshold / 100) ? "#ff0000" : healthPercent <= 0.5 ? "#ff9900" : "#2ecc71";
						core.setFillStyle(this.ctx, "#555");
						core.fillRect(this.ctx, CONFIG.CANVAS_WIDTH / 2 - barWidth / 2, 30, barWidth, 15);
						core.setFillStyle(this.ctx, color);
						core.fillRect(this.ctx, CONFIG.CANVAS_WIDTH / 2 - barWidth / 2, 30, barWidth * healthPercent, 15);
						core.setFillStyle(this.ctx, "#fff");
						core.setFont(this.ctx, "bold 12px Arial");
						core.setTextAlign(this.ctx, "center");
						core.fillText(this.ctx, `${state.bossName}: ${Math.ceil(boss.health)}/100`, CONFIG.CANVAS_WIDTH / 2, 32);
					}
				}

				// 【新增】绘制玩法提示
				if (state.mode === 'Story' && this.game.gameMode.showLevelHint) {
					core.setFillStyle(this.ctx, "rgba(0, 0, 0, 0.6)");
					core.fillRoundRect(this.ctx, 10, CONFIG.CANVAS_HEIGHT - 50, CONFIG.CANVAS_WIDTH - 20, 40, 8);

					core.setFillStyle(this.ctx, "#FFFFFF");
					core.setFont(this.ctx, "bold 14px Arial");
					core.setTextAlign(this.ctx, "center");
					core.setTextBaseline(this.ctx, "middle");
					core.fillText(this.ctx, "玩法提示：撞击骷髅队长跟它的卫兵，对Boss造成伤害！", CONFIG.CANVAS_WIDTH / 2, CONFIG.CANVAS_HEIGHT - 30);
				}
			}
			drawButton(rect, text, color, selectorId) {
				core.setFillStyle(this.ctx, color);
				core.fillRoundRect(this.ctx, rect.x, rect.y, rect.width, rect.height, CONFIG.UI.BUTTON_RADIUS);
				core.setFillStyle(this.ctx, "#ffffff");
				core.setFont(this.ctx, CONFIG.UI.BUTTON_FONT);
				core.setTextAlign(this.ctx, "center");
				core.setTextBaseline(this.ctx, "middle");
				core.fillText(this.ctx, text, rect.x + rect.width / 2, rect.y + rect.height / 2);
				if (selectorId) core.drawUIEventSelector(selectorId, 'selector', rect.x, rect.y, rect.width, rect.height);
			}
			handleClick(x, y) {
				const clickPos = { x: x * CONFIG.TILE_SIZE, y: y * CONFIG.TILE_SIZE };
				const inRect = (rect) => clickPos.x >= rect.x && clickPos.x < rect.x + rect.width && clickPos.y >= rect.y && clickPos.y < rect.y + rect.height;
				const state = this.game.state;
				if (inRect(this.buttons.EXIT)) { this.game.exit(); return; }
				if (!state.gameStarted) {
					if (this.view === 'main') {
						if (inRect(this.buttons.CLASSIC)) this.selectedMode = 'Classic';
						else if (inRect(this.buttons.STORY)) {
							this.selectedMode = 'Story';
							this.view = 'level_select';
						} else if (inRect(this.buttons.START) && this.selectedMode === 'Classic') this.game.startGame('Classic');
					} else if (this.view === 'level_select') {
						if (inRect({ x: 4, y: 280, width: 80, height: 32 })) { this.view = 'main'; }
						CONFIG.BOSS_LIST.forEach((boss, index) => {
							const btnRect = { x: 100, y: 120 + index * 40, width: 152, height: 32 };
							if (inRect(btnRect) && index <= this.unlockedLevel) { this.game.startGame('Story', index); }
						});
					}
					this.draw();
				} else if (state.victory) {
					if (inRect(this.buttons.VICTORY_NEXT)) this.game.gameMode.proceedToNextLevel();
					else if (inRect(this.buttons.VICTORY_FINISH)) this.game.exit();
				} else if (state.gameOver) { if (inRect(this.buttons.RESTART)) this.game.startGame(state.mode, state.level); }
			}
		}

		class InputHandler {
			constructor(game) {
				this.game = game;
				this.onClick = this.onClick.bind(this);
				this.onKeyDown = this.onKeyDown.bind(this);
			}
			register() {
				core.registerAction('onclick', 'pipe_bird_click', this.onClick, 100);
				core.registerAction('keyDown', 'pipe_bird_keydown', this.onKeyDown, 100);
			}
			unregister() {
				core.unregisterAction('onclick', 'pipe_bird_click');
				core.unregisterAction('keydown', 'pipe_bird_keydown');
			}
			onClick(x, y) {
				if (this.game.active) this.game.bird.jump();
				else this.game.uiManager.handleClick(x, y);
			}
			onKeyDown(keyCode) {
				// 新增：如果游戏已退出，则不执行任何操作
				if (this.game.isExited) {
					return;
				}

				if (keyCode === 27) {
					// 修复：优化Esc键逻辑
					if (this.game.uiManager.view === 'level_select') {
						this.game.uiManager.view = 'main';
						this.game.uiManager.draw();
					} else {
						this.game.exit();
					}
				}
				if ([32, 38].includes(keyCode)) {
					if (this.game.active) {
						this.game.bird.jump();
					} else if (this.game.state.gameOver) {
						this.game.startGame(this.game.state.mode, this.game.state.level);
					} else if (!this.game.state.gameStarted && this.game.uiManager.selectedMode === 'Classic') {
						this.game.startGame('Classic');
					}
				}
			}
		}

		// ===================================================================
		// VIII. 启动游戏 (Game Start)
		// ===================================================================
		const game = new Game();
		game.init();
	}
},
    "祖玛": function () {
	this.starZuMaGame = function () {
		// 祖玛游戏核心类（优化版）
		class ZuMaGame {
			constructor() {
				// 按钮位置常量
				this.buttonPositions = {
					topLeft: { x: 50, y: 30 },
					topCenter: { x: 30, y: 320 },
					topRight: { x: 320, y: 322 },
					center: { x: 176, y: 176 },
					belowCenter: { x: 176, y: 230 }
				};

				// 游戏配置
				this.config = {
					canvasSize: 352, // 游戏区域大小
					ballSize: 20, // 珠子大小
					shooterSize: 32, // 发射器大小
					trackRadius: 165, // 轨道半径
					flyingBallSpeed: 1000, // 飞行珠子速度
					colors: ['red', 'green', 'blue', 'yellow', 'purple'], // 珠子颜色
					maxLevel: 9999, // 最大关卡数
					particleCount: 15, // 每个珠子的粒子数量
					comboTime: 2000, // 连击有效时间(ms)
					ballSpeed: 16 // 轨道珠子移动速度
				};

				// 游戏状态
				this.state = {
					gameOver: false, // 游戏是否结束
					paused: false, // 游戏是否暂停
					score: 0, // 当前分数
					level: 1, // 当前关卡
					difficulty: 'easy', // 游戏难度
					ballsToGenerate: 0, // 待生成的珠子数量
					lastGenerateTime: 0, // 上次生成珠子的时间
					generationInterval: 500, // 生成间隔(ms)
					gameStarted: false, // 游戏是否已开始
					comboCount: 0, // 连击次数
					lastComboTime: 0, // 上次连击时间
					comboMultiplier: 1, // 连击倍数
					ballsRemoved: 0, // 新增：记录消除的珠子数量
					levelBalls: 0,
					playTime: 0, // 新增：记录游戏进行时间（毫秒）
					generatedCount: 0
				};

				// 游戏数据
				this.data = {
					tracks: [], // 多个轨道数组
					trackLengths: [], // 每个轨道的长度
					ballChains: [], // 每个轨道上的珠子链
					pendingBalls: [], // 每个轨道待生成的珠子队列
					generatedDistance: [], // 每个轨道已生成的距离
					chainStartPositions: [], // 每个轨道第一个珠子的位置
					shooterBall: null, // 发射器中的珠子
					flyingBall: null, // 飞行中的珠子
					rotation: Math.PI / 2, // 发射器旋转角度（默认向右）
					chainStartPositions: [], // 每个轨道第一个珠子的位置
					endPoints: [], // 每个轨道的终点
					particles: [], // 粒子效果
					scoreAnimations: [], // 分数动画
					comboAnimations: [], // 连击动画
					shooterPositions: [], // 新增：每个轨道的发射器位置
					ballSpeed: this.config.ballSpeed, // 轨道珠子移动速度
				};

				// 游戏元素
				this.elements = {
					gameCanvas: null, // 游戏主画布
					uiCanvas: null, // UI画布
					backgroundCanvas: null, // 背景画布
					trackCanvas: null // 轨道画布
				};
				this.ballCache = {}; // 珠子图像缓存

				this.buttons = {
					start: {
						x: this.buttonPositions.center.x,
						y: this.buttonPositions.center.y,
						width: 120,
						height: 40,
						icon: "开始游戏", // 添加 icon 属性
						visible: true
					},
					pause: {
						x: 340, // 右上角位置
						y: 30,
						radius: 10,
						icon: "❚❚", // 暂停图标
						visible: false
					},
					resume: {
						x: 340,
						y: 30,
						radius: 10,
						icon: "▶", // 继续图标
						visible: false
					},
					restart: {
						x: 340,
						y: 60, // 垂直排列
						radius: 10,
						icon: "↺", // 重新开始图标
						visible: false
					},
					exit: {
						x: 340,
						y: 90,
						radius: 10,
						icon: "✕", // 退出图标
						visible: false
					},
					startExit: {
						x: this.buttonPositions.belowCenter.x,
						y: this.buttonPositions.belowCenter.y,
						width: 120,
						height: 40,
						icon: "退出游戏", // 添加 icon 属性
						visible: true
					}
				};
				this.animationFrameId = null;
				this.destroyed = false; // 添加实例销毁标志
				// 初始化游戏

				this.init();
			}

			// 初始化游戏
			init() {
				core.lockControl();

				// 创建游戏画布
				this.createCanvases();

				// 绘制背景
				this.drawBackground();

				// 绘制开始菜单
				this.drawStartMenu();

				// 注册事件
				this.registerActions();
				this.lastTimestamp = 0;
				this.animationFrameId = requestAnimationFrame(this.gameLoop.bind(this));
			}

			// 创建游戏画布
			createCanvases() {
				const size = this.config.canvasSize;

				// 创建背景画布
				this.elements.backgroundCanvas = core.createCanvas(
					'background-canvas', 0, 0, size, size, 130
				);

				// 创建轨道画布
				this.elements.trackCanvas = core.createCanvas(
					'track-canvas', 0, 0, size, size, 131
				);

				// 创建游戏主画布
				this.elements.gameCanvas = core.createCanvas(
					'game-canvas', 0, 0, size, size, 132
				);

				// 创建UI画布
				this.elements.uiCanvas = core.createCanvas(
					'ui-canvas', 0, 0, size, size, 133
				);

				// 设置文本对齐
				core.setTextAlign('ui-canvas', 'center');
				core.setTextBaseline('ui-canvas', 'middle');
			}

			// 绘制背景
			drawBackground() {
				const size = this.config.canvasSize;
				const tileSize = 32; // 地面图块大小
				const cols = Math.ceil(size / tileSize);
				const rows = Math.ceil(size / tileSize);

				// 平铺绘制地面
				for (let x = 0; x < cols; x++) {
					for (let y = 0; y < rows; y++) {
						core.drawIcon(
							'background-canvas',
							'ground',
							x * tileSize,
							y * tileSize,
							tileSize,
							tileSize
						);
					}
				}
			}

			// 绘制开始菜单
			drawStartMenu() {
				const centerX = this.config.canvasSize / 2;
				const centerY = this.config.canvasSize / 2;

				// 清空UI画布
				core.clearMap('ui-canvas');

				// 绘制标题
				core.fillBoldText(
					'ui-canvas',
					'祖玛游戏',
					centerX, centerY - 80,
					'#FFD700',
					'#000000',
					'bold 48px Arial'
				);

				// 绘制开始按钮
				this.drawButton(this.buttons.start);

				// 绘制退出按钮
				this.drawButton(this.buttons.startExit);

				// 绘制操作说明
				core.fillBoldText(
					'ui-canvas',
					'点击屏幕发射珠子',
					centerX, centerY + 80,
					'#FFFFFF',
					'#000000',
					'20px Arial'
				);
			}

			// 开始游戏
			startGame() {
				this.state.gameStarted = true;

				// 重置游戏状态
				this.resetGame();

				// 开始游戏循环
				//this.gameLoop();
			}

			resetGame() {
				// 停止当前游戏循环
				this.stopGameLoop();

				// 重置游戏状态
				this.state = {
					gameOver: false, // 游戏是否结束
					paused: false, // 游戏是否暂停
					score: 0, // 当前分数
					level: 1, // 当前关卡
					difficulty: 'easy', // 游戏难度
					ballsToGenerate: 0, // 待生成的珠子数量
					lastGenerateTime: 0, // 上次生成珠子的时间
					generationInterval: 500, // 生成间隔(ms)
					gameStarted: true, // 游戏是否已开始
					comboCount: 0, // 连击次数
					lastComboTime: 0, // 上次连击时间
					comboMultiplier: 1, // 连击倍数
					ballsRemoved: 0, // 新增：记录消除的珠子数量
					levelBalls: 0,
					playTime: 0, // 新增：记录游戏进行时间（毫秒）
					generatedCount: 0
				};

				// 重置游戏数据
				this.data = {
					tracks: [], // 多个轨道数组
					trackLengths: [], // 每个轨道的长度
					ballChains: [], // 每个轨道上的珠子链
					pendingBalls: [], // 每个轨道待生成的珠子队列
					generatedDistance: [], // 每个轨道已生成的距离
					chainStartPositions: [], // 每个轨道第一个珠子的位置
					shooterBall: null, // 发射器中的珠子
					flyingBall: null, // 飞行中的珠子
					rotation: Math.PI / 2, // 发射器旋转角度（默认向右）
					chainStartPositions: [], // 每个轨道第一个珠子的位置
					endPoints: [], // 每个轨道的终点
					particles: [], // 粒子效果
					scoreAnimations: [], // 分数动画
					comboAnimations: [], // 连击动画
					shooterPositions: [], // 新增：每个轨道的发射器位置
					ballSpeed: this.config.ballSpeed, // 轨道珠子移动速度
				};

				// 清空画布
				core.clearMap('track-canvas');
				core.clearMap('game-canvas');
				core.clearMap('ui-canvas');

				// 创建新轨道
				this.createTrack();
				this.drawTrack();

				// 初始化关卡
				this.initLevel();

				// 创建发射器
				this.createShooter();

				// 更新按钮状态
				this.updateButtonVisibility();

				// 更新UI显示
				this.updateUI();

				// 启动新游戏循环
				this.lastTimestamp = 0;
				this.state.gameStarted = true;
				this.animationFrameId = requestAnimationFrame(this.gameLoop.bind(this));

				this.lastDifficultyCheck = 0; // 初始化上次难度检查时间
			}

			// ========== 轨道生成函数 ========== //

			createTrack() {
				// 重置轨道数据
				this.data.tracks = [];
				this.data.trackLengths = [];
				this.data.endPoints = [];
				this.data.shooterPositions = [];

				const { canvasSize } = this.config;
				const centerX = canvasSize / 2;
				const centerY = canvasSize / 2;
				const baseRadius = this.config.trackRadius;
				let { level } = this.state;
				const commonPositions = this.getCommonPositions();

				// 根据关卡确定轨道类型
				let trackType;
				if (level >= 15) {
					level = (level - 1) % 14 + 1;
					// 15级及以上：从所有轨道类型中随机选择
					//trackType = allTrackTypes[Math.floor(Math.random() * allTrackTypes.length)];
				}
				// 1-14级：使用预设的轨道映射
				const trackTypeMap = {
					1: "circle",
					2: "heart",
					3: "linearSpiral",
					4: "zigzag",
					5: "squareSpiral",
					6: "star",
					7: "doublecircle",
					8: "forked",
					9: "doublezigzag",
					10: "gridCross",
					11: "doubleSpiral",
					12: "crossed",
					13: "multiForked",
					14: "complexCrossed"
				};
				trackType = trackTypeMap[level] || "circle"; // 默认使用圆形轨道


				// 轨道生成器映射
				const generatorMap = {
					circle: () => this.generateCircleTracks(centerX, centerY, baseRadius, commonPositions),
					heart: () => this.generateHeartTrack(centerX, centerY, baseRadius, commonPositions),
					star: () => this.generateStarTrack(centerX, centerY, baseRadius * 0.9, 6, commonPositions),
					zigzag: () => this.generateZigzagTrack(centerX, centerY, baseRadius * 1.8, baseRadius * 1.8, commonPositions, 10, 1),
					linearSpiral: () => this.generateLinearSpiral(centerX, centerY, 4, baseRadius / 10, commonPositions),
					doublecircle: () => this.generateCircleTracks(centerX, centerY, baseRadius, commonPositions, 2),
					forked: () => this.generateForkedTracks(centerX, centerY, baseRadius, 2, 1.5, commonPositions),
					doublezigzag: () => this.generateZigzagTrack(centerX, centerY, baseRadius * 1.8, baseRadius * 1.8, commonPositions, 10, 2),
					crossed: () => this.generateCrossedTracks(centerX, centerY, baseRadius, 2, commonPositions),
					doubleSpiral: () => this.generateSpiralTracks(centerX, centerY, baseRadius, commonPositions),
					squareSpiral: () => this.generateSquareSpiralTrack(centerX, centerY, baseRadius, 5, baseRadius / 15, commonPositions),
					gridCross: () => this.generateGridCrossTrack(centerX, centerY, baseRadius * 0.8, commonPositions),
					multiForked: () => this.generateForkedTracks(centerX, centerY, baseRadius, 3, 2.0, commonPositions),
					complexCrossed: () => this.generateCrossedTracks(centerX, centerY, baseRadius, 3, commonPositions),
					donutMaze: () => this.generateDonutMaze(centerX, centerY, 4, baseRadius / 5, commonPositions)
				};

				// 执行轨道生成
				const generator = generatorMap[trackType] || generatorMap.circle;
				const shooterPos = generator();

				// 初始化轨道数据
				this.data.ballChains = this.data.tracks.map(() => []);
				this.data.chainStartPositions = this.data.tracks.map(() => 0);
				this.data.shooterPosition = shooterPos;

				this.data.trackDifficulty = this.data.tracks.map(track => {
					const length = this.calculateTrackLength(track);
					const turns = this.calculateTrackTurns(track); // 新增方法计算转弯次数
					return length * (1 + turns * 0.2); // 每个转弯增加20%难度
				});
			}


			// 新增方法：计算轨道转弯次数
			calculateTrackTurns(track) {
				let turns = 0;
				for (let i = 2; i < track.length; i++) {
					const v1 = { x: track[i - 1].x - track[i - 2].x, y: track[i - 1].y - track[i - 2].y };
					const v2 = { x: track[i].x - track[i - 1].x, y: track[i].y - track[i - 1].y };

					// 计算夹角变化
					const dot = v1.x * v2.x + v1.y * v2.y;
					const det = v1.x * v2.y - v1.y * v2.x;
					const angle = Math.abs(Math.atan2(det, dot));

					if (angle > Math.PI / 6) turns++; // 角度变化大于30度算转弯
				}
				return turns;
			}

			// 获取五个常用位置
			getCommonPositions() {
				const canvasSize = this.config.canvasSize;
				const centerX = canvasSize / 2;
				const centerY = canvasSize / 2;
				const margin = 16;
				return [
					{ x: centerX, y: centerY }, // 中心
					{ x: centerX, y: margin }, // 上边缘中点
					{ x: centerX, y: canvasSize - margin }, // 下边缘中点
					{ x: margin, y: centerY }, // 左边缘中点
					{ x: canvasSize - margin, y: centerY } // 右边缘中点
				];
			}

			// 从候选位置中随机选择
			selectRandomPosition(positions) {
				if (!positions || positions.length === 0) {
					return { x: this.config.canvasSize / 2, y: this.config.canvasSize / 2 };
				}
				const index = core.rand2(positions.length);
				return positions[index];
			}

			// ========== 轨道生成函数 ========== //

			// 生成圆形轨道
			generateCircleTracks(centerX, centerY, radius, commonPositions, trackCount = 1) {
				for (let trackIdx = 0; trackIdx < trackCount; trackIdx++) {
					const track = [];
					const isFullCircle = false;
					const startAngle = isFullCircle ? 0 : Math.PI / 4;
					const endAngle = isFullCircle ? Math.PI * 2 : Math.PI * 1.75;
					const points = 200;
					const trackRadius = radius * (0.7 + trackIdx * 0.2);

					for (let i = 0; i < points; i++) {
						const progress = i / points;
						const angle = startAngle + progress * (endAngle - startAngle);
						track.push({
							x: centerX + trackRadius * Math.cos(angle),
							y: centerY + trackRadius * Math.sin(angle)
						});
					}

					this.addTrack(track);
				}

				// 圆形轨道：仅使用五个常用位置
				return this.selectRandomPosition(commonPositions);
			}

			// 生成螺旋轨道
			generateSpiralTracks(centerX, centerY, radius, commonPositions) {
				const spiralTurns = 1.5 + Math.random() * 0.5;
				const pointsPerTurn = 120;
				const trackCount = this.state.level >= 4 ? 3 : 2;

				for (let trackIdx = 0; trackIdx < trackCount; trackIdx++) {
					const spiral = [];
					const phaseShift = trackIdx * (Math.PI * 2 / trackCount);

					for (let i = 0; i < pointsPerTurn * spiralTurns; i++) {
						const progress = i / (pointsPerTurn * spiralTurns);
						const angle = progress * Math.PI * 2 + phaseShift;
						const spiralRadius = radius * (
							trackIdx % 2 === 0 ?
							(0.5 + 0.5 * progress) :
							(0.5 + 0.5 * (1 - progress))
						);

						spiral.push({
							x: centerX + spiralRadius * Math.cos(angle),
							y: centerY + spiralRadius * Math.sin(angle)
						});
					}

					this.addTrack(spiral);
				}

				// 螺旋轨道：仅使用五个常用位置
				return this.selectRandomPosition(commonPositions);
			}

			// 生成分叉轨道
			generateForkedTracks(centerX, centerY, radius, branchCount, spacingFactor, commonPositions) {
				const commonSegment = this.generateCommonSegment(centerX, centerY, radius);
				const forkPoint = commonSegment[commonSegment.length - 1];

				for (let branchIdx = 0; branchIdx < branchCount; branchIdx++) {
					const branch = this.generateForkedBranch(
						centerX,
						centerY,
						radius,
						forkPoint,
						branchIdx,
						branchCount,
						spacingFactor
					);

					const track = [...commonSegment, ...branch];
					this.addTrack(track);
				}
				const canvasSize = this.config.canvasSize;
				const margin = 16;
				const candidatePositions = [
					{ x: margin, y: centerY }, // 左边缘中点
					{ x: canvasSize - margin, y: centerY } // 右边缘中点
				];
				return this.selectRandomPosition(candidatePositions);
			}

			// 生成公共起始段
			generateCommonSegment(centerX, centerY, radius) {
				const commonSegment = [];
				const startAngle = Math.PI / 4;
				const forkAngle = Math.PI / 2;
				const commonPoints = 60;
				const straightPoints = Math.floor(commonPoints * 0.3);
				const startPoint = {
					x: centerX + radius * Math.cos(startAngle),
					y: centerY + radius * Math.sin(startAngle)
				};
				const midPoint = {
					x: centerX + radius * Math.cos(startAngle + (forkAngle - startAngle) * 0.3),
					y: centerY + radius * Math.sin(startAngle + (forkAngle - startAngle) * 0.3)
				};

				for (let i = 0; i < straightPoints; i++) {
					const progress = i / straightPoints;
					commonSegment.push({
						x: startPoint.x + progress * (midPoint.x - startPoint.x),
						y: startPoint.y + progress * (midPoint.y - startPoint.y)
					});
				}

				const curvePoints = commonPoints - straightPoints;
				for (let i = 0; i < curvePoints; i++) {
					const progress = i / curvePoints;
					const angle = startAngle + 0.3 * (forkAngle - startAngle) +
						progress * 0.7 * (forkAngle - startAngle);
					commonSegment.push({
						x: centerX + radius * Math.cos(angle),
						y: centerY + radius * Math.sin(angle)
					});
				}

				return commonSegment;
			}

			// 生成单个分支
			generateForkedBranch(centerX, centerY, radius, startPoint, branchIdx, totalBranches, spacingFactor) {
				const branch = [];
				const branchPoints = 80 + branchIdx * 20;
				const startAngle = Math.PI / 2;
				const angleSpread = Math.PI * spacingFactor;
				const minAngle = startAngle - angleSpread / 2;
				const maxAngle = startAngle + angleSpread / 2;
				const angleRange = maxAngle - minAngle;
				const branchAngle = minAngle + angleRange * (branchIdx / (totalBranches - 1));
				const arcRatio = 0.6 + 0.2 * (branchIdx / totalBranches);
				const arcPoints = Math.floor(branchPoints * arcRatio);

				for (let i = 1; i <= arcPoints; i++) {
					const progress = i / arcPoints;
					const angleProgress = Math.pow(progress, 0.8);
					const angle = branchAngle + angleProgress * (Math.PI * 1.5 - branchAngle);
					const radiusVariation = 0.25;
					const branchRadius = radius * (0.9 - radiusVariation / 2 + radiusVariation * (branchIdx / (totalBranches - 1)));
					branch.push({
						x: centerX + branchRadius * Math.cos(angle),
						y: centerY + branchRadius * Math.sin(angle)
					});
				}

				const straightPoints = branchPoints - arcPoints;
				if (straightPoints > 0) {
					const lastPoint = branch[branch.length - 1];
					const endAngle = branchAngle + (Math.PI * 1.5 - branchAngle);
					const endRadius = radius * (1.0 + 0.1 * branchIdx);
					const endPoint = {
						x: centerX + endRadius * Math.cos(endAngle),
						y: centerY + endRadius * Math.sin(endAngle)
					};

					for (let i = 1; i <= straightPoints; i++) {
						const progress = i / straightPoints;
						branch.push({
							x: lastPoint.x + progress * (endPoint.x - lastPoint.x),
							y: lastPoint.y + progress * (endPoint.y - lastPoint.y)
						});
					}
				}

				return branch;
			}

			// 生成交叉轨道
			generateCrossedTracks(centerX, centerY, radius, trackCount, commonPositions) {
				// 第一条轨道：改良∞字形
				const infinityTrack = this.generateInfinityTrack(centerX, centerY, radius);
				this.addTrack(infinityTrack);

				// 第二条轨道：直线+弧线组合
				if (trackCount >= 2) {
					const complexTrack = [];
					const trackPoints = 150;
					const offset = radius * 0.15;
					const straightSection = Math.floor(trackPoints * 0.4);

					for (let i = 0; i < straightSection; i++) {
						const progress = i / straightSection;
						complexTrack.push({
							x: centerX - radius * 0.65 + progress * (radius * 1.3),
							y: centerY - radius * 0.65 + offset + progress * (radius * 1.3)
						});
					}

					const curveSection = trackPoints - straightSection;
					for (let i = 0; i < curveSection; i++) {
						const progress = i / curveSection;
						const angle = Math.PI * 0.25 + progress * Math.PI * 1.5;
						complexTrack.push({
							x: centerX + (radius * 0.8) * Math.cos(angle),
							y: centerY + offset + (radius * 0.8) * Math.sin(angle)
						});
					}
					this.addTrack(complexTrack);
				}

				// 第三条轨道：正弦波轨道
				if (trackCount >= 3) {
					const sineTrack = [];
					const points = 180;
					const amplitude = radius * 0.5;
					const frequency = 3;

					for (let i = 0; i < points; i++) {
						const progress = i / points;
						const angle = progress * Math.PI * 2;
						const sineOffset = amplitude * Math.sin(progress * Math.PI * 2 * frequency);
						sineTrack.push({
							x: centerX + (radius * 0.75) * Math.cos(angle),
							y: centerY + (radius * 0.75) * Math.sin(angle) + sineOffset
						});
					}
					this.addTrack(sineTrack);
				}

				// 交叉轨道：五个常用位置 + ∞字轨道上的特殊点
				const candidatePositions = [
					...commonPositions,
					infinityTrack[Math.floor(infinityTrack.length * 0.25)], // 右侧弧顶
					infinityTrack[Math.floor(infinityTrack.length * 0.75)] // 左侧弧顶
				];
				return this.selectRandomPosition(candidatePositions);
			}

			// 生成改良∞字形轨道
			generateInfinityTrack(centerX, centerY, radius) {
				const infinityTrack = [];
				const points = 300;
				const a = radius * 0.9;
				const b = radius * 0.6;

				for (let i = 0; i < points; i++) {
					const t = Math.PI / 2 + (i / points) * Math.PI * 2;
					const x = centerX + a * Math.sin(t);
					const y = centerY + b * Math.sin(2 * t) * 0.7;
					infinityTrack.push({ x, y });
				}

				return infinityTrack;
			}

			// 生成心形轨道
			generateHeartTrack(centerX, centerY, radius, commonPositions) {
				const heartTrack = [];
				const points = 300;
				const size = radius * 0.05;

				for (let i = 0; i < points; i++) {
					const t = (i / points) * Math.PI * 2;
					const heartX = 16 * Math.pow(Math.sin(t), 3);
					const heartY = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) -
						2 * Math.cos(3 * t) - Math.cos(4 * t));

					heartTrack.push({
						x: centerX + size * heartX,
						y: centerY + size * heartY
					});
				}
				this.addTrack(heartTrack);

				// 心形轨道：五个常用位置 + 心形顶部凹陷处
				const candidatePositions = [
					...commonPositions,
				];
				return this.selectRandomPosition(candidatePositions);
			}

			// 生成方形螺旋轨道
			// 生成方形螺旋轨道
			generateSquareSpiralTrack(centerX, centerY, radius, layers, step, commonPositions) {
				const track = [];

				let x = centerX;
				let y = centerY;
				let dir = 0;
				let len = radius;

				for (let layer = 0; layer < layers; layer++) {
					for (let i = 0; i < 4; i++) {
						const dx = [1, 0, -1, 0][dir];
						const dy = [0, -1, 0, 1][dir];

						for (let j = 0; j < len; j += step) {
							track.push({ x, y });
							x += dx * step;
							y += dy * step;
						}
						dir = (dir + 1) % 4;
					}
					len -= step * 2;
				}

				// 终点向下偏移一个单位
				if (track.length > 0) {
					const last = track[track.length - 1];
					last.y += 4 * step;
				}

				this.addTrack(track);

				// 常用位置 + 第一个转角点
				const candidatePositions = track.length > 10 ? [...commonPositions, track[Math.floor(track.length * 0.05)]] :
					commonPositions;
				return this.selectRandomPosition(candidatePositions);
			}


			// 生成折线轨道
			generateZigzagTrack(centerX, centerY, width, height, commonPositions, segments = 10, trackCount = 1) {
				const track = [];

				// 单轨道生成（原逻辑）
				if (trackCount === 1) {
					const stepY = height / segments;
					for (let i = 0; i <= segments; i++) {
						const x = i % 2 === 0 ? centerX - width / 2 : centerX + width / 2;
						const y = centerY + i * stepY - height / 2;
						track.push({ x, y });
					}
					this.addTrack(track);
				}
				// 双轨道生成（上下对称，改进镜像）
				else if (trackCount === 2) {
					const halfHeight = height / 2;
					const stepY = halfHeight / segments;

					// 生成上轨道（从顶部到中心）
					const topTrack = [];
					for (let i = 0; i <= segments; i++) {
						const x = i % 2 === 0 ? centerX - width / 2 : centerX + width / 2;
						const y = centerY - halfHeight + i * stepY;
						topTrack.push({ x, y });
					}
					this.addTrack(topTrack);

					// 生成下轨道（镜像 X 轴，使轨道分离）
					const bottomTrack = [];
					for (let i = 0; i <= segments; i++) {
						// 镜像 X 轴：起始与结束互换
						const x = i % 2 === 0 ? centerX + width / 2 : centerX - width / 2;
						// Y 从中心向下
						const y = centerY + halfHeight - i * stepY;
						bottomTrack.push({ x, y });
					}
					this.addTrack(bottomTrack);
				}

				// 位置选择逻辑（与圆形轨道一致）
				const candidatePositions = [
					...commonPositions,
				];
				return this.selectRandomPosition(candidatePositions);
			}


			// 生成星形轨道
			generateStarTrack(centerX, centerY, radius, points, commonPositions) {
				const track = [];
				const totalPoints = points * 2;

				for (let i = 0; i <= totalPoints; i++) {
					const angle = (i * Math.PI) / points;
					const isOuter = i % 2 === 0;
					const r = isOuter ? radius : radius * 0.4;
					track.push({
						x: centerX + r * Math.cos(angle),
						y: centerY + r * Math.sin(angle)
					});
				}
				this.addTrack(track);

				const candidatePositions = [
					...commonPositions,
				];
				return this.selectRandomPosition(candidatePositions);
			}

			// 生成线性螺旋轨道
			generateLinearSpiral(centerX, centerY, turns, step, commonPositions) {
				const track = [];
				let angle = 0;
				let radius = 0;
				const maxAngle = turns * 2 * Math.PI;

				while (angle < maxAngle) {
					const x = centerX + radius * Math.cos(angle);
					const y = centerY + radius * Math.sin(angle);
					track.push({ x, y });
					radius += step * 0.05;
					angle += 0.15;
				}
				this.addTrack(track);

				// 线性螺旋：五个常用位置 + 螺旋起点
				const candidatePositions = [
					...commonPositions,
					track[0] // 螺旋起点
				];
				return this.selectRandomPosition(candidatePositions);
			}

			// 生成网格交叉轨道
			generateGridCrossTrack(centerX, centerY, radius, commonPositions) {
				const track = [];
				const gap = radius;
				const points = 15;

				// 定义四个方向的终点
				const directions = [
					{ dx: -gap, dy: 0 }, // 左
					{ dx: 0, dy: -gap }, // 上
					{ dx: gap, dy: 0 }, // 右
					{ dx: 0, dy: gap } // 下
				];

				// 生成四条放射状线段
				for (const dir of directions) {
					for (let i = 0; i <= points; i++) {
						const progress = i / points;
						track.push({
							x: centerX + dir.dx * progress,
							y: centerY + dir.dy * progress
						});
					}
				}
				this.addTrack(track);

				// 网格轨道：五个常用位置 + 随机射线端点
				const endpoints = [
					{ x: centerX - gap, y: centerY }, // 左端
					{ x: centerX, y: centerY - gap }, // 上端
					{ x: centerX + gap, y: centerY }, // 右端
					{ x: centerX, y: centerY + gap } // 下端
				];

				const candidatePositions = [...commonPositions, ...endpoints];
				return this.selectRandomPosition(candidatePositions);
			}

			// 生成多层方框轨道
			generateDonutMaze(centerX, centerY, layers, stepSize, commonPositions) {
				for (let i = 0; i < layers; i++) {
					const size = stepSize * (layers - i);
					const track = [
						{ x: centerX - size, y: centerY - size },
						{ x: centerX + size, y: centerY - size },
						{ x: centerX + size, y: centerY + size },
						{ x: centerX - size, y: centerY + size },
						{ x: centerX - size, y: centerY - size }
					];
					this.addTrack(track);
				}

				// 多层方框：五个常用位置 + 随机角落
				const maxSize = stepSize * layers;
				const corners = [
					{ x: centerX - maxSize, y: centerY - maxSize },
					{ x: centerX + maxSize, y: centerY - maxSize },
					{ x: centerX + maxSize, y: centerY + maxSize },
					{ x: centerX - maxSize, y: centerY + maxSize }
				];

				const candidatePositions = [...commonPositions, ...corners];
				return this.selectRandomPosition(candidatePositions);
			}

			// 添加轨道到数据结构
			addTrack(track) {
				this.data.tracks.push(track);
				this.data.endPoints.push(track[track.length - 1]);
				this.data.trackLengths.push(this.calculateTrackLength(track));
			}

			// ========== 辅助函数 ========== //

			// 计算轨道长度
			calculateTrackLength(track) {
				let length = 0;
				for (let i = 1; i < track.length; i++) {
					const dx = track[i].x - track[i - 1].x;
					const dy = track[i].y - track[i - 1].y;
					length += Math.sqrt(dx * dx + dy * dy);
				}
				return length;
			}

			// 绘制轨道
			drawTrack() {
				// 绘制所有轨道
				for (let trackIdx = 0; trackIdx < this.data.tracks.length; trackIdx++) {
					const track = this.data.tracks[trackIdx];
					const trackColor = this.getTrackColor(trackIdx);

					// 绘制轨道线
					for (let i = 1; i < track.length; i++) {
						core.drawLine(
							'track-canvas',
							track[i - 1].x, track[i - 1].y,
							track[i].x, track[i].y,
							trackColor,
							4
						);
					}

					// 绘制轨道起点和终点
					const start = track[0];
					const end = this.data.endPoints[trackIdx];

					// 起点（绿色）
					core.fillCircle('track-canvas', start.x, start.y, 8, '#4CAF50');
					// 终点（红色）
					core.fillCircle('track-canvas', end.x, end.y, 8, '#F44336');

					// 绘制轨道编号
					core.fillText(
						'track-canvas',
						`轨道 ${trackIdx+1}`,
						start.x, start.y - 15,
						'#FFFFFF',
						'bold 14px Arial'
					);
				}
			}

			// 获取轨道颜色（不同轨道不同颜色）
			getTrackColor(trackIdx) {
				const colors = [
					'rgba(255, 204, 0, 0.4)', // 黄色
					'rgba(0, 204, 255, 0.4)', // 蓝色
					'rgba(204, 0, 255, 0.4)', // 紫色
					'rgba(0, 255, 102, 0.4)' // 绿色
				];
				return colors[trackIdx % colors.length];
			}

			initLevel() {
				this.state.generatedCount = 0;

				// 计算本关珠子总数（不分配到具体轨道）
				let totalTrackLength = this.data.trackLengths.reduce((sum, len) => sum + len, 0);

				// 更平衡的珠子数量计算
				const baseDensity = 0.005;
				const baseBalls = 8;
				const levelFactor = 1 + (this.state.level - 1) * 0.05;

				const maxColors = Math.min(3 + Math.floor((this.state.level - 1) / 14), 5);
				this.state.levelColors = this.config.colors.slice(0, maxColors);

				this.state.levelBalls = Math.max(
					baseBalls,
					Math.min(
						Math.floor(baseBalls + totalTrackLength * baseDensity * levelFactor),
						50
					)
				);

				// ====== 关键修改：预生成所有珠子序列 ======
				// 初始化轨道数据结构
				this.data.pendingBalls = this.data.tracks.map(() => []);
				this.data.accumulatedDistance = this.data.tracks.map(() => 0); // 新增：累计移动距离
				this.data.chainStartPositions = this.data.tracks.map(() => 0);
				this.data.ballChains = this.data.tracks.map(() => []);

				// 按轨道长度比例分配珠子数量
				const ballsPerTrack = this.distributeBallsToTracks(this.state.levelBalls);

				// 为每个轨道生成珠子序列
				for (let trackIdx = 0; trackIdx < this.data.tracks.length; trackIdx++) {
					this.data.pendingBalls[trackIdx] = this.generateBallSequence(
						ballsPerTrack[trackIdx],
						trackIdx
					);
				}

				// 初始生成几个珠子（确保游戏开始就有珠子）
				this.generateInitialBalls();

				// 重置时间记录
				this.state.lastGenerateTime = 0;
			}

			// === 辅助方法：按轨道长度比例分配珠子 ===
			distributeBallsToTracks(totalBalls) {
				const totalLength = this.data.trackLengths.reduce((s, l) => s + l, 0);
				const ballsPerTrack = [];

				// 计算每个轨道应分配的珠子数量
				let remaining = totalBalls;
				for (let i = 0; i < this.data.trackLengths.length - 1; i++) {
					const count = Math.round((this.data.trackLengths[i] / totalLength) * totalBalls);
					ballsPerTrack.push(count);
					remaining -= count;
				}
				// 最后一个轨道获取剩余所有珠子
				ballsPerTrack.push(remaining);

				return ballsPerTrack;
			}

			// === 辅助方法：生成珠子序列（避免连续三个相同颜色） ===
			generateBallSequence(count, trackIdx) {
				const sequence = [];

				for (let i = 0; i < count; i++) {
					let color;

					// 避免连续三个相同颜色
					if (sequence.length >= 2 &&
						sequence[sequence.length - 1] === sequence[sequence.length - 2]) {
						const available = this.state.levelColors.filter(
							c => c !== sequence[sequence.length - 1]
						);
						color = available[Math.floor(Math.random() * available.length)];
					} else {
						color = this.getRandomColor();
					}

					sequence.push(color);
				}

				return sequence;
			}


			// 在每个轨道上生成一个初始珠子
			generateInitialBalls() {
				// 每个轨道生成一个初始珠子
				for (let trackIdx = 0; trackIdx < this.data.tracks.length; trackIdx++) {
					if (this.data.pendingBalls[trackIdx].length > 0) {
						const color = this.data.pendingBalls[trackIdx].shift();
						this.data.ballChains[trackIdx].unshift({ color, trackIdx });

						// 初始化累计距离（避免立即生成新珠子）
						this.data.accumulatedDistance[trackIdx] = 0;
					}
				}
			}

			// 新增方法：动态更新生成间隔
			updateGenerationInterval() {
				// 计算移动一个珠子直径所需时间
				const timePerBall = this.config.ballSize / this.data.ballSpeed;
				// 设置间隔为移动0.5-1.5个珠子的时间（随机波动）
				this.state.generationInterval = timePerBall * (0.5 + Math.random()) * 1000;
			}

			// 根据索引计算轨道上的坐标（修复间隔问题）
			getBallPosition(trackIdx, ballIdx) {
				const track = this.data.tracks[trackIdx];
				const trackLength = this.data.trackLengths[trackIdx];
				const ballDiameter = this.config.ballSize;

				// 关键修复：正确使用 chainStartPositions
				// 珠子位置 = 起始偏移 + 珠子索引 * 珠子间距
				const normalizedPosition = this.data.chainStartPositions[trackIdx] +
					ballIdx * (ballDiameter / trackLength);

				// 找到对应的线段
				let accumulatedLength = 0;
				for (let i = 1; i < track.length; i++) {
					const segmentLength = Math.sqrt(
						Math.pow(track[i].x - track[i - 1].x, 2) +
						Math.pow(track[i].y - track[i - 1].y, 2)
					);

					const segmentStart = accumulatedLength / trackLength;
					const segmentEnd = (accumulatedLength + segmentLength) / trackLength;

					if (normalizedPosition >= segmentStart && normalizedPosition <= segmentEnd) {
						// 在当前线段内插值
						const segmentProgress = (normalizedPosition - segmentStart) / (segmentEnd - segmentStart);
						return {
							x: track[i - 1].x + (track[i].x - track[i - 1].x) * segmentProgress,
							y: track[i - 1].y + (track[i].y - track[i - 1].y) * segmentProgress
						};
					}
					accumulatedLength += segmentLength;
				}
				return track[track.length - 1]; // 返回终点位置
			}

			// 创建发射器
			createShooter() {
				// 使用动态计算的发射器位置
				const shooterPos = this.data.shooterPosition;

				this.data.shooterBall = {
					x: shooterPos.x,
					y: shooterPos.y,
					color: this.getRandomColor()
				};
			}

			// 获取随机颜色（使用关卡限定颜色）
			getRandomColor() {
				return this.state.levelColors[
					Math.floor(Math.random() * this.state.levelColors.length)
				];
			}

			// 发射珠子
			shootBall(targetX, targetY) {
				if (!this.state.gameStarted || this.state.paused || this.state.gameOver) return;
				if (this.data.flyingBall) return;

				// 校验输入
				if (!Number.isFinite(targetX) || !Number.isFinite(targetY)) return;

				const shooterX = this.data.shooterBall.x;
				const shooterY = this.data.shooterBall.y;

				const dx = targetX - shooterX;
				const dy = targetY - shooterY;
				const distance = Math.hypot(dx, dy);

				// 避免 0 除
				if (distance < 1e-6) return;

				this.updateUI();

				this.data.rotation = Math.atan2(dy, dx);

				const speed = this.config.flyingBallSpeed;
				this.data.flyingBall = {
					x: shooterX,
					y: shooterY,
					vx: dx / distance * speed,
					vy: dy / distance * speed,
					color: this.data.shooterBall.color
				};

				this.data.shooterBall.color = this.getRandomColor();
			}


			// 更新游戏状态
			update(deltaTime) {
				if (!this.state.gameStarted || this.state.paused || this.state.gameOver) return;

				// 更新飞行中的珠子
				this.updateFlyingBall(deltaTime); // 传入deltaTime

				if (this.updateBallChains(deltaTime)) {
					return; // 如果触发结束则提前返回
				}

				// 更新粒子效果
				this.updateParticles();

				// 更新分数动画
				this.updateScoreAnimations();

				// 更新连击动画
				this.updateComboAnimations();

				// 更新连击状态
				this.updateComboState();

				this.checkLevelClear();

				if (Date.now() - this.lastDifficultyCheck > 5000) {
					this.adjustDynamicDifficulty();
					this.lastDifficultyCheck = Date.now();
				}
			}

			adjustDynamicDifficulty() {
				// 计算玩家表现（消除速度）
				const ballsPerMinute = this.state.ballsRemoved / (this.state.playTime / 60000);

				// 根据表现调整
				if (ballsPerMinute > 30) { // 表现太好
					this.data.ballSpeed *= 1.05;
					this.state.generationInterval *= 0.95;
				} else if (ballsPerMinute < 15) { // 表现太差
					this.data.ballSpeed *= 0.95;
					this.state.generationInterval *= 1.05;
				}

				// 更新间隔
				this.updateGenerationInterval();
			}

			// 更新连击状态
			updateComboState() {
				const now = Date.now();
				// 检查连击是否超时
				if (this.state.comboCount > 0 && now - this.state.lastComboTime > this.config.comboTime) {
					// 连击结束，重置状态
					this.state.comboCount = 0;
					this.state.comboMultiplier = 1;
				}
			}


			// 更新飞行中的珠子
			updateFlyingBall(deltaTime) {
				if (!this.data.flyingBall) return;

				const centerX = this.config.canvasSize / 2;
				const centerY = this.config.canvasSize / 2;

				if (!this.data.flyingBall) return;

				// 使用deltaTime确保速度一致
				this.data.flyingBall.x += this.data.flyingBall.vx * deltaTime;
				this.data.flyingBall.y += this.data.flyingBall.vy * deltaTime;

				// 边界检测 - 飞出边界后消失
				if (this.data.flyingBall.x < 0 ||
					this.data.flyingBall.x > this.config.canvasSize ||
					this.data.flyingBall.y < 0 ||
					this.data.flyingBall.y > this.config.canvasSize) {
					this.data.flyingBall = null;
					return;
				}

				// 检测与所有轨道珠子链的碰撞
				let minDistance = Infinity;
				let nearestTrackIdx = -1;
				let nearestBallIdx = -1;

				for (let trackIdx = 0; trackIdx < this.data.ballChains.length; trackIdx++) {
					const chain = this.data.ballChains[trackIdx];

					for (let ballIdx = 0; ballIdx < chain.length; ballIdx++) {
						const ballPos = this.getBallPosition(trackIdx, ballIdx);
						const dx = this.data.flyingBall.x - ballPos.x;
						const dy = this.data.flyingBall.y - ballPos.y;
						const distance = Math.sqrt(dx * dx + dy * dy);

						if (distance < minDistance) {
							minDistance = distance;
							nearestTrackIdx = trackIdx;
							nearestBallIdx = ballIdx;
						}
					}
				}

				// 检查碰撞
				if (minDistance < this.config.ballSize * 0.5) {
					console.log(`✅ Collision! Inserting behind ball ${nearestBallIdx} on track ${nearestTrackIdx}`);
					// 在最近的珠子后面插入新珠子
					const chain = this.data.ballChains[nearestTrackIdx];
					chain.splice(nearestBallIdx + 1, 0, {
						color: this.data.flyingBall.color,
						trackIdx: nearestTrackIdx // 修复：使用正确轨道索引
					});

					this.data.flyingBall = null;

					// 检查匹配
					this.checkMatches(nearestTrackIdx, nearestBallIdx + 1);
				}

				// 飞行时间过长则消失
				if (this.data.flyingBall) {
					const distanceFromCenter = Math.sqrt(
						Math.pow(this.data.flyingBall.x - centerX, 2) +
						Math.pow(this.data.flyingBall.y - centerY, 2)
					);

					if (distanceFromCenter > this.config.trackRadius * 2) {
						this.data.flyingBall = null;
					}
				}
			}

			// 在 updateBallChains 中修改珠子添加逻辑
			updateBallChains(deltaTime) {
				const ballDiameter = this.config.ballSize;
				const speed = this.data.ballSpeed;

				for (let trackIdx = 0; trackIdx < this.data.tracks.length; trackIdx++) {
					const trackLength = this.data.trackLengths[trackIdx];
					const chain = this.data.ballChains[trackIdx];
					const pendingBalls = this.data.pendingBalls[trackIdx];

					// 计算本帧移动距离
					const distanceMoved = speed * deltaTime;

					// 更新累计移动距离
					this.data.accumulatedDistance[trackIdx] += distanceMoved;

					// === 关键修改：距离驱动的珠子生成 ===
					// 检查是否应该生成珠子（移动距离达到珠子直径）
					while (this.data.accumulatedDistance[trackIdx] >= ballDiameter) {
						if (pendingBalls.length > 0) {
							// 取出一个珠子添加到轨道起点
							chain.unshift({
								color: pendingBalls.shift(),
								trackIdx
							});
							if (chain.length >= 3) {
								// 只检查新珠子周围可能的匹配
								this.checkMatches(trackIdx, 0); // 检查位置0（新珠子）
								this.checkMatches(trackIdx, 1); // 检查相邻珠子
							}
							this.data.chainStartPositions[trackIdx] = 0;
						}
						// 减去一个珠子直径的距离（保留余数）
						this.data.accumulatedDistance[trackIdx] -= ballDiameter;
					}

					// 更新珠子链位置（基于chainStartPositions）
					this.data.chainStartPositions[trackIdx] += distanceMoved / trackLength;

					// 检查是否到达终点（只检查最后一个珠子）
					if (chain.length > 0) {
						const lastBallIdx = chain.length - 1;
						const lastBallPosition = this.data.chainStartPositions[trackIdx] +
							lastBallIdx * (ballDiameter / trackLength);

						if (lastBallPosition >= 1) {
							this.endGame();
							return true;
						}
					}
				}

				return false;
			}

			checkLevelClear() {
				// 检查所有轨道是否为空
				let allChainsEmpty = this.data.ballChains.every(chain => chain.length === 0);
				if (!allChainsEmpty) return false;

				// 检查是否还有待生成的珠子
				const allPendingEmpty = this.data.pendingBalls.every(p => p.length === 0);

				// 满足条件则进入下一关
				if (allPendingEmpty) {
					this.nextLevel();
					return true;
				}
				return false;
			}
			// 检查匹配
			checkMatches(trackIdx, ballIdx) {
				const chain = this.data.ballChains[trackIdx];
				const color = chain[ballIdx].color;
				let start = ballIdx;
				let end = ballIdx;

				// 向前查找相同颜色的珠子
				while (start > 0 && chain[start - 1].color === color) {
					start--;
				}

				// 向后查找相同颜色的珠子
				while (end < chain.length - 1 && chain[end + 1].color === color) {
					end++;
				}

				// 计算匹配数量
				const matchCount = end - start + 1;

				if (matchCount >= 3) {
					// 记录连击
					this.recordCombo();

					// 计算分数（考虑连击加成）
					const basePoints = matchCount * 10 * this.state.level;
					const comboBonus = Math.min(5, this.state.comboCount) * 0.2; // 最大100%加成
					const points = Math.floor(basePoints * (1 + comboBonus));

					// 增加分数
					this.state.score += points;
					this.updateUI();

					// 添加消除特效
					const centerX = this.config.canvasSize / 2;
					const centerY = this.config.canvasSize / 2;

					// 为每个被消除的珠子添加粒子效果
					for (let i = start; i <= end; i++) {
						const ballPos = this.getBallPosition(trackIdx, i);
						this.addParticles(ballPos.x, ballPos.y, chain[i].color);
					}

					// 显示得分动画（在匹配组中心位置）
					const centerBallIdx = Math.floor((start + end) / 2);
					const centerPos = this.getBallPosition(trackIdx, centerBallIdx);
					this.addScoreAnimation(centerPos.x, centerPos.y, points, matchCount);

					// 移除匹配的珠子
					chain.splice(start, matchCount);

					if (start < chain.length) {
						this.checkMatches(trackIdx, start);
					}
				}
			}

			// 记录连击
			recordCombo() {
				const now = Date.now();

				// 检查连击是否有效（在连击时间内）
				if (now - this.state.lastComboTime < this.config.comboTime) {
					this.state.comboCount++;
					this.state.comboMultiplier = 1 + Math.min(5, this.state.comboCount) * 0.2;

					// 显示连击动画
					const centerX = this.config.canvasSize / 2;
					const centerY = this.config.canvasSize / 2;
					this.addComboAnimation(centerX, centerY - 50);
				} else {
					// 新连击
					this.state.comboCount = 1;
					this.state.comboMultiplier = 1.2;
				}

				this.state.lastComboTime = now;
			}

			// 添加粒子效果
			addParticles(x, y, color) {
				const particleCount = this.config.particleCount;

				for (let i = 0; i < particleCount; i++) {
					// 随机角度和速度
					const angle = Math.random() * Math.PI * 2;
					const speed = 0.5 + Math.random() * 2;

					// 粒子大小
					const size = 2 + Math.random() * 6;

					// 粒子生命周期
					const life = 20 + Math.random() * 30;

					// 添加到粒子数组
					this.data.particles.push({
						x: x,
						y: y,
						vx: Math.cos(angle) * speed,
						vy: Math.sin(angle) * speed,
						size: size,
						color: color,
						life: life,
						maxLife: life
					});
				}
			}

			// 更新粒子效果
			updateParticles() {
				for (let i = this.data.particles.length - 1; i >= 0; i--) {
					const particle = this.data.particles[i];

					// 更新位置
					particle.x += particle.vx;
					particle.y += particle.vy;

					// 应用重力
					particle.vy += 0.05;

					// 减少生命周期
					particle.life--;

					// 移除死亡粒子
					if (particle.life <= 0) {
						this.data.particles.splice(i, 1);
					}
				}
			}

			// 添加分数动画
			// 添加分数动画
			addScoreAnimation(x, y, points, matchCount) {
				// 根据内容决定是分数还是过关文本
				const isText = typeof points === 'string';

				// 根据消除数量设置不同大小
				const size = isText ? 48 : (matchCount > 3 ? 32 : 24);
				const fontSize = isText ? 'bold 48px Arial' :
					(matchCount > 3 ? 'bold 28px Arial' : 'bold 24px Arial');

				this.data.scoreAnimations.push({
					x: x,
					y: y,
					text: isText ? points : `+${points}`,
					alpha: 1.0,
					size: size,
					fontSize: fontSize,
					life: 60 // 60帧动画
				});
			}

			// 绘制分数动画
			drawScoreAnimations() {
				for (const anim of this.data.scoreAnimations) {
					core.fillBoldText(
						'ui-canvas',
						anim.text,
						anim.x, anim.y,
						anim.text.includes('+') ? '#FFD700' : '#4CAF50',
						'#000000',
						anim.fontSize,
						anim.alpha
					);
				}
			}

			// 更新分数动画
			updateScoreAnimations() {
				for (let i = this.data.scoreAnimations.length - 1; i >= 0; i--) {
					const anim = this.data.scoreAnimations[i];

					// 向上移动
					anim.y -= 1;

					// 淡出效果
					anim.alpha -= 0.0167; // 1/60

					// 减少生命周期
					anim.life--;

					// 移除结束的动画
					if (anim.life <= 0) {
						this.data.scoreAnimations.splice(i, 1);
					}
				}
			}

			// 添加连击动画
			addComboAnimation(x, y) {
				this.data.comboAnimations.push({
					x: x,
					y: y,
					text: `连击 x${this.state.comboCount}!`,
					alpha: 1.0,
					size: 36,
					life: 90 // 90帧动画
				});
			}

			// 更新连击动画
			updateComboAnimations() {
				for (let i = this.data.comboAnimations.length - 1; i >= 0; i--) {
					const anim = this.data.comboAnimations[i];

					// 向上移动
					anim.y -= 0.5;

					// 淡出效果
					anim.alpha -= 0.011; // 1/90

					// 减少生命周期
					anim.life--;

					// 移除结束的动画
					if (anim.life <= 0) {
						this.data.comboAnimations.splice(i, 1);
					}
				}
			}

			// 下一关
			nextLevel() {
				// 添加过关动画
				const centerX = this.config.canvasSize / 2;
				const centerY = this.config.canvasSize / 2;

				// 创建过关粒子效果
				for (let i = 0; i < 100; i++) {
					const angle = Math.random() * Math.PI * 2;
					const speed = 2 + Math.random() * 5;
					this.data.particles.push({
						x: centerX,
						y: centerY,
						vx: Math.cos(angle) * speed,
						vy: Math.sin(angle) * speed,
						size: 4 + Math.random() * 8,
						color: this.state.levelColors[Math.floor(Math.random() * this.state.levelColors.length)],
						life: 30 + Math.random() * 30,
						maxLife: 60
					});
				}

				// 添加过关文本动画
				this.data.scoreAnimations.push({
					x: centerX,
					y: centerY,
					text: `过关!`,
					alpha: 1.0,
					size: 48,
					fontSize: 'bold 48px Arial',
					life: 60
				});

				// 原有代码保持不变...
				this.state.level++;

				// 速度增长公式：基础速度 * (1 + 关卡^0.7 / 20)
				const speedFactor = 1 + Math.pow(this.state.level, 0.7) / 20;
				this.data.ballSpeed = this.config.ballSpeed * speedFactor;

				// 动态调整生成间隔
				this.updateGenerationInterval();

				this.data.ballSpeed *= 1.25;
				if (this.state.level > this.config.maxLevel) {
					this.state.level = this.config.maxLevel;
					this.showVictoryScreen();
					return;
				}

				this.updateUI();

				// 重新生成并绘制轨道
				this.createTrack();
				core.clearMap('track-canvas');
				this.drawTrack();

				// 初始化新关卡数据
				this.initLevel();

				// 重新创建发射器
				this.createShooter();

				// 重置连击状态
				this.state.comboCount = 0;
				this.state.comboMultiplier = 1;
			}


			// 显示胜利画面
			showVictoryScreen() {
				const centerX = this.config.canvasSize / 2;
				const centerY = this.config.canvasSize / 2;

				// 半透明覆盖层
				core.fillArc('ui-canvas', centerX, centerY, 140, 0, Math.PI * 2, 'rgba(0, 0, 0, 0.85)');

				// 绘制胜利文本
				core.fillBoldText(
					'ui-canvas',
					'恭喜通关!',
					centerX, centerY - 50,
					'#4CAF50',
					'#000000',
					'bold 48px Arial'
				);

				core.fillBoldText(
					'ui-canvas',
					`最终得分: ${this.state.score}`,
					centerX, centerY,
					'#FFFFFF',
					'#000000',
					'bold 24px Arial'
				);

				// 显示重新开始按钮
				this.buttons.restart.visible = true;
				this.stopGameLoop();
			}

			// 结束游戏
			endGame() {
				this.state.gameOver = true;
				this.stopGameLoop();
				this.drawGameOverScreen();
			}

			// 绘制游戏结束画面
			drawGameOverScreen() {
				const centerX = this.config.canvasSize / 2;
				const centerY = this.config.canvasSize / 2;

				// 绘制游戏结束文本
				core.fillBoldText(
					'ui-canvas',
					'游戏结束',
					centerX, centerY - 50,
					'#FF5252',
					'#000000',
					'bold 48px Arial'
				);

				core.fillBoldText(
					'ui-canvas',
					`最终得分: ${this.state.score}`,
					centerX, centerY,
					'#FFFFFF',
					'#000000',
					'bold 24px Arial'
				);

				// 显示重新开始按钮
				this.buttons.restart.visible = true;
			}

			// 绘制游戏
			render() {
				if (!this.state.gameStarted) return;

				// 清空游戏画布
				core.clearMap('game-canvas');

				// 绘制珠子链
				this.drawBallChains();

				// 绘制飞行中的珠子
				if (this.data.flyingBall) {
					this.drawBall(
						this.data.flyingBall.x,
						this.data.flyingBall.y,
						this.data.flyingBall.color
					);
				}

				// 绘制发射器
				this.drawShooter();

				// 绘制粒子效果
				this.drawParticles();

				// 绘制UI
				this.drawUI();

				// 绘制分数动画
				this.drawScoreAnimations();

				// 绘制连击动画
				this.drawComboAnimations();
			}

			// 绘制UI
			drawUI() {
				// 清空UI画布
				core.clearMap('ui-canvas');

				// 绘制关卡信息
				this.drawLevelInfo();

				// 绘制按钮
				this.drawAllButtons();

				// 绘制暂停屏幕
				if (this.state.paused) {
					this.drawPauseScreen();
				}

				// 绘制游戏结束屏幕
				if (this.state.gameOver) {
					const centerX = this.config.canvasSize / 2;
					const centerY = this.config.canvasSize / 2;

					// 绘制游戏结束文本
					core.fillBoldText(
						'ui-canvas',
						'游戏结束',
						centerX, centerY - 50,
						'#FF5252',
						'#000000',
						'bold 48px Arial'
					);

					core.fillBoldText(
						'ui-canvas',
						`最终得分: ${this.state.score}`,
						centerX, centerY,
						'#FFFFFF',
						'#000000',
						'bold 24px Arial'
					);
				}

				// 绘制胜利屏幕
				if (this.state.level === this.config.maxLevel &&
					this.data.ballChains.every(chain => chain.length === 0)) {
					const centerX = this.config.canvasSize / 2;
					const centerY = this.config.canvasSize / 2;

					// 半透明覆盖层
					core.fillArc('ui-canvas', centerX, centerY, 140, 0, Math.PI * 2, 'rgba(0, 0, 0, 0.85)');

					// 绘制胜利文本
					core.fillBoldText(
						'ui-canvas',
						'恭喜通关!',
						centerX, centerY - 50,
						'#4CAF50',
						'#000000',
						'bold 48px Arial'
					);

					core.fillBoldText(
						'ui-canvas',
						`最终得分: ${this.state.score}`,
						centerX, centerY,
						'#FFFFFF',
						'#000000',
						'bold 24px Arial'
					);
				}

				// 绘制连击指示器
				if (this.state.comboCount > 0) {
					const centerX = this.config.canvasSize / 2;
					const comboY = 100;

					// 背景
					core.fillRoundRect(
						'ui-canvas',
						centerX - 60,
						comboY - 20,
						120,
						40,
						20,
						'rgba(0, 0, 0, 0.5)'
					);

					// 连击文本
					core.fillBoldText(
						'ui-canvas',
						`连击: x${this.state.comboCount}`,
						centerX, comboY,
						'#FFD700',
						null,
						'bold 20px Arial'
					);

					// 连击加成
					core.fillBoldText(
						'ui-canvas',
						`加成: ${Math.floor((this.state.comboMultiplier - 1) * 100)}%`,
						centerX, comboY + 25,
						'#4CAF50',
						null,
						'bold 16px Arial'
					);
				}
			}

			// 绘制珠子链（多轨道支持）
			drawBallChains() {

				for (let trackIdx = 0; trackIdx < this.data.ballChains.length; trackIdx++) {
					const chain = this.data.ballChains[trackIdx];
					for (let i = 0; i < chain.length; i++) {
						const pos = this.getBallPosition(trackIdx, i);
						this.drawBall(pos.x, pos.y, chain[i].color);
					}
				}
			}

			// 绘制粒子效果
			drawParticles() {
				for (const particle of this.data.particles) {
					const alpha = particle.life / particle.maxLife;
					core.fillCircle(
						'game-canvas',
						particle.x,
						particle.y,
						particle.size,
						particle.color,
						alpha
					);
				}
			}

			// 绘制分数动画
			drawScoreAnimations() {
				for (const anim of this.data.scoreAnimations) {
					core.fillBoldText(
						'ui-canvas',
						`+${anim.points}`,
						anim.x, anim.y,
						'#FFD700',
						'#000000',
						anim.fontSize,
						anim.alpha
					);
				}
			}

			// 绘制连击动画
			drawComboAnimations() {
				for (const anim of this.data.comboAnimations) {
					core.fillBoldText(
						'ui-canvas',
						anim.text,
						anim.x, anim.y,
						'#FF4500',
						'#000000',
						`bold ${anim.size}px Arial`,
						anim.alpha
					);
				}
			}

			// 绘制关卡信息
			drawLevelInfo() {
				const size = this.config.canvasSize;

				if (!this.state.gameStarted) return;

				// 计算待生成球的总数
				const pendingCount = this.data.pendingBalls.reduce(
					(total, queue) => total + queue.length, 0
				);

				// 绘制关卡信息
				core.fillBoldText(
					'ui-canvas',
					`关卡: ${this.state.level}`,
					size - 60, 20,
					'#FFFFFF',
					'#000000',
					'bold 16px Arial'
				);

				// 绘制分数
				core.fillBoldText(
					'ui-canvas',
					`分数: ${this.state.score}`,
					size - 60, 40,
					'#FFD700',
					'#000000',
					'bold 16px Arial'
				);

				// 绘制待生成球数量
				core.fillBoldText(
					'ui-canvas',
					`待生成: ${pendingCount}`,
					size - 60, 60,
					'#FFA500',
					'#000000',
					'bold 16px Arial'
				);

				// 绘制轨道数量
				core.fillBoldText(
					'ui-canvas',
					`轨道: ${this.data.tracks.length}`,
					size - 60, 80,
					'#FFA500',
					'#000000',
					'bold 16px Arial'
				);
			}

			// 修改后的绘制按钮方法
			// 更新 drawButton 方法
			drawButton(button) {
				if (!button.visible) return;

				// 设置按钮颜色
				let bgColor = 'rgba(50, 50, 150, 0.7)';
				if (button === this.buttons.exit || button === this.buttons.startExit) {
					bgColor = 'rgba(150, 50, 50, 0.7)';
				}

				// 圆形按钮（游戏内控制按钮）
				if (button.radius) {
					// 绘制圆形按钮背景
					core.fillCircle(
						'ui-canvas',
						button.x,
						button.y,
						button.radius,
						bgColor
					);

					// 绘制按钮边框
					core.strokeCircle(
						'ui-canvas',
						button.x,
						button.y,
						button.radius,
						'#FFFFFF',
						2
					);

					// 绘制按钮图标
					core.fillText(
						'ui-canvas',
						button.icon,
						button.x,
						button.y,
						'#FFFFFF',
						'bold 16px Arial'
					);
				}
				// 矩形按钮（开始菜单按钮）
				else {
					// 绘制按钮背景
					core.fillRoundRect(
						'ui-canvas',
						button.x - button.width / 2,
						button.y - button.height / 2,
						button.width,
						button.height,
						8,
						bgColor
					);

					// 绘制按钮边框
					core.strokeRoundRect(
						'ui-canvas',
						button.x - button.width / 2,
						button.y - button.height / 2,
						button.width,
						button.height,
						8,
						'#FFFFFF',
						2
					);

					// 绘制按钮文本
					core.fillBoldText(
						'ui-canvas',
						button.icon, // 使用 icon 属性
						button.x, button.y,
						'#FFFFFF',
						null,
						'bold 16px Arial'
					);
				}
			}

			// 绘制所有按钮
			drawAllButtons() {
				for (const key in this.buttons) {
					this.drawButton(this.buttons[key]);
				}
			}

			// 更新按钮可见性
			updateButtonVisibility() {
				// 重置所有按钮
				for (const key in this.buttons) {
					this.buttons[key].visible = false;
				}

				if (this.state.gameStarted) {
					// 游戏进行中显示右上角控制按钮
					if (this.state.paused) {
						this.buttons.resume.visible = true;
					} else {
						this.buttons.pause.visible = true;
					}
					this.buttons.restart.visible = true;
					this.buttons.exit.visible = true;
				} else {
					// 开始菜单显示开始和退出按钮（保持原样）
					this.buttons.start.visible = true;
					this.buttons.startExit.visible = true;
				}
			}

			// 绘制单个珠子
			drawBall(x, y, color) {
				const ballSize = this.config.ballSize;
				const hueMap = {
					red: 0,
					blue: 240,
					green: 120,
					yellow: 60,
					purple: 300
				};
				const hueRotate = hueMap[color] || 0;
				const cacheKey = `ball_${ballSize}_${hueRotate}`;

				// 创建或获取缓存图像
				if (!this.ballCache[cacheKey]) {
					const offscreen = document.createElement('canvas');
					offscreen.width = ballSize;
					offscreen.height = ballSize;
					const ctx = offscreen.getContext('2d');

					// 使用原有绘制方法（通过core.getBlockInfo）
					const info = core.getBlockInfo('redSlime');
					if (info) {
						ctx.filter = `hue-rotate(${hueRotate}deg)`;
						const tileSize = 32;
						const offset = (tileSize - ballSize) / 2;

						ctx.drawImage(
							info.image,
							tileSize * info.posX + offset, // 源图像x起始位置（居中裁剪）
							info.height * info.posY + offset, // 源图像y起始位置（居中裁剪）
							ballSize, // 源图像裁剪宽度
							ballSize, // 源图像裁剪高度
							0, // 画布x位置
							0, // 画布y位置
							ballSize, // 绘制宽度（保持原大小）
							ballSize // 绘制高度（保持原大小）
						);
					} else {
						// 备选绘制方法
						ctx.beginPath();
						ctx.arc(ballSize / 2, ballSize / 2, ballSize / 2 - 2, 0, Math.PI * 2);
						ctx.fillStyle = color;
						ctx.fill();
					}

					this.ballCache[cacheKey] = offscreen;
				}

				// 绘制珠子到游戏画布
				core.drawImage(
					'game-canvas',
					this.ballCache[cacheKey],
					x - ballSize / 2,
					y - ballSize / 2,
					ballSize,
					ballSize
				);
			}

			// 绘制发射器
			drawShooter() {
				const shooterX = this.data.shooterBall.x;
				const shooterY = this.data.shooterBall.y;
				const size = this.config.shooterSize;

				const correctedRotation = this.data.rotation - Math.PI / 2;

				// 绘制旋转炮管
				core.drawImage(
					'game-canvas',
					'hero.png',
					0, 0, 32, 32,
					shooterX - size / 2,
					shooterY - size / 2,
					size,
					size,
					correctedRotation
				);

				// 绘制发射器中的珠子
				this.drawBall(shooterX, shooterY, this.data.shooterBall.color);
			}

			// 绘制暂停屏幕
			drawPauseScreen() {
				const centerX = this.config.canvasSize / 2;
				const centerY = this.config.canvasSize / 2;

				// 绘制暂停文本
				core.fillBoldText(
					'ui-canvas',
					'游戏暂停',
					centerX, centerY - 30,
					'#FFD700',
					'#000000',
					'bold 36px Arial'
				);
			}

			// 游戏主循环
			gameLoop(timestamp) {
				if (this.destroyed) return;

				const deltaTime = this.lastTimestamp ?
					(timestamp - this.lastTimestamp) / 1000 : 0.016;
				this.lastTimestamp = timestamp;

				if (this.state.gameStarted && !this.state.gameOver) {
					if (!this.state.paused) { // 仅在非暂停状态下更新时间
						this.state.playTime += deltaTime * 1000;
						this.update(deltaTime);
					}
					this.render();
				}

				this.animationFrameId = requestAnimationFrame(this.gameLoop.bind(this));
			}

			stopGameLoop() {
				if (this.animationFrameId) {
					cancelAnimationFrame(this.animationFrameId);
					this.animationFrameId = null;
				}
				this.lastTimestamp = 0;
			}

			// 注册事件（整合点击事件）
			registerActions() {
				// 注册统一的点击事件处理
				core.registerAction('onclick', 'zuma_click', (x, y, px, py) => {
					// 检查按钮点击
					for (const key in this.buttons) {
						const button = this.buttons[key];
						if (button.visible) {
							let clicked = false;

							// 圆形按钮检测
							if (button.radius) {
								const dx = px - button.x;
								const dy = py - button.y;
								const distance = Math.sqrt(dx * dx + dy * dy);
								clicked = distance < button.radius;
							}
							// 矩形按钮检测
							else {
								const dx = px - button.x;
								const dy = py - button.y;
								clicked = Math.abs(dx) < button.width / 2 &&
									Math.abs(dy) < button.height / 2;
							}

							if (clicked) {
								this.handleButtonClick(key);
								return true;
							}
						}
					}

					// 如果没有点击按钮，则发射珠子
					this.shootBall(px, py);
					return true;
				}, 100);
			}

			// 处理按钮点击
			handleButtonClick(buttonKey) {
				switch (buttonKey) {
				case 'start':
					this.startGame();
					break;
				case 'pause':
					this.state.paused = true;
					this.buttons.pause.visible = false;
					this.buttons.resume.visible = true;
					this.drawUI();
					break;
				case 'resume':
					this.state.paused = false;
					this.buttons.resume.visible = false;
					this.buttons.pause.visible = true;
					this.drawUI();
					break;
				case 'restart':
					this.resetGame();
					break;
					// 处理退出按钮
				case 'exit':
				case 'startExit':
					this.cleanup();
					break;
				}
			}

			// 更新UI显示
			updateUI() {

			}

			// 游戏结束清理
			cleanup() {
				this.destroyed = true;

				this.ballCache = {}; // 清除图像缓存
				this.data = null; // 释放数据引用

				// 停止游戏循环
				this.stopGameLoop();
				// 注销所有事件
				core.unregisterAction('onclick', 'zuma_click');

				// 删除所有自定义画布
				core.deleteCanvas('background-canvas');
				core.deleteCanvas('track-canvas');
				core.deleteCanvas('game-canvas');
				core.deleteCanvas('ui-canvas');

				// 解锁控制
				core.unlockControl();
				core.doAction();
			}
		}
		let currentGameInstance = null;
		// 启动游戏
		function startZuMaGame() {
			// 清理旧实例
			if (currentGameInstance) {
				currentGameInstance.cleanup();
				currentGameInstance = null;
			}

			// 创建新实例
			currentGameInstance = new ZuMaGame();
			currentGameInstance.init();
		}

		// 启动游戏
		startZuMaGame();
	}
},
    "放置游戏": function () {
	// UI常量对象 - 统一管理所有位置和尺寸
	const UI_CONSTANTS = {
		// 画布位置
		CANVAS_POSITIONS: {
			backgroundCanvas: { x: 0, y: 0, width: 352, height: 352 },
			monsterGoldGame: { x: 0, y: 0, width: 352, height: 352 },
			recruitPopup: { x: 26, y: 50, width: 300, height: 250 },
			recruitResult: { x: 26, y: 50, width: 300, height: 250 },
			evolutionPopup: { x: 26, y: 76, width: 300, height: 200 },
			eventPopup: { x: 36, y: 126, width: 280, height: 120 },
			bossMinions: { x: 60, y: 100, width: 232, height: 20 },
			fragmentPopup: { x: 26, y: 50, width: 300, height: 250 } // 新增碎片弹窗位置
		},

		// 修改：整合BOSS信息区域
		BOSS_BAR: {
			x: 0,
			y: 50,
			width: 352,
			height: 80
		},
		BOSS_ICON: { x: 20, y: 65 },
		BOSS_NAME: { x: 70, y: 60 },
		BOSS_PROGRESS_TEXT: { x: 280, y: 75 },
		BOSS_MINIONS_LABEL: { x: 70, y: 80 },
		BOSS_MINIONS_ICONS_START: { x: 120, y: 80 },
		BOSS_INCOME: { x: 280, y: 90 },

		BUFF_AREA: {
			x: 0,
			y: 115,
			width: 352,
			height: 20
		},

		// 游戏UI元素位置
		TITLE: { x: 176, y: 20 },
		GOLD_ICON: { x: 2, y: 42 },
		GOLD_TEXT: { x: 36, y: 42 },
		INCOME_TEXT: { x: 330, y: 40 },

		// 修改：怪物槽位下移避免重叠
		MONSTER_SLOT: {
			startX: 23,
			startY: 140, // 下移避免与Boss区域重叠
			slotWidth: 80,
			slotHeight: 90,
			innerWidth: 70,
			innerHeight: 80
		},

		// 按钮
		UPGRADE_BUTTON: { x: 15, y: 320, width: 70, height: 30 },
		RECRUIT_BUTTON: { x: 99, y: 320, width: 70, height: 30 },
		EVOLVE_BUTTON: { x: 183, y: 320, width: 70, height: 30 },
		FRAGMENT_BUTTON: { x: 267, y: 320, width: 70, height: 30 },
		CLOSE_BUTTON: { x: 330, y: 10, width: 20, height: 20 },
		RESTART_BUTTON: { x: 330, y: 40, width: 20, height: 20 }
	};

	const gameState = {
		gold: 1000,
		bossGold: 0, // BOSS当前金币
		income: 0,
		bossIncome: 0, // BOSS收入
		monsters: Array(8).fill(null),
		selectedSlot: -1,
		currentBoss: null,
		bossProgress: 0,
		lastSaveTime: 0,
		bosses: [{
				id: "slimelord",
				name: "史莱姆王",
				minions: Array(2).fill("greenSlime").concat(Array(2).fill("redSlime")).concat(Array(2).fill("blackSlime"))
			},
			{
				id: "skeletonCaptain",
				name: "骷髅队长",
				minions: Array(6).fill("skeleton").concat(Array(2).fill("skeletonSoldier"))
			},
			{
				id: "vampire",
				name: "吸血鬼",
				minions: Array(6).fill("zombie").concat(Array(2).fill("zombieKnight"))
			},
			{
				id: "yellowKnight",
				name: "骑士队长",
				minions: Array(3).fill("ghostSkeleton").concat(Array(3).fill("soldier")).concat(Array(3).fill("swordsman")).concat(Array(3).fill("redKnight"))
			},
			{
				id: "redKing",
				name: "假魔王",
				minions: Array(8).fill("whiteKing")
			},
			{
				id: "blackMagician",
				name: "大法师",
				minions: Array(8).fill("darkKnight")
			},
			{
				id: "redKing",
				name: "魔王",
				minions: Array(8).fill("blackMagician")
			}
		],
		// 完整进化表
		evolutionMap: {
			"greenSlime": "redSlime",
			"redSlime": "blackSlime",
			"bat": "bigBat",
			"bluePriest": "redPriest",
			"skeleton": "skeletonSoldier",
			"skeletonSoldier": "skeletonCaptain",
			"yellowGuard": "rock",
			"skeletonCaptain": "ghostSkeleton",
			"blackSlime": "slimeMan",
			"bigBat": "redBat",
			"zombie": "zombieKnight",
			"redPriest": "brownWizard",
			"zombieKnight": "redKnight",
			"rock": "blueGuard",
			"vampire": null,
			"octopus": null,
			"slimeMan": "slimelord",
			"ghostSkeleton": null,
			"soldier": null,
			"swordsman": null,
			"blueGuard": "redGuard",
			"redKnight": "yellowKnight",
			"yellowKnight": "darkKnight",
			"redBat": "vampire",
			"slimelord": null,
			"brownWizard": "redWizard",
			"redWizard": null,
			"darkKnight": null,
			"redGuard": null,
			"magicDragon": null,
			"blackMagician": null,
		},
		// 进化消耗和成功率
		evolutionCost: {
			"greenSlime": { cost: 100, baseRate: 0.8 },
			"redSlime": { cost: 200, baseRate: 0.7 },
			"blackSlime": { cost: 300, baseRate: 0.65 },
			"bat": { cost: 100, baseRate: 0.75 },
			"bigBat": { cost: 250, baseRate: 0.65 },
			"redBat": { cost: 500, baseRate: 0.6 },
			"bluePriest": { cost: 150, baseRate: 0.75 },
			"redPriest": { cost: 300, baseRate: 0.7 },
			"brownWizard": { cost: 600, baseRate: 0.6 },
			"skeleton": { cost: 100, baseRate: 0.8 },
			"skeletonSoldier": { cost: 200, baseRate: 0.7 },
			"skeletonCaptain": { cost: 400, baseRate: 0.6 },
			"ghostSkeleton": { cost: 800, baseRate: 0.5 },
			"yellowGuard": { cost: 150, baseRate: 0.75 },
			"rock": { cost: 300, baseRate: 0.7 },
			"blueGuard": { cost: 500, baseRate: 0.65 },
			"redGuard": { cost: 700, baseRate: 0.6 },
			"zombie": { cost: 200, baseRate: 0.7 },
			"zombieKnight": { cost: 400, baseRate: 0.65 },
			"redKnight": { cost: 600, baseRate: 0.6 },
			"darkKnight": { cost: 1000, baseRate: 0.5 },
			"slimeMan": { cost: 700, baseRate: 0.6 },
			"slimelord": { cost: 1500, baseRate: 0.4 }
		},
		nextEventTime: Date.now() + 30000,
		eventActive: false,
		eventEndTime: 0,
		lastTime: Date.now(),
		activeBuffs: [],
		fragments: {}, // 碎片库存
		starCosts: [0, 40, 80, 120, 160, 200], // 升星所需碎片: [0星,1升2,2升3,...]
		hidden: false, // 标记游戏是否隐藏
		running: true // 标记游戏是否运行
	};

	// 创建怪物对象
	function createMonster(id, stars = 1) {
		return {
			id: id,
			name: core.material.enemys[id].name,
			baseIncome: core.material.enemys[id].money,
			currentIncome: core.material.enemys[id].money,
			canEvolve: gameState.evolutionMap[id] !== undefined,
			buffEffects: [],
			stars: stars, // 星级 (1-5)
			fragments: 0, // 当前拥有的碎片
			nextStarCost: gameState.starCosts[stars] // 升到下一星所需碎片
		};
	}

	// 初始化BOSS手下
	function initBossMinions() {
		const boss = gameState.bosses[0];

		gameState.currentBoss = gameState.bosses[0];
		gameState.bossIncome = 0;

		// 计算BOSS手下总收入
		boss.minions.forEach(minionId => {
			const minionData = core.material.enemys[minionId];
			if (minionData) {
				gameState.bossIncome += minionData.money;
			}
		});

		gameState.bossGold = 10000;
	}

	// 游戏UI元素
	let gameCanvas = null;
	let backgroundCanvas = null;

	// 修改startMonsterGoldGame函数，支持重新打开隐藏的游戏
	this.startMonsterGoldGame = function () {
		// 如果游戏已存在且只是隐藏，重新显示
		if (gameState && gameState.hidden) {
			gameState.hidden = false;
			core.lockControl();

			// 重新创建画布
			const bgPos = UI_CONSTANTS.CANVAS_POSITIONS.backgroundCanvas;
			backgroundCanvas = core.createCanvas("backgroundCanvas", bgPos.x, bgPos.y, bgPos.width, bgPos.height, 130);
			drawBackground();

			const gamePos = UI_CONSTANTS.CANVAS_POSITIONS.monsterGoldGame;
			gameCanvas = core.createCanvas("monsterGoldGame", gamePos.x, gamePos.y, gamePos.width, gamePos.height, 135);

			// 重新注册事件
			registerGameEvents();

			// 绘制UI
			drawGameUI();

			return;
		}

		// 否则正常初始化
		core.lockControl();
		initGame();
	};

	// 初始化游戏
	function initGame() {
		// 使用UI常量
		const bgPos = UI_CONSTANTS.CANVAS_POSITIONS.backgroundCanvas;
		backgroundCanvas = core.createCanvas("backgroundCanvas", bgPos.x, bgPos.y, bgPos.width, bgPos.height, 130);
		drawBackground();

		// 创建游戏画布
		const gamePos = UI_CONSTANTS.CANVAS_POSITIONS.monsterGoldGame;
		gameCanvas = core.createCanvas("monsterGoldGame", gamePos.x, gamePos.y, gamePos.width, gamePos.height, 135);

		// 设置文本对齐和基线
		core.setTextAlign(gameCanvas, "center");
		core.setTextBaseline(gameCanvas, "middle");

		// 初始怪物
		gameState.monsters[0] = createMonster("greenSlime");
		gameState.monsters[1] = createMonster("bat");
		gameState.selectedSlot = 0;

		// 初始化碎片
		gameState.fragments = {

		};

		// 初始化BOSS
		initBossMinions();

		// 初始化游戏状态
		calculateIncome();

		// 注册事件处理
		registerGameEvents();

		// 开始游戏循环
		gameLoop();
	}

	// 绘制背景
	function drawBackground() {
		const size = 352;
		const tileSize = 32;
		const cols = Math.ceil(size / tileSize);
		const rows = Math.ceil(size / tileSize);

		// 平铺绘制地面
		for (let x = 0; x < cols; x++) {
			for (let y = 0; y < rows; y++) {
				core.drawIcon(
					backgroundCanvas,
					'ground',
					x * tileSize,
					y * tileSize,
					tileSize,
					tileSize
				);
			}
		}
	}

	// 计算升星加成
	function calculateStarBonus(stars) {
		// 1星: 100%, 2星: 125%, 3星: 150%, 4星: 175%, 5星: 200%
		return 1 + (stars - 1) * 0.25;
	}

	// 计算总收入
	function calculateIncome() {
		gameState.income = 0;
		for (let i = 0; i < gameState.monsters.length; i++) {
			const monster = gameState.monsters[i];
			if (monster) {
				// 重置收入为基础值
				let income = monster.baseIncome;

				// 应用星级加成
				income *= calculateStarBonus(monster.stars);

				// 应用所有Buff效果
				for (const buff of monster.buffEffects) {
					income = buff.effect(income);
				}

				monster.currentIncome = income;
				gameState.income += income;
			}
		}
	}

	// 升星操作
	function upgradeStar(monster) {
		if (monster.stars >= 5) return; // 已达最高星级

		const cost = monster.nextStarCost;
		const availableFragments = gameState.fragments[monster.id] || 0;
		if (availableFragments < cost) {
			showFloatingText(`${monster.name}碎片不足!需要${cost}个`, 176, 300, "#ff5555");
			return;
		}

		// 扣除碎片
		gameState.fragments[monster.id] = availableFragments - cost;

		// 升星
		monster.stars++;
		monster.fragments = Math.max(0, monster.fragments - cost);
		monster.nextStarCost = gameState.starCosts[monster.stars];

		// 重新计算收入
		calculateIncome();

		showFloatingText(`${monster.name}升到${monster.stars}星!`, 176, 250, "#55ff55");
	}

	// 注册游戏事件
	function registerGameEvents() {
		// 注册点击事件
		core.registerAction("onclick", "monsterGameClick", handleClick, 100);
	}

	// 注销游戏事件
	function unregisterGameEvents() {
		core.unregisterAction("onclick", "monsterGameClick");
	}

	// 处理点击事件 - 修复点击判定问题
	function handleClick(x, y, px, py) {
		// 计算点击位置在352x352游戏区域内的坐标
		const gameX = Math.floor(px);
		const gameY = Math.floor(py);

		// 使用UI常量
		const slotCfg = UI_CONSTANTS.MONSTER_SLOT;

		// 检查怪物槽点击
		if (gameY >= slotCfg.startY && gameY <= slotCfg.startY + 2 * slotCfg.slotHeight) {
			const row = Math.floor((gameY - slotCfg.startY) / slotCfg.slotHeight);
			const col = Math.floor((gameX - slotCfg.startX) / slotCfg.slotWidth);
			const slotIndex = row * 4 + col;

			if (slotIndex >= 0 && slotIndex < 8) {
				// 计算槽位的实际内部区域
				const slotX = slotCfg.startX + col * slotCfg.slotWidth;
				const slotY = slotCfg.startY + row * slotCfg.slotHeight;

				// 精确检查点击是否在槽位内部
				if (gameX >= slotX && gameX <= slotX + slotCfg.innerWidth &&
					gameY >= slotY && gameY <= slotY + slotCfg.innerHeight) {

					if (gameState.monsters[slotIndex]) {
						// 选择已有怪物
						gameState.selectedSlot = slotIndex;
						drawGameUI();
					}
					return true; // 阻止事件继续传播
				}
			}
		}

		// 检查招募按钮点击
		const recruitBtn = UI_CONSTANTS.RECRUIT_BUTTON;
		if (gameX >= recruitBtn.x && gameX <= recruitBtn.x + recruitBtn.width &&
			gameY >= recruitBtn.y && gameY <= recruitBtn.y + recruitBtn.height) {
			showRecruitPopup();
			return true;
		}

		// 检查进化按钮点击
		const evolveBtn = UI_CONSTANTS.EVOLVE_BUTTON;
		if (gameX >= evolveBtn.x && gameX <= evolveBtn.x + evolveBtn.width &&
			gameY >= evolveBtn.y && gameY <= evolveBtn.y + evolveBtn.height) {
			const selectedMonster = gameState.selectedSlot >= 0 ?
				gameState.monsters[gameState.selectedSlot] : null;

			if (selectedMonster && gameState.evolutionMap[selectedMonster.id] && selectedMonster.canEvolve) {
				showEvolutionPopup(selectedMonster);
			}
			return true;
		}

		// 检查升星按钮点击
		const upgradeBtn = UI_CONSTANTS.UPGRADE_BUTTON;
		if (gameX >= upgradeBtn.x && gameX <= upgradeBtn.x + upgradeBtn.width &&
			gameY >= upgradeBtn.y && gameY <= upgradeBtn.y + upgradeBtn.height) {
			const selectedMonster = gameState.selectedSlot >= 0 ?
				gameState.monsters[gameState.selectedSlot] : null;

			if (selectedMonster) {
				upgradeStar(selectedMonster);
			}
			return true;
		}

		// 新增：检查碎片按钮点击
		const fragmentBtn = UI_CONSTANTS.FRAGMENT_BUTTON;
		if (gameX >= fragmentBtn.x && gameX <= fragmentBtn.x + fragmentBtn.width &&
			gameY >= fragmentBtn.y && gameY <= fragmentBtn.y + fragmentBtn.height) {
			showFragmentPopup();
			return true;
		}

		// 检查关闭按钮点击
		const closeBtn = UI_CONSTANTS.CLOSE_BUTTON;
		if (gameX >= closeBtn.x && gameX <= closeBtn.x + closeBtn.width &&
			gameY >= closeBtn.y && gameY <= closeBtn.y + closeBtn.height) {
			hideGame();
			return true;
		}

		// 检查重新开始按钮点击
		const restartBtn = UI_CONSTANTS.RESTART_BUTTON;
		if (gameX >= restartBtn.x && gameX <= restartBtn.x + restartBtn.width &&
			gameY >= restartBtn.y && gameY <= restartBtn.y + restartBtn.height) {
			restartGame();
			return true;
		}

		return false;
	}

	// 显示招募弹窗
	function showRecruitPopup() {
		// 使用UI常量
		const popupPos = UI_CONSTANTS.CANVAS_POSITIONS.recruitPopup;

		// 创建招募弹窗画布
		const recruitCanvas = core.createCanvas("recruitPopup", popupPos.x, popupPos.y, popupPos.width, popupPos.height, 140);
		const ctx = core.getContextByName(recruitCanvas);

		// 修复文本对齐问题
		core.setTextAlign(recruitCanvas, "center");
		core.setTextBaseline(recruitCanvas, "middle");

		// 绘制弹窗背景 - 使用圆角矩形
		core.fillRoundRect(recruitCanvas, 0, 0, popupPos.width, popupPos.height, 10, "rgba(41,67,101,0.9)");
		core.strokeRoundRect(recruitCanvas, 0, 0, popupPos.width, popupPos.height, 10, "#1a3a5f", 2);

		// 绘制标题
		core.fillBoldText(recruitCanvas, "怪物招募", popupPos.width / 2, 30, "#4fc3f7", "#003366", "bold 24px Arial");

		// 绘制招募选项 - 使用圆角矩形
		core.fillRoundRect(recruitCanvas, 50, 60, 200, 60, 8, "rgba(30,100,200,0.7)");
		core.strokeRoundRect(recruitCanvas, 50, 60, 200, 60, 8, "#1a4a9a", 2);
		core.fillBoldText(recruitCanvas, "单次招募 (100金币)", popupPos.width / 2, 90, "#ffffff", "#000000", "bold 16px Arial");

		core.fillRoundRect(recruitCanvas, 50, 140, 200, 60, 8, "rgba(180,30,200,0.7)");
		core.strokeRoundRect(recruitCanvas, 50, 140, 200, 60, 8, "#7a1fa2", 2);
		core.fillBoldText(recruitCanvas, "十连招募 (900金币)", popupPos.width / 2, 170, "#ffffff", "#000000", "bold 16px Arial");

		// 绘制关闭按钮 - 使用圆形
		core.fillCircle(recruitCanvas, popupPos.width - 15, 25, 10, "#e53935");
		core.strokeCircle(recruitCanvas, popupPos.width - 15, 25, 10, "#b71c1c", 2);
		core.fillText(recruitCanvas, "×", popupPos.width - 15, 28, "#ffffff", "bold 16px Arial");

		// 注册统一点击事件处理 - 使用位置常量
		core.registerAction("onclick", "recruitPopupClick", function (x, y, px, py) {
			// 计算在弹窗内的相对坐标
			const offsetX = px - popupPos.x;
			const offsetY = py - popupPos.y;

			// 单次招募区域
			if (offsetX >= 50 && offsetX <= 250 && offsetY >= 60 && offsetY <= 120) {
				recruitMonster(1);
				core.deleteCanvas("recruitPopup");
				core.unregisterAction("onclick", "recruitPopupClick");
				return true;
			}

			// 十连招募区域
			if (offsetX >= 50 && offsetX <= 250 && offsetY >= 140 && offsetY <= 200) {
				recruitMonster(10);
				core.deleteCanvas("recruitPopup");
				core.unregisterAction("onclick", "recruitPopupClick");
				return true;
			}

			// 关闭按钮 (计算圆形点击)
			const dist = Math.sqrt(Math.pow(offsetX - (popupPos.width - 15), 2) + Math.pow(offsetY - 25, 2));
			if (dist <= 12) {
				core.deleteCanvas("recruitPopup");
				core.unregisterAction("onclick", "recruitPopupClick");
				return true;
			}

			return false;
		}, 105);
	}

	// 抽卡概率配置
	const recruitProbabilities = {
		"greenSlime": 0.50, // 50%
		"redSlime": 0.20, // 20%
		"bat": 0.15, // 15%
		"bluePriest": 0.10, // 10%
		"skeleton": 0.05 // 5%
	};

	// 招募怪物
	function recruitMonster(count) {
		const cost = count === 1 ? 100 : 900;

		// 检查金币是否足够
		if (gameState.gold < cost) {
			showFloatingText(`金币不足! 需要${cost}金币`, 176, 300, "#ff5555");
			return;
		}

		// 扣除金币
		gameState.gold -= cost;

		// 获取所有可能的怪物ID (按概率)
		const monsterPool = [];
		for (const [id, prob] of Object.entries(recruitProbabilities)) {
			const count = Math.max(1, Math.floor(prob * 100));
			for (let i = 0; i < count; i++) {
				monsterPool.push(id);
			}
		}

		// 招募怪物
		const recruitedMonsters = [];
		const fragmentGains = {};

		for (let i = 0; i < count; i++) {
			const monsterId = monsterPool[Math.floor(Math.random() * monsterPool.length)];
			const fragments = Math.floor(Math.random() * 3) + 1; // 获得1-3个碎片

			// 查找空槽位
			let placed = false;
			for (let j = 0; j < gameState.monsters.length; j++) {
				if (gameState.monsters[j] === null) {
					// 将新怪物放入空槽位
					gameState.monsters[j] = createMonster(monsterId);
					placed = true;
					break;
				}
			}

			// 记录碎片获取
			fragmentGains[monsterId] = (fragmentGains[monsterId] || 0) + fragments;

			recruitedMonsters.push({
				id: monsterId,
				name: core.material.enemys[monsterId].name,
				fragments: fragments,
				placed: placed
			});
		}

		// 添加碎片到库存
		for (const [monsterId, count] of Object.entries(fragmentGains)) {
			gameState.fragments[monsterId] = (gameState.fragments[monsterId] || 0) + count;
		}

		// 显示招募结果
		showRecruitResult(recruitedMonsters);

		// 更新UI
		calculateIncome();
		drawGameUI();
	}

	// 显示招募结果
	function showRecruitResult(monsters) {
		// 使用UI常量
		const popupPos = UI_CONSTANTS.CANVAS_POSITIONS.recruitResult;

		// 创建结果弹窗画布
		const resultCanvas = core.createCanvas("recruitResult", popupPos.x, popupPos.y, popupPos.width, popupPos.height, 140);
		const ctx = core.getContextByName(resultCanvas);

		// 修复文本对齐问题
		core.setTextAlign(resultCanvas, "center");
		core.setTextBaseline(resultCanvas, "middle");

		// 绘制弹窗背景
		core.fillRoundRect(resultCanvas, 0, 0, popupPos.width, popupPos.height, 10, "rgba(101,67,41,0.9)");
		core.strokeRoundRect(resultCanvas, 0, 0, popupPos.width, popupPos.height, 10, "#5d4037", 2);

		// 绘制标题
		core.fillBoldText(resultCanvas, "招募结果", popupPos.width / 2, 30, "#ff9800", "#663300", "bold 24px Arial");

		// 绘制招募到的怪物
		const count = monsters.length;
		const cols = Math.min(count, 5);
		const startX = (popupPos.width - cols * 50) / 2;

		for (let i = 0; i < count; i++) {
			const col = i % cols;
			const row = Math.floor(i / cols);
			const x = startX + col * 50;
			const y = 60 + row * 70;

			// 绘制怪物图标背景
			core.fillRoundRect(resultCanvas, x, y, 40, 40, 5, "rgba(255,255,255,0.2)");
			core.drawIcon(resultCanvas, monsters[i].id, x + 5, y, 30, 30);
			core.fillText(resultCanvas, monsters[i].name, x + 20, y + 55, "#ffffff", "12px Arial");
			core.fillText(resultCanvas, `${monsters[i].fragments}碎片`, x + 20, y + 70, "#FFD700", "10px Arial");

			// 标记是否放入槽位
			if (monsters[i].placed) {
				core.fillText(resultCanvas, "✓", x + 35, y + 20, "#55ff55", "bold 14px Arial");
			}
		}

		// 绘制关闭按钮
		core.fillCircle(resultCanvas, popupPos.width - 15, 25, 10, "#e53935");
		core.strokeCircle(resultCanvas, popupPos.width - 15, 25, 10, "#b71c1c", 2);
		core.fillText(resultCanvas, "×", popupPos.width - 15, 28, "#ffffff", "bold 16px Arial");

		// 注册统一点击事件处理 - 使用位置常量
		core.registerAction("onclick", "resultPopupClick", function (x, y, px, py) {
			// 计算在弹窗内的相对坐标
			const offsetX = px - popupPos.x;
			const offsetY = py - popupPos.y;

			// 关闭按钮 (计算圆形点击)
			const dist = Math.sqrt(Math.pow(offsetX - (popupPos.width - 15), 2) + Math.pow(offsetY - 25, 2));
			if (dist <= 12) {
				core.deleteCanvas("recruitResult");
				core.unregisterAction("onclick", "resultPopupClick");
				return true;
			}

			return false;
		}, 107);

		// 5秒后自动关闭
		setTimeout(() => {
			if (core.dymCanvas["recruitResult"]) {
				core.deleteCanvas("recruitResult");
				core.unregisterAction("onclick", "resultPopupClick");
			}
		}, 5000);
	}

	// 计算进化成功率
	function calculateEvolutionSuccessRate(monster) {
		// 计算当前累计花费碎片数
		let accumulatedCost = 0;
		for (let i = 1; i < monster.stars; i++) {
			accumulatedCost += gameState.starCosts[i];
		}

		// 计算满星碎片数（1星升到5星）
		const maxFragments = gameState.starCosts.slice(1, 5).reduce((sum, cost) => sum + cost, 0);

		// 成功率 = (累计花费碎片数 / 满星碎片数) * 95%
		let successRate = (accumulatedCost / maxFragments) * 0.95;

		// 1星成功率为0，5星成功率为95%
		return Math.min(0.95, Math.max(0, successRate));
	}

	// 显示进化弹窗
	function showEvolutionPopup(monster) {
		const nextMonsterId = gameState.evolutionMap[monster.id];
		if (!nextMonsterId) return;

		const evolutionCost = gameState.evolutionCost[monster.id] || { cost: 300, baseRate: 0.7 };
		const successRate = calculateEvolutionSuccessRate(monster);

		// 使用UI常量
		const popupPos = UI_CONSTANTS.CANVAS_POSITIONS.evolutionPopup;

		// 创建进化弹窗画布
		const evolutionCanvas = core.createCanvas("evolutionPopup", popupPos.x, popupPos.y, popupPos.width, popupPos.height, 140);
		const ctx = core.getContextByName(evolutionCanvas);

		// 修复文本对齐问题
		core.setTextAlign(evolutionCanvas, "center");
		core.setTextBaseline(evolutionCanvas, "middle");

		// 绘制弹窗背景
		core.fillRoundRect(evolutionCanvas, 0, 0, popupPos.width, popupPos.height, 10, "rgba(27,94,32,0.9)");
		core.strokeRoundRect(evolutionCanvas, 0, 0, popupPos.width, popupPos.height, 10, "#1b5e20", 2);

		// 绘制标题
		core.fillBoldText(evolutionCanvas, "怪物进化", popupPos.width / 2, 30, "#76ff03", "#003300", "bold 20px Arial");

		// 添加星级信息
		core.fillBoldText(
			evolutionCanvas,
			`当前星级: ${monster.stars}星`,
			popupPos.width / 2, 40,
			monster.stars >= 3 ? "#55ff55" : "#ff5555",
			"#000000",
			"14px Arial"
		);

		// 绘制怪物信息
		core.drawIcon(evolutionCanvas, monster.id, 70, 70, 40, 40);
		core.fillBoldText(evolutionCanvas, monster.name, 70, 120, "#ffffff", "#000000", "14px Arial");

		// 绘制箭头
		core.fillBoldText(evolutionCanvas, "➡️", popupPos.width / 2, 90, "#ffffff", "#000000", "30px Arial");

		// 绘制目标怪物
		const nextMonsterName = core.material.enemys[nextMonsterId].name;
		core.drawIcon(evolutionCanvas, nextMonsterId, popupPos.width - 70, 70, 40, 40);
		core.fillBoldText(evolutionCanvas, nextMonsterName, popupPos.width - 70, 120, "#ffffff", "#000000", "14px Arial");

		// 绘制消耗金币
		core.drawIcon(evolutionCanvas, "gold", popupPos.width / 2 - 30, 140, 20, 20);
		core.fillBoldText(evolutionCanvas, `消耗金币: ${evolutionCost.cost}`, popupPos.width / 2, 155, "#FFD700", "#000000", "16px Arial");

		// 绘制成功率
		core.fillBoldText(evolutionCanvas, `成功率: ${Math.round(successRate * 100)}%`, popupPos.width / 2, 180, "#4fc3f7", "#000000", "14px Arial");

		if (monster.stars < 3) {
			core.fillBoldText(
				evolutionCanvas,
				"警告: 星级不足! 进化成功率较低且可能死亡",
				popupPos.width / 2, 200,
				"#ff5555",
				"#000000",
				"12px Arial"
			);
		}

		// 绘制操作按钮
		core.fillRoundRect(evolutionCanvas, 70, 150, 70, 30, 5, "rgba(46,125,50,0.8)");
		core.strokeRoundRect(evolutionCanvas, 70, 150, 70, 30, 5, "#2e7d32", 2);
		core.fillBoldText(evolutionCanvas, "进化", 105, 170, "#ffffff", "#000000", "16px Arial");

		core.fillRoundRect(evolutionCanvas, 160, 150, 70, 30, 5, "rgba(100,100,100,0.8)");
		core.strokeRoundRect(evolutionCanvas, 160, 150, 70, 30, 5, "#424242", 2);
		core.fillBoldText(evolutionCanvas, "取消", 195, 170, "#ffffff", "#000000", "16px Arial");

		// 注册统一点击事件处理 - 使用位置常量
		core.registerAction("onclick", "evolutionPopupClick", function (x, y, px, py) {
			// 计算在弹窗内的相对坐标
			const offsetX = px - popupPos.x;
			const offsetY = py - popupPos.y;

			// 进化按钮
			if (offsetX >= 70 && offsetX <= 140 && offsetY >= 150 && offsetY <= 180) {
				performEvolution(monster, evolutionCost, successRate);
				core.deleteCanvas("evolutionPopup");
				core.unregisterAction("onclick", "evolutionPopupClick");
				return true;
			}

			// 取消按钮
			if (offsetX >= 160 && offsetX <= 230 && offsetY >= 150 && offsetY <= 180) {
				core.deleteCanvas("evolutionPopup");
				core.unregisterAction("onclick", "evolutionPopupClick");
				return true;
			}

			return false;
		}, 105);
	}

	// 执行进化
	function performEvolution(monster, evolution, successRate) {
		// 检查金币
		if (gameState.gold < evolution.cost) {
			showFloatingText("金币不足！", 176, 300, "#ff5555");
			return;
		}

		// 扣除金币
		gameState.gold -= evolution.cost;
		showFloatingText(`-${evolution.cost}`, 176, 300, "#ff5555");

		// 进化成功率
		const success = Math.random() < successRate;
		const nextMonsterId = gameState.evolutionMap[monster.id];

		if (success) {
			// 进化成功
			monster.id = nextMonsterId;
			monster.name = core.material.enemys[nextMonsterId].name;
			monster.baseIncome = core.material.enemys[nextMonsterId].money;
			monster.stars = 1; // 进化后星级重置为1
			monster.fragments = 0;
			monster.nextStarCost = gameState.starCosts[1];
			monster.canEvolve = gameState.evolutionMap[nextMonsterId] !== undefined;

			showFloatingText("进化成功！", 176, 250, "#55ff55");
		} else {
			// 进化失败
			if (monster.stars < 3) {
				// 低星级进化失败 - 怪物死亡
				const slotIndex = gameState.monsters.indexOf(monster);
				if (slotIndex >= 0) {
					gameState.monsters[slotIndex] = null;
				}
				showFloatingText("进化失败! 怪物死亡", 176, 250, "#ff5555");
			} else {
				// 高星级进化失败 - 只是暂时不能进化
				monster.canEvolve = false;
				showFloatingText("进化失败！", 176, 250, "#ff5555");
			}
		}

		// 重新计算收入
		calculateIncome();

		// 更新UI
		drawGameUI();
	}

	// 显示浮动文字
	// 显示浮动文字 - 修复版
	function showFloatingText(text, x, y, color = "#ffffff") {
		// 1. 检查游戏是否隐藏 - 阻止在隐藏状态下显示
		if (gameState.hidden || !gameState.running) return;

		// 2. 创建唯一的画布名称
		const canvasName = `floatText_${Date.now()}}`;

		// 3. 创建画布
		const floatCanvas = core.createCanvas(canvasName, x - 50, y - 20, 100, 40, 140);
		if (!floatCanvas) return; // 创建失败时退出

		// 4. 设置文本对齐
		core.setTextAlign(floatCanvas, "center");
		core.setTextBaseline(floatCanvas, "middle");

		// 5. 绘制文字背景
		core.fillRoundRect(floatCanvas, 0, 0, 100, 40, 5, "rgba(0,0,0,0.5)");
		core.fillBoldText(floatCanvas, text, 50, 20, color, "#000000", "bold 16px Arial", 100);

		// 6. 动画状态
		let posY = y;
		let opacity = 1;
		let destroyed = false; // 标记是否已被销毁

		// 7. 动画函数
		function animate() {
			// 检查是否已被销毁
			if (destroyed || !core.dymCanvas[canvasName]) return;

			posY -= 1;
			opacity -= 0.02;

			// 更新位置和透明度
			core.relocateCanvas(canvasName, x - 50, posY - 20);
			core.setOpacity(canvasName, opacity);

			// 清除并重绘
			core.clearMap(floatCanvas);
			core.fillRoundRect(floatCanvas, 0, 0, 100, 40, 5, "rgba(0,0,0,0.5)");
			core.fillBoldText(floatCanvas, text, 50, 20, color, "#000000", "bold 16px Arial", 100);

			// 检查是否结束
			if (opacity <= 0) {
				destroyFloatText();
			} else {
				requestAnimationFrame(animate);
			}
		}

		// 8. 销毁函数
		function destroyFloatText() {
			if (destroyed) return;
			destroyed = true;

			// 删除画布
			if (core.dymCanvas[canvasName]) {
				core.deleteCanvas(canvasName);
			}
		}

		// 9. 开始动画
		animate();

		// 10. 返回销毁函数以便外部调用
		return destroyFloatText;
	}

	// 绘制五角星
	function drawStar(canvas, cx, cy, radius, style) {
		const points = [];
		for (let i = 0; i < 10; i++) {
			const angle = Math.PI / 2 + i * 2 * Math.PI / 10;
			const r = i % 2 === 0 ? radius : radius * 0.4;
			points.push([
				cx + r * Math.cos(angle),
				cy + r * Math.sin(angle)
			]);
		}
		core.fillPolygon(canvas, points, style);
	}

	// 新增：显示碎片弹窗
	function showFragmentPopup() {
		const popupPos = UI_CONSTANTS.CANVAS_POSITIONS.fragmentPopup;
		const fragmentCanvas = core.createCanvas("fragmentPopup", popupPos.x, popupPos.y, popupPos.width, popupPos.height, 140);

		// 绘制弹窗背景
		core.fillRoundRect(fragmentCanvas, 0, 0, popupPos.width, popupPos.height, 10, "rgba(75,54,33,0.9)");
		core.strokeRoundRect(fragmentCanvas, 0, 0, popupPos.width, popupPos.height, 10, "#4e342e", 2);

		// 绘制标题
		core.setTextAlign(fragmentCanvas, "center");
		core.setTextBaseline(fragmentCanvas, "middle");
		core.fillBoldText(fragmentCanvas, "怪物碎片图鉴", popupPos.width / 2, 25, "#ffd54f", "#3e2723", "bold 20px Arial");

		// 绘制碎片列表
		let yPos = 50;
		let hasFragments = false;

		for (const id in gameState.fragments) {
			if (gameState.fragments[id] > 0) {
				hasFragments = true;
				const monster = core.material.enemys[id];
				const count = gameState.fragments[id];
				const nextEvolution = gameState.evolutionMap[id];

				// 绘制怪物图标
				core.drawIcon(fragmentCanvas, id, 20, yPos - 10, 30, 30);

				// 绘制怪物名称和碎片数量
				core.setTextAlign(fragmentCanvas, "left");
				core.fillText(fragmentCanvas, `${monster.name}`, 60, yPos, "#ffffff", "14px Arial");
				core.fillText(fragmentCanvas, `碎片: ${count}`, 60, yPos + 20, "#ffd54f", "12px Arial");

				// 绘制进化信息
				if (nextEvolution) {
					const nextName = core.material.enemys[nextEvolution].name || "未知";
					core.fillText(fragmentCanvas, `可进化: ${nextName}`, 180, yPos + 10, "#a5d6a7", "12px Arial");
				}

				yPos += 45;
			}
		}

		// 如果没有碎片
		if (!hasFragments) {
			core.fillText(fragmentCanvas, "暂无碎片，请通过招募获取", popupPos.width / 2, 150, "#bdbdbd", "16px Arial");
		}

		// 绘制关闭按钮
		core.fillCircle(fragmentCanvas, popupPos.width - 15, 25, 10, "#e53935");
		core.strokeCircle(fragmentCanvas, popupPos.width - 15, 25, 10, "#b71c1c", 2);
		core.fillText(fragmentCanvas, "×", popupPos.width - 15, 28, "#ffffff", "bold 16px Arial");

		// 注册点击事件
		core.registerAction("onclick", "fragmentPopupClick", function (x, y, px, py) {
			const offsetX = px - popupPos.x;
			const offsetY = py - popupPos.y;

			// 关闭按钮
			const dist = Math.sqrt(Math.pow(offsetX - (popupPos.width - 15), 2) + Math.pow(offsetY - 25, 2));
			if (dist <= 12) {
				core.deleteCanvas("fragmentPopup");
				core.unregisterAction("onclick", "fragmentPopupClick");
				return true;
			}

			return false;
		}, 105);
	}

	// 绘制游戏UI - 使用常量避免重叠
	function drawGameUI() {
		// 清空游戏画布
		core.clearMap(gameCanvas);

		// 绘制背景
		core.fillRect(gameCanvas, 0, 0, 352, 352, "#2c3e50");

		// 使用UI常量
		const ui = UI_CONSTANTS;

		// 绘制标题
		core.setTextAlign(gameCanvas, "center");
		core.fillBoldText(
			gameCanvas, "富豪大作战",
			ui.TITLE.x, ui.TITLE.y,
			"#FFD700", "#8B0000", "bold 24px Arial"
		);
		core.fillText(
			gameCanvas, "比boss赚更多钱击败它！",
			ui.TITLE.x, ui.TITLE.y + 16,
			"#FFFFFF", "bold 8px Arial"
		);

		// 绘制金币信息
		core.drawIcon(gameCanvas, "gold", ui.GOLD_ICON.x, ui.GOLD_ICON.y, 25, 25);
		core.setTextAlign(gameCanvas, "left");
		core.fillBoldText(
			gameCanvas, `金币: ${Math.floor(gameState.gold)}`,
			ui.GOLD_TEXT.x, ui.GOLD_TEXT.y,
			"#FFD700", "#000000", "bold 16px Arial"
		);

		// 绘制收入信息
		let incomeColor = "#55ff55";
		let incomeEffectText = "";

		if (gameState.activeBuffs.length > 0) {
			const buff = gameState.activeBuffs[0];
			if (buff.type === "incomeUp") {
				incomeColor = "#ffff00";
				incomeEffectText = ` (${buff.multiplier}x)`;
			} else if (buff.type === "incomeDown") {
				incomeColor = "#ff5555";
				incomeEffectText = ` (${buff.multiplier}x)`;
			}
		}

		core.setTextAlign(gameCanvas, "right");
		core.fillBoldText(
			gameCanvas, `收入: ${gameState.income}/秒${incomeEffectText}`,
			ui.INCOME_TEXT.x, ui.INCOME_TEXT.y,
			incomeColor, "#000000", "bold 16px Arial"
		);

		// 修改：绘制整合的Boss信息栏
		const bossBar = ui.BOSS_BAR;
		core.fillRoundRect(
			gameCanvas,
			bossBar.x, bossBar.y,
			bossBar.width, bossBar.height,
			5, "rgba(40,20,10,0.7)"
		);

		// 绘制Boss图标和名称
		const currentBoss = gameState.currentBoss;
		core.drawIcon(gameCanvas, currentBoss.id, ui.BOSS_ICON.x, ui.BOSS_ICON.y, 40, 40);
		core.setTextAlign(gameCanvas, "left");
		core.fillBoldText(
			gameCanvas, currentBoss.name,
			ui.BOSS_NAME.x, ui.BOSS_NAME.y,
			"#ff5555", "#000000", "bold 14px Arial"
		);

		core.fillBoldText(
			gameCanvas, `金币: ${Math.round(gameState.bossGold)}`,
			ui.BOSS_PROGRESS_TEXT.x, ui.BOSS_PROGRESS_TEXT.y,
			"#ff9800", "#000000", "12px Arial"
		);


		// 绘制手下信息
		core.fillText(
			gameCanvas, "手下:",
			ui.BOSS_MINIONS_LABEL.x, ui.BOSS_MINIONS_LABEL.y,
			"#ffffff", "12px Arial"
		);

		// 绘制手下图标
		const minionTypes = {};
		currentBoss.minions.forEach(minionId => {
			minionTypes[minionId] = (minionTypes[minionId] || 0) + 1;
		});

		let minionX = ui.BOSS_MINIONS_ICONS_START.x;
		for (const [minionId, count] of Object.entries(minionTypes)) {
			core.drawIcon(gameCanvas, minionId, minionX, ui.BOSS_MINIONS_ICONS_START.y - 5, 32, 32);
			core.fillText(gameCanvas, `x${count}`, minionX + 40, ui.BOSS_MINIONS_ICONS_START.y + 16, "#ffffff", "20px Arial");
			minionX += 80;
		}

		// 绘制BOSS收入
		core.fillText(
			gameCanvas, `产出: ${gameState.bossIncome}/秒`,
			ui.BOSS_INCOME.x, ui.BOSS_INCOME.y,
			"#ff5555", "12px Arial"
		);

		// 绘制Buff信息区域
		if (gameState.activeBuffs.length > 0) {
			const buffArea = ui.BUFF_AREA;
			core.fillRoundRect(
				gameCanvas,
				buffArea.x, buffArea.y - 10,
				buffArea.width, buffArea.height,
				5, "rgba(0,0,0,0.5)"
			);

			const buff = gameState.activeBuffs[0];
			const buffColor = buff.type === "incomeUp" ? "#55ff55" : "#ff5555";
			const buffText = `${buff.name} (剩余: ${Math.ceil((buff.endTime - Date.now()) / 1000)}秒)`;

			core.fillBoldText(
				gameCanvas, buffText,
				buffArea.x + buffArea.width / 2, buffArea.y,
				buffColor, "#000000", "12px Arial"
			);
		}

		// 绘制怪物槽位 (4x2布局)
		const slotCfg = ui.MONSTER_SLOT;
		for (let i = 0; i < 8; i++) {
			const row = Math.floor(i / 4);
			const col = i % 4;
			const x = slotCfg.startX + col * slotCfg.slotWidth;
			const y = slotCfg.startY + row * slotCfg.slotHeight;

			// 绘制槽位背景
			const isSelected = i === gameState.selectedSlot;
			core.fillRoundRect(
				gameCanvas, x, y, slotCfg.innerWidth, slotCfg.innerHeight, 8,
				isSelected ? "rgba(255,152,0,0.3)" : "rgba(93,64,55,0.5)"
			);
			core.strokeRoundRect(
				gameCanvas, x, y, slotCfg.innerWidth, slotCfg.innerHeight, 8,
				isSelected ? "#ff9800" : "#5d4037", 2
			);

			// 绘制怪物信息
			const monster = gameState.monsters[i];
			if (monster) {
				// 绘制怪物图标 - 添加边框效果
				const ctx = gameCanvas;
				ctx.save();
				if (monster.buffEffects.length > 0) {
					const buff = monster.buffEffects[0];
					if (buff.type === "incomeUp") {
						// 增益效果 - 绿色发光边框
						ctx.shadowColor = "#55ff55";
						ctx.shadowBlur = 10;
					} else if (buff.type === "incomeDown") {
						// 减益效果 - 红色发光边框
						ctx.shadowColor = "#ff5555";
						ctx.shadowBlur = 10;
					}
				}
				core.drawIcon(gameCanvas, monster.id, x + 15, y + 15, 40, 40);
				ctx.restore();

				// 绘制星级
				for (let s = 0; s < monster.stars; s++) {
					drawStar(
						gameCanvas,
						x + 10 + s * 12,
						y + 5,
						5,
						s >= 3 ? "#FFD700" : "#ff9800" // 3星以上金色，以下橙色
					);
				}

				// 绘制怪物名称
				core.setTextAlign(gameCanvas, "center");
				core.fillBoldText(gameCanvas, monster.name, x + 35, y + 65, "#ffffff", "#000000", "10px Arial");

				// 绘制怪物产出 - 使用不同颜色显示效果
				let incomeColor = "#FFD700"; // 默认金色
				if (monster.buffEffects.length > 0) {
					const buff = monster.buffEffects[0];
					if (buff.type === "incomeUp") {
						incomeColor = "#55ff55"; // 增益效果绿色
					} else if (buff.type === "incomeDown") {
						incomeColor = "#ff5555"; // 减益效果红色
					}
				}

				core.fillBoldText(
					gameCanvas,
					`+${monster.currentIncome}/秒`,
					x + 35, y + 78,
					incomeColor, "#000000",
					"10px Arial"
				);

				// 绘制碎片信息（从全局碎片库存获取）
				const fragmentCount = gameState.fragments[monster.id] || 0;
				const nextStarCost = monster.nextStarCost;

				core.fillBoldText(
					gameCanvas,
					`${fragmentCount}/${nextStarCost}`,
					x + 35, y + 15,
					fragmentCount >= nextStarCost ? "#55ff55" : "#ff9800",
					"#000000",
					"10px Arial"
				);
			} else {
				// 绘制空槽位提示
				core.setTextAlign(gameCanvas, "center");
				core.fillBoldText(
					gameCanvas, "空",
					x + slotCfg.innerWidth / 2, y + slotCfg.innerHeight / 2,
					"#8d6e63", "#000000", "14px Arial"
				);
			}
		}

		// 绘制操作按钮
		const upgradeBtn = ui.UPGRADE_BUTTON;
		const selectedMonster = gameState.selectedSlot >= 0 ?
			gameState.monsters[gameState.selectedSlot] : null;
		const canUpgrade = selectedMonster && selectedMonster.stars < 5 &&
			(gameState.fragments[selectedMonster.id] || 0) >= selectedMonster.nextStarCost;

		core.fillRoundRect(
			gameCanvas,
			upgradeBtn.x, upgradeBtn.y,
			upgradeBtn.width, upgradeBtn.height,
			5, canUpgrade ? "rgba(255,152,0,0.8)" : "rgba(100,100,100,0.5)"
		);
		core.strokeRoundRect(
			gameCanvas,
			upgradeBtn.x, upgradeBtn.y,
			upgradeBtn.width, upgradeBtn.height,
			5, canUpgrade ? "#ff9800" : "#424242", 2
		);
		core.setTextAlign(gameCanvas, "center");
		core.fillBoldText(
			gameCanvas, "升星",
			upgradeBtn.x + upgradeBtn.width / 2, upgradeBtn.y + upgradeBtn.height / 2,
			canUpgrade ? "#ffffff" : "#888888",
			"#000000", "bold 16px Arial"
		);

		const recruitBtn = ui.RECRUIT_BUTTON;
		core.fillRoundRect(
			gameCanvas,
			recruitBtn.x, recruitBtn.y,
			recruitBtn.width, recruitBtn.height,
			5, "rgba(30,100,200,0.8)"
		);
		core.strokeRoundRect(
			gameCanvas,
			recruitBtn.x, recruitBtn.y,
			recruitBtn.width, recruitBtn.height,
			5, "#1a4a9a", 2
		);
		core.fillBoldText(
			gameCanvas, "招募",
			recruitBtn.x + recruitBtn.width / 2, recruitBtn.y + recruitBtn.height / 2,
			"#ffffff", "#000000", "bold 16px Arial"
		);

		const canEvolve = selectedMonster && gameState.evolutionMap[selectedMonster.id] && selectedMonster.canEvolve;
		const evolveBtn = ui.EVOLVE_BUTTON;
		core.fillRoundRect(
			gameCanvas,
			evolveBtn.x, evolveBtn.y,
			evolveBtn.width, evolveBtn.height,
			5, canEvolve ? "rgba(46,125,50,0.8)" : "rgba(100,100,100,0.5)"
		);
		core.strokeRoundRect(
			gameCanvas,
			evolveBtn.x, evolveBtn.y,
			evolveBtn.width, evolveBtn.height,
			5, canEvolve ? "#2e7d32" : "#424242", 2
		);
		core.fillBoldText(
			gameCanvas, "进化",
			evolveBtn.x + evolveBtn.width / 2, evolveBtn.y + evolveBtn.height / 2,
			canEvolve ? "#ffffff" : "#888888",
			"#000000", "bold 16px Arial"
		);

		// 新增：绘制碎片按钮
		const fragmentBtn = ui.FRAGMENT_BUTTON;
		core.fillRoundRect(
			gameCanvas,
			fragmentBtn.x, fragmentBtn.y,
			fragmentBtn.width, fragmentBtn.height,
			5, "rgba(255,213,79,0.8)"
		);
		core.strokeRoundRect(
			gameCanvas,
			fragmentBtn.x, fragmentBtn.y,
			fragmentBtn.width, fragmentBtn.height,
			5, "#ffb300", 2
		);
		core.fillBoldText(
			gameCanvas, "图鉴",
			fragmentBtn.x + fragmentBtn.width / 2, fragmentBtn.y + fragmentBtn.height / 2,
			"#ffffff",
			"#000000", "bold 16px Arial"
		);
		const closeBtn = UI_CONSTANTS.CLOSE_BUTTON;
		core.fillRoundRect(
			gameCanvas,
			closeBtn.x, closeBtn.y,
			closeBtn.width, closeBtn.height,
			10, "#e53935"
		);
		core.fillText(
			gameCanvas, "×",
			closeBtn.x + closeBtn.width / 2, closeBtn.y + closeBtn.height / 2,
			"#ffffff", "bold 16px Arial"
		);

		// 绘制重新开始按钮
		const restartBtn = UI_CONSTANTS.RESTART_BUTTON;
		core.fillRoundRect(
			gameCanvas,
			restartBtn.x, restartBtn.y,
			restartBtn.width, restartBtn.height,
			10, "#4caf50"
		);
		core.fillText(
			gameCanvas, "↻",
			restartBtn.x + restartBtn.width / 2, restartBtn.y + restartBtn.height / 2,
			"#ffffff", "bold 16px Arial"
		);
	}

	// 游戏主循环
	function gameLoop() {
		try {
			// 计算时间差
			const now = Date.now();
			const deltaTime = Math.min(1000, now - gameState.lastTime);
			gameState.lastTime = now;

			// 增加金币（按时间比例）
			const incomeThisFrame = gameState.income * (deltaTime / 1000);
			gameState.gold += incomeThisFrame;

			// 增加BOSS金币
			const bossIncomeThisFrame = gameState.bossIncome * (deltaTime / 1000);
			gameState.bossGold += bossIncomeThisFrame;

			// 检查是否击败BOSS
			if (gameState.gold > gameState.bossGold) {
				// 击败当前BOSS
				const currentBossIndex = gameState.currentBoss;
				if (currentBossIndex < gameState.bosses.length - 1) {
					// 进入下一个BOSS
					gameState.currentBoss = gameState.bosses[currentBossIndex + 1];

					gameState.gold = 0;

					initBossMinions();
					showFloatingText(`击败BOSS! 进入${gameState.bosses[currentBossIndex + 1].name}`, 176, 150, "#55ff55");
				} else {
					// 游戏胜利
					showFloatingText("恭喜! 你击败了所有BOSS!", 176, 150, "#55ff55");
				}
			}

			// 检查事件结束
			if (gameState.eventActive && now > gameState.eventEndTime) {
				gameState.eventActive = false;

				// 移除所有临时Buff
				for (let i = gameState.activeBuffs.length - 1; i >= 0; i--) {
					if (gameState.activeBuffs[i].isTemporary) {
						removeBuff(gameState.activeBuffs[i].id);
					}
				}

				showFloatingText("事件结束", 176, 150, "#aaaaff");
			}

			// 检查随机事件
			if (now > gameState.nextEventTime) {
				triggerRandomEvent();
			}

			if (now - gameState.lastSaveTime > 10000) {
				if (saveGameState()) {
					// 显示保存提示（但不打扰玩家）
					if (!gameState.hidden) {
						showFloatingText("游戏已自动保存", 300, 20, "#55aaff");
					}
				}
			}

			// 更新UI
			drawGameUI();
		} catch (e) {
			console.error("游戏循环错误:", e);
			// 尝试恢复游戏
			gameState.running = true;
		} finally {
			if (gameState.running) {
				requestAnimationFrame(gameLoop);
			}
		}
	}

	// 新增：加载游戏状态
	function loadGameState() {
		try {
			const savedData = core.getLocalStorage('monsterGoldGameSave');
			if (!savedData) return false;

			const saveState = JSON.parse(savedData);


			// 恢复基本状态
			gameState.gold = saveState.gold;
			gameState.bossGold = saveState.bossGold;
			gameState.income = saveState.income;
			gameState.bossIncome = saveState.bossIncome;
			gameState.selectedSlot = saveState.selectedSlot;
			gameState.nextEventTime = saveState.nextEventTime;
			gameState.eventActive = saveState.eventActive;
			gameState.eventEndTime = saveState.eventEndTime;
			gameState.fragments = saveState.fragments;
			gameState.lastSaveTime = saveState.lastSaveTime;

			// 恢复怪物
			gameState.monsters = saveState.monsters.map(monsterData => {
				if (!monsterData) return null;

				const monster = createMonster(monsterData.id, monsterData.stars);
				monster.fragments = monsterData.fragments || 0;
				return monster;
			});

			// 恢复BOSS
			if (saveState.bossIndex >= 0 && saveState.bossIndex < gameState.bosses.length) {
				gameState.currentBoss = gameState.bosses[saveState.bossIndex];
			} else {
				gameState.currentBoss = gameState.bosses[0];
			}

			// 重新计算收入
			calculateIncome();

			return true;
		} catch (e) {
			console.error("加载游戏失败:", e);
			return false;
		}
	}

	// 新增：保存游戏状态
	function saveGameState() {
		try {
			const saveData = {
				gold: gameState.gold,
				bossGold: gameState.bossGold,
				income: gameState.income,
				bossIncome: gameState.bossIncome,
				monsters: gameState.monsters.map(monster =>
					monster ? {
						id: monster.id,
						stars: monster.stars,
						fragments: monster.fragments
					} : null
				),
				selectedSlot: gameState.selectedSlot,
				bossIndex: gameState.bosses.indexOf(gameState.currentBoss),
				nextEventTime: gameState.nextEventTime,
				eventActive: gameState.eventActive,
				eventEndTime: gameState.eventEndTime,
				fragments: gameState.fragments,
				lastSaveTime: Date.now()
			};

			core.setLocalStorage('monsterGoldGameSave', JSON.stringify(saveData));
			gameState.lastSaveTime = Date.now();
			return true;
		} catch (e) {
			console.error("保存游戏失败:", e);
			return false;
		}
	}

	// 添加Buff
	function addBuff(buff) {
		// 应用Buff到所有怪物
		for (let i = 0; i < gameState.monsters.length; i++) {
			const monster = gameState.monsters[i];
			if (monster) {
				monster.buffEffects.push(buff);
			}
		}

		// 添加到全局Buff列表
		gameState.activeBuffs.push(buff);
		calculateIncome();
	}

	// 移除Buff
	function removeBuff(buffId) {
		// 从所有怪物中移除Buff
		for (let i = 0; i < gameState.monsters.length; i++) {
			const monster = gameState.monsters[i];
			if (monster) {
				const beforeLength = monster.buffEffects.length;
				monster.buffEffects = monster.buffEffects.filter(b => b.id !== buffId);
				// 如果Buff被移除，重新计算收入
				if (beforeLength !== monster.buffEffects.length) {
					calculateIncome();
				}
			}
		}

		// 从全局Buff列表中移除
		gameState.activeBuffs = gameState.activeBuffs.filter(b => b.id !== buffId);
	}

	// 触发随机事件
	function triggerRandomEvent() {
		const events = [{
				title: "黄金矿洞",
				content: "发现黄金矿洞！所有怪物产出金币速度提升3倍，持续1分钟！",
				effect: () => {
					// 添加增益Buff
					addBuff({
						id: "goldMine",
						name: "黄金矿洞",
						description: "所有怪物产出金币速度提升3倍",
						type: "incomeUp",
						multiplier: 3,
						isTemporary: true,
						endTime: Date.now() + 60000,
						effect: (income) => income * 3
					});

					gameState.eventActive = true;
					gameState.eventEndTime = Date.now() + 60000;
				}
			},
			{
				title: "魔王突袭",
				content: "魔王发动突袭！你的怪物被吓坏了，金币产出减半，持续30秒！",
				effect: () => {
					// 添加减益Buff
					addBuff({
						id: "bossRaid",
						name: "魔王突袭",
						description: "所有怪物产出金币速度减半",
						type: "incomeDown",
						multiplier: 0.5,
						isTemporary: true,
						endTime: Date.now() + 30000,
						effect: (income) => Math.max(1, Math.floor(income * 0.5))
					});

					gameState.eventActive = true;
					gameState.eventEndTime = Date.now() + 30000;
				}
			},
			{
				title: "史莱姆繁殖",
				content: "你的史莱姆繁殖了！获得一只新的绿史莱姆！",
				effect: () => {
					let slotFound = false;
					// 尝试寻找空槽位
					for (let i = 0; i < gameState.monsters.length; i++) {
						if (gameState.monsters[i] === null) {
							gameState.monsters[i] = createMonster("greenSlime");
							slotFound = true;
							break;
						}
					}

					if (slotFound) {
						// 有空槽位，添加怪物
						showFloatingText("获得绿史莱姆！", 176, 300, "#55ff55");
						calculateIncome();
					} else {
						// 没有空槽位，获得碎片
						const fragmentCount = Math.floor(Math.random() * 3) + 1;
						gameState.fragments["greenSlime"] = (gameState.fragments["greenSlime"] || 0) + fragmentCount;
						showFloatingText(`获得${fragmentCount}个绿史莱姆碎片！`, 176, 300, "#FFD700");
					}
				}
			},
			{
				title: "神秘宝箱",
				content: "发现神秘宝箱！获得500金币！",
				effect: () => {
					gameState.gold += 500;
					showFloatingText("+500", 176, 300, "#55ff55");
				}
			},
			{
				title: "幸运日",
				content: "今天是幸运日！所有怪物产出金币提升20%，持续45秒！",
				effect: () => {
					// 添加增益Buff
					addBuff({
						id: "luckyDay",
						name: "幸运日",
						description: "所有怪物产出金币提升20%",
						type: "incomeUp",
						multiplier: 1.2,
						isTemporary: true,
						endTime: Date.now() + 45000,
						effect: (income) => Math.floor(income * 1.2)
					});

					gameState.eventActive = true;
					gameState.eventEndTime = Date.now() + 45000;
				}
			}
		];

		const event = events[Math.floor(Math.random() * events.length)];
		event.effect();

		if (gameState.hidden) {
			// 创建事件弹窗 - 改为小弹窗
			const popupPos = { x: 10, y: 10, width: 130, height: 30 }; // 左上角小弹窗位置
			const eventCanvas = core.createCanvas("eventPopup", popupPos.x, popupPos.y, popupPos.width, popupPos.height, 140);

			core.setTextBaseline(eventCanvas, "middle");

			// 绘制弹窗背景 - 改为半透明
			core.fillRoundRect(eventCanvas, 0, 0, popupPos.width, popupPos.height, 5, "rgba(0,0,0,0.5)");

			// 只显示事件内容，不显示按钮
			core.fillText(eventCanvas, `事件"${event.title}"发生了...`, popupPos.x, popupPos.height / 2, "#ffffff", "10px Arial", popupPos.width);

			// 渐隐效果
			let opacity = 1; // 初始不透明度
			const fadeDuration = 2000; // 渐隐持续时间(毫秒)
			const fadeStart = 2000; // 开始渐隐的时间(显示2秒后开始渐隐)
			const fadeInterval = 50; // 渐隐间隔(毫秒)
			const fadeStep = fadeInterval / fadeDuration; // 每次减少的不透明度

			const fadeOut = setInterval(() => {
				if (!core.dymCanvas["eventPopup"]) {
					clearInterval(fadeOut); // 清除定时器
					return; // 退出函数
				}
				opacity -= fadeStep;
				if (opacity <= 0) {
					opacity = 0;
					clearInterval(fadeOut);
					core.deleteCanvas("eventPopup");
				}
				core.setOpacity("eventPopup", opacity);
			}, fadeInterval);

			// 延迟开始渐隐
			setTimeout(() => {
				// 确保定时器还在运行(防止提前关闭)
				if (opacity > 0) {
					// 已经开始渐隐则不需要做任何事
				}
			}, fadeStart);

			// 3秒后自动关闭(作为备用，防止渐隐效果出现问题)
			setTimeout(() => {
				clearInterval(fadeOut); // 清除渐隐定时器
				core.deleteCanvas("eventPopup");
			}, 3000);
		} else {
			// 获取位置常量
			const popupPos = UI_CONSTANTS.CANVAS_POSITIONS.eventPopup;

			// 创建事件弹窗
			const eventCanvas = core.createCanvas("eventPopup", popupPos.x, popupPos.y, popupPos.width, popupPos.height, 140);
			const ctx = core.getContextByName(eventCanvas);

			// 修复文本对齐问题
			core.setTextAlign(eventCanvas, "center");
			core.setTextBaseline(eventCanvas, "middle");

			// 绘制弹窗背景
			core.fillRoundRect(eventCanvas, 0, 0, popupPos.width, popupPos.height, 8, "rgba(62,39,35,0.9)");
			core.strokeRoundRect(eventCanvas, 0, 0, popupPos.width, popupPos.height, 8, "#3e2723", 2);

			// 绘制标题
			core.fillBoldText(eventCanvas, event.title, popupPos.width / 2, 25, "#ffab40", "#000000", "bold 18px Arial");

			// 绘制内容
			core.fillText(eventCanvas, event.content, popupPos.width / 2, 60, "#efebe9", "14px Arial", 260);

			// 绘制确认按钮
			core.fillRoundRect(eventCanvas, (popupPos.width - 80) / 2, 85, 80, 25, 5, "rgba(93,64,55,0.8)");
			core.strokeRoundRect(eventCanvas, (popupPos.width - 80) / 2, 85, 80, 25, 5, "#4e342e", 2);
			core.fillBoldText(eventCanvas, "确定", popupPos.width / 2, 98, "#ffffff", "#000000", "14px Arial");

			// 注册统一点击事件处理 - 使用位置常量
			core.registerAction("onclick", "eventPopupClick", function (x, y, px, py) {
				// 计算在弹窗内的相对坐标
				const offsetX = px - popupPos.x;
				const offsetY = py - popupPos.y;

				// 检查是否点击了确认按钮
				if (offsetX >= (popupPos.width - 80) / 2 &&
					offsetX <= (popupPos.width - 80) / 2 + 80 &&
					offsetY >= 85 &&
					offsetY <= 110) {
					core.deleteCanvas("eventPopup");
					core.unregisterAction("onclick", "eventPopupClick");
					return true;
				}
				return false;
			}, 110);
		}


		// 安排下一个事件
		gameState.nextEventTime = Date.now() + 30000 + Math.random() * 90000;
	}

	// 添加隐藏游戏函数
	function hideGame() {
		// 删除所有画布
		core.deleteCanvas("backgroundCanvas");
		core.deleteCanvas("monsterGoldGame");
		core.deleteCanvas("eventPopup");
		core.deleteCanvas("evolutionPopup");
		core.deleteCanvas("recruitPopup");
		core.deleteCanvas("recruitResult");

		// 删除所有浮动文字
		for (const canvasName in core.dymCanvas) {
			if (canvasName.startsWith("floatText_")) {
				core.deleteCanvas(canvasName);
			}
		}

		// 注销游戏事件
		unregisterGameEvents();

		// 设置隐藏状态
		gameState.hidden = true;

		core.unlockControl();
		core.doAction();
	}

	// 添加重新开始函数
	function restartGame() {
		// 重置游戏状态
		gameState.gold = 1000;
		gameState.bossGold = 0;
		gameState.bossTargetGold = 10000;
		gameState.income = 0;
		gameState.bossIncome = 0;
		gameState.monsters = Array(8).fill(null);
		gameState.selectedSlot = -1;
		gameState.currentBoss = "skeletonCaptain";
		gameState.bossProgress = 0;
		gameState.activeBuffs = [];
		gameState.fragments = {
			"greenSlime": 5,
			"bat": 3,
			"redSlime": 2,
			"bluePriest": 1
		};
		gameState.hidden = false;

		if (loadGameState()) {
			showFloatingText("游戏加载成功!", 176, 150, "#55ff55");
		} else {
			gameState.selectedSlot = 0;

			// 重新初始化BOSS
			initBossMinions();
			calculateIncome();
		}

		// 重新绘制UI
		drawBackground();
		drawGameUI();

		// 显示提示信息
		showFloatingText("游戏已重新开始!", 176, 150, "#55ff55");
	}

	// 游戏结束清理
	this.stopMonsterGoldGame = function () {
		unregisterGameEvents();
		core.deleteCanvas("backgroundCanvas");
		core.deleteCanvas("monsterGoldGame");
		core.deleteCanvas("eventPopup");
		core.deleteCanvas("evolutionPopup");
		core.deleteCanvas("recruitPopup");
		core.deleteCanvas("recruitResult");
		core.deleteCanvas("fragmentPopup"); // 新增：清理碎片弹窗

		// 删除所有浮动文字
		for (const canvasName in core.dymCanvas) {
			if (canvasName.startsWith("floatText_")) {
				core.deleteCanvas(canvasName);
			}
		}


		core.unlockControl();
		core.doAction();
	};
},
    "文字显示优化": function () {
	ui.prototype.drawTextContent = function (ctx, content, config) {
		ctx = core.getContextByName(ctx);
		// 设置默认配置项
		var textAttribute = core.status.textAttribute || core.initStatus.textAttribute;
		var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute;
		config = core.clone(config || {});
		config.left = config.left || 0;
		config.right = config.left + (config.maxWidth == null ? core._PX_ : config.maxWidth);
		config.top = config.top || 0;
		config.color = core.arrayToRGBA(config.color || textAttribute.text);
		if (config.bold == null) config.bold = textAttribute.bold;
		config.italic = config.italic || false;
		config.align = config.align || textAttribute.align || "left";
		config.fontSize = config.fontSize || textAttribute.textfont;
		config.lineHeight = config.lineHeight || (config.fontSize * 1.3);
		config.defaultFont = config.font = config.font || globalAttribute.font;
		config.time = config.time || 0;
		config.letterSpacing = config.letterSpacing == null ? (textAttribute.letterSpacing || 0) : config.letterSpacing;

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

		// 创建一个新的临时画布
		var tempCtx = document.createElement('canvas').getContext('2d');
		if (config.isHD && ctx) {
			core.maps._setHDCanvasSize(
				tempCtx,
				ctx.canvas.width,
				ctx.canvas.height
			);
		} else {
			tempCtx.canvas.width = ctx == null ? 1 : ctx.canvas.width;
			tempCtx.canvas.height = ctx == null ? 1 : ctx.canvas.height;
		}
		tempCtx.textBaseline = 'top';
		tempCtx.font = this._buildFont(config.fontSize, config.bold, config.italic, config.font);
		tempCtx.fillStyle = config.color;
		config = this._drawTextContent_draw(ctx, tempCtx, content, config);
		return config;
	}
	ui.prototype._drawTextContent_draw = function (ctx, tempCtx, content, config) {
		// 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) {
				// It works, why?
				const scale = config.isHD ? devicePixelRatio * core.domStyle.scale : 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;
	}

},
    "自动清怪与拾取": function () {
	// 在此增加新插件
	/**
	 * --------------- 安装说明 ---------------
	 *
	 * 1. 首先确保你已经安装了 "高级动画插件" (animate.js)。
	 * 2. 将本插件的代码完整复制到插件编辑器中。
	 * 3. 保证本插件在插件列表中的顺序在 "高级动画插件" 之后。
	 *
	 * --------------- 使用说明 ---------------
	 *
	 * 通过设置 flags.__auto__ 对象来开启或关闭各项功能。
	 * 可用属性如下:
	 * - battle: Boolean, true 为开启自动战斗（仅限0伤或秒杀的怪物）。
	 * - item: Boolean, true 为开启自动拾取道具。
	 * - wall: Boolean, true 为开启自动打开暗墙和消除熔岩（需持有特定道具）。
	 */

	// 依赖高级动画插件，如果不存在则使用空对象防止报错
	const { Transition, hyper, Ticker } = core.plugin.animate ?? {};

	// 道具飞向勇士的“磁吸”动画时长（毫秒）
	const transitionTime = 200;

	// 用于存放所有正在播放的道具拾取动画
	const transitionList = [];

	// 如果不是在播放录像，则启动一个定时器用于刷新动画画布
	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);
		});
	}

	// --- 挂钩（Hook）核心移动函数 ---
	// 每当勇士移动结束，就触发一次自动寻路逻辑

	control.prototype.moveOneStep = function (callback) {
		const res = this.controldata.moveOneStep(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;
	};

	/**
	 * 核心更新函数，在每次移动后触发
	 */
	function update() {
		core.auto();
		if (main.replayChecking) return;
		// 更新所有正在飞行的道具动画的目标点为勇士的当前位置
	}

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

	/**
	 * 检查一个值是否非null或非undefined
	 */
	function has(v) {
		return v !== null && v !== undefined;
	}

	// ==================================================================================
	// =============================== 核心辅助函数 =====================================
	// ==================================================================================

	/**
	 * 判断一个怪物是否应该被自动攻击
	 */
	function canBattle(enemyId, x, y) {
		const loc = `${x},${y}`;
		const floor = core.floors[core.status.floorId];
		const e = core.material.enemys[enemyId];
		const hasEvent =
			has(floor.afterBattle[loc]) ||
			has(floor.beforeBattle[loc]) ||
			has(e.beforeBattle) ||
			has(e.afterBattle) ||
			has(floor.events[loc]);
		if (hasEvent) return false;

		const damage = core.getDamageInfo(enemyId, void 0, x, y)?.damage;
		if (has(damage) && damage <= 0) return true;

		return false;
	}

	/**
	 * 判断一个道具是否应该被自动拾取
	 */
	function canGetItem(item, loc, floorId) {
		return true;
	}

	/**
	 * [新增] 执行拾取道具的操作，包括播放动画
	 * @returns {boolean} - true表示拾取成功，false表示不满足拾取条件
	 */
	function _performGetItem(item, x, y, loc, floorId) {
		if (!canGetItem(item, loc, floorId)) {
			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; // 道具拾取成功
	}

	/**
	 * 检查一个点对于自动寻路来说是否是“绝对不可通行”的。
	 */
	function _isAbsolutelyImpassable(x, y, floorId, autoFlags) {
		const index = x + "," + y;
		const block = core.getBlock(x, y, floorId);

		if (core.status.checkBlock.damage[index] ||
			core.status.checkBlock.repulse[index] ||
			core.status.checkBlock.ambush[index]) {
			return true;
		}

		if (!block || block.disable) return false;

		const event = block.event;
		const cls = event.cls;

		if (((cls.startsWith('enemy') && autoFlags.battle) || (autoFlags.item && cls === 'items') || (autoFlags.wall && ((block.id === 5 && core.hasItem('snow') && core.material.items["snow"].cls === 'constants') ||
				block.id === 2 || block.id === 3))) && !event.data) {
			return false;
		}

		if (event.noPass || event.script || event.event) {
			return true;
		}

		if (event.trigger) {
			if (event.trigger !== 'changeFloor') return true;
			var ignore = core.flags.ignoreChangeFloor;
			if (event.data && event.data.ignoreChangeFloor != null)
				ignore = event.data.ignoreChangeFloor;
			if (!ignore) return true;
		}

		return false;
	}

	/**
	 * 判断一个图块是否是自动寻路的目标。
	 */
	function _getTargetType(block, autoFlags) {
		if (!has(block) || block.disable) return null;

		const event = block.event;
		const cls = event.cls;
		const results = {};
		let isTarget = false;

		if (autoFlags.battle && cls.startsWith('enemy')) {
			if (canBattle(event.id, block.x, block.y)) {
				results.isEnemy = true;
				isTarget = true;
			} else {
				results.isUnbeatableEnemy = true;
				isTarget = true;
			}
		}

		if (autoFlags.item && cls === 'items') {
			results.isItem = true;
			isTarget = true;
		}

		if (autoFlags.wall) {
			if ((block.id === 2 || block.id === 3) && !event.data) {
				results.isWall = true;
				isTarget = true;
			}
			if (block.id === 5 && core.hasItem('snow') && core.material.items["snow"].cls === 'constants') {
				results.isLava = true;
				isTarget = true;
			}
		}

		return isTarget ? results : null;
	}


	/**
	 * [已重写] 广度优先搜索 (BFS)，寻找并处理所有可达的目标
	 */
	function bfs(floorId, deep, autoFlags) {
		core.updateCheckBlock(floorId);

		const { x, y } = core.status.hero.loc;
		const floor = core.status.maps[floorId];
		const directions = Object.entries(core.utils.scan);

		const queue = [
			[x, y]
		];
		const visited = {
			[`${x},${y}`]: true
		};
		let currentDeep = 0;

		while (queue.length > 0 && currentDeep < deep) {
			const levelSize = queue.length;
			for (let i = 0; i < levelSize; i++) {
				const [nx, ny] = queue.shift();

				for (const [dir, move] of directions) {
					const tx = nx + move.x;
					const ty = ny + move.y;
					const loc = `${tx},${ty}`;

					if (tx < 0 || ty < 0 || tx >= floor.width || ty >= floor.height || visited[loc]) {
						continue;
					}
					visited[loc] = true;

					// --- 邻近交互逻辑 ---
					if (_isAbsolutelyImpassable(tx, ty, floorId, autoFlags)) {
						const isSpecialTile = core.status.checkBlock.damage[loc] ||
							core.status.checkBlock.repulse[loc] ||
							core.status.checkBlock.ambush[loc];

						if (isSpecialTile) {
							const block = core.getBlock(tx, ty, floorId);
							if (block && !block.disable && !block.event.data) {
								const event = block.event;
								const cls = event.cls;

								if (autoFlags.battle && cls.startsWith('enemy') && canBattle(event.id, tx, ty)) {
									core.battle(event.id, tx, ty, true);
									core.updateCheckBlock(floorId);
								} else if (autoFlags.wall) {
									if (block.id === 2 || block.id === 3) {
										core.removeBlock(tx, ty);
										core.updateCheckBlock(floorId);
									} else if (block.id === 4 && core.hasItem('snow') && core.material.items["snow"] === 'constants') {
										core.removeBlock(tx, ty);
										core.updateCheckBlock(floorId);
									}
								} else if (autoFlags.item && core.flags.enableGentleClick && cls === 'items') {
									const item = core.material.items[block.event.id];
									// [重构] 调用新的辅助函数
									if (_performGetItem(item, tx, ty, loc, floorId)) {
										core.updateCheckBlock(floorId);
									}
								}
							}
						}
						continue;
					}

					// --- 移动逻辑 ---
					const block = core.getBlock(tx, ty, floorId);
					const targetType = _getTargetType(block, autoFlags);
					let shouldQueue = false;

					if (!targetType) {
						shouldQueue = true;
					} else {
						const { isEnemy, isItem, isWall, isLava, isUnbeatableEnemy } = targetType;

						if (isUnbeatableEnemy) {
							shouldQueue = false;
						} else if (isWall || isLava) {
							core.removeBlock(tx, ty);
							shouldQueue = true;
						} else if (isEnemy) {
							core.battle(block.event.id, tx, ty);
							shouldQueue = true;
						} else if (isItem) {
							const item = core.material.items[block.event.id];
							// [重构] 调用新的辅助函数
							if (_performGetItem(item, tx, ty, loc, floorId)) {
								shouldQueue = true;
							} else {
								shouldQueue = false;
							}
						}
					}

					if (shouldQueue) {
						core.updateCheckBlock(floorId);
						queue.push([tx, ty]);
					}
				}
			}
			currentDeep++;
		}
	}


	/**
	 * 插件主函数，暴露给外部调用
	 */
	this.auto = function () {
		const autoFlags = core.status.hero.flags.__auto__ || {};
		if (Object.keys(autoFlags).length === 0) return;
		if (!core.maps._canMoveDirectly_checkGlobal()) return;

		// --- 逻辑修复 ---
		// 检查勇士当前所站的图块。
		// 如果该图块会造成伤害、推开或触发伏击，则不应执行任何自动操作。
		const { x, y } = core.status.hero.loc;
		const index = x + "," + y;
		core.updateCheckBlock(core.status.floorId); // 确保检测信息是最新
		if (core.status.checkBlock.damage[index] ||
			core.status.checkBlock.repulse[index] ||
			core.status.checkBlock.ambush[index]) {
			return; // 立即返回，停止后续所有自动逻辑
		}
		// --- 修复结束 ---

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

		let deep = Infinity;

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

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

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

	core.registerReplayAction("moveDirectly", function (action) {
		if (action.indexOf("move:") != 0) return false;
		/*
		if (!core.hasFlag('poison') && core.status.thisMap.width < 2 * core.bigmap.extend + core.__SIZE__ &&
			core.status.thisMap.height < 2 * core.bigmap.extend + core.__SIZE__) {
			while (core.status.replay.toReplay.length > 0 &&
				core.status.replay.toReplay[0].indexOf('move:') == 0) {
				core.status.route.push(action);
				action = core.status.replay.toReplay.shift();
			}
		}
		*/

		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 () {
	// 在此增加新插件

	// +++ 核心寻路逻辑函数 +++

	/**
	 * @description 检查寻路过程中的一个点是否可以通行。
	 */
	function _checkPointForPathfinding(blocksObj, x, y, floorId) {
		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;
			var ignore = core.flags.ignoreChangeFloor;
			if (block.event.data && block.event.data.ignoreChangeFloor != null)
				ignore = block.event.data.ignoreChangeFloor;
			if (!ignore) 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;
	};

	/**
	 * @description 使用BFS算法，检查在指定楼层上，从起点到终点是否存在一条可通行的路径。
	 */
	function canReach(startX, startY, endX, endY, floorId) {
		floorId = floorId || core.status.floorId;
		var map = core.status.maps[floorId];
		if (!map) return false;

		if (startX < 0 || startX >= map.width || startY < 0 || startY >= map.height) return false;
		if (endX < 0 || endX >= map.width || endY < 0 || endY >= map.height) return false;
		if (startX == endX && startY == endY) return true;

		var canMoveArray = core.maps.generateMovableArray(floorId);
		var blocksObj = core.maps.getMapBlocksObj(floorId);
		var bgMap = core.maps.getBgMapArray(floorId);
		core.updateCheckBlock(floorId);

		var queue = [];
		var visited = {};

		var startIndex = startX + "," + startY;
		queue.push(startIndex);
		visited[startIndex] = true;

		var head = 0;
		while (head < queue.length) {
			var now = queue[head++].split(","),
				x = parseInt(now[0]),
				y = parseInt(now[1]);

			for (var direction in core.utils.scan) {
				if (!core.inArray(canMoveArray[x][y], direction)) continue;

				var nx = x + core.utils.scan[direction].x;
				var ny = y + core.utils.scan[direction].y;
				var nindex = nx + "," + ny;

				if (nx == endX && ny == endY)
					return true;

				if (nx < 0 || nx >= map.width || ny < 0 || ny >= map.height) continue;
				if (visited[nindex]) continue;
				if (core.onSki(bgMap[ny][nx])) continue;

				if (!_checkPointForPathfinding(blocksObj, nx, ny, floorId)) continue;

				visited[nindex] = true;
				queue.push(nindex);
			}
		}
		return false;
	};


	// +++ 性能优化：混合策略寻路缓存 +++

	/**
	 * @name clearPathfindingCache
	 * @description 清空寻路缓存。主要用于跨楼层改变地形的罕见事件。
	 * @param {string} [floorId] - 如果提供，则只清除指定楼层的缓存和所有跨楼层路径的缓存。
	 * 如果不提供，则清除所有缓存。
	 * @example
	 * // 某个事件改变了'MT10'层的地形，手动清除其缓存
	 * core.clearPathfindingCache('MT10');
	 */
	core.clearPathfindingCache = function (floorId) {
		if (floorId) {
			if (core.status.hero.flags.pathfindingCache[floorId]) {
				delete core.status.hero.flags.pathfindingCache[floorId];
			}
			// crossFloor 缓存已被移除，但保留此逻辑以兼容旧版或意外情况
			if (core.status.hero.flags.pathfindingCache.crossFloor) {
				delete core.status.hero.flags.pathfindingCache.crossFloor;
			}
		} else {
			core.status.hero.flags.pathfindingCache = {};
		}
	};

	/**
	 * @description 实际执行寻路计算的内部函数
	 */
	function _calculateCanToStair(stair, toIndex, startstair) {
		let targetFloorId = core.floorIds[toIndex];

		let sx, sy;
		let currentFloorId = core.status.floorId;
		if (currentFloorId === targetFloorId && !startstair) {
			sx = core.getHeroLoc('x');
			sy = core.getHeroLoc('y');
		} else {
			const startPoint = core.status.maps[targetFloorId][startstair];
			if (!startPoint) return false;
			[sx, sy] = startPoint;
		}

		core.extractBlocks(targetFloorId);
		const floor = core.status.maps[targetFloorId];

		var destLocs = [];
		for (const block of floor.blocks) {
			if (block.event && block.event.id === stair) {
				destLocs.push([block.x, block.y]);
			}
		}
		if (destLocs.length === 0) return false;

		for (const dest of destLocs) {
			if (canReach(sx, sy, dest[0], dest[1], targetFloorId)) {
				return true;
			}
		}
		return false;
	}

	// +++ 重构后的函数（采用混合缓存策略） +++

	/**
	 * @name canToStair
	 * @description (混合缓存) 检查在指定楼层上，是否能从一个起点到达指定的楼梯口。
	 * 只对非当前楼层进行缓存。
	 */
	const canToStair = function (stair, toIndex, startstair) {
		let targetFloorId = core.floorIds[toIndex];
		let currentFloorId = core.status.floorId;

		// 策略核心：如果是当前楼层，总进行实时计算，不使用缓存
		if (targetFloorId === currentFloorId) {
			return _calculateCanToStair(stair, toIndex, startstair);
		}

		// 对于其他楼层，使用缓存
		if (!core.status.hero.flags.pathfindingCache[targetFloorId]) {
			core.status.hero.flags.pathfindingCache[targetFloorId] = {};
		}
		const floorCache = core.status.hero.flags.pathfindingCache[targetFloorId];
		const cacheKey = `canToStair:${stair}:${startstair}`;
		if (floorCache[cacheKey] != null) {
			return floorCache[cacheKey];
		}

		const result = _calculateCanToStair(stair, toIndex, startstair);
		floorCache[cacheKey] = result;
		return result;
	};

	/**
	 * @name canToFloor
	 * @description (已优化) 检查是否能从当前楼层到达目标楼层。
	 * 此函数不再拥有自己的缓存，而是依赖 canToStair 的缓存策略。
	 * 这确保了涉及当前楼层的计算总是实时的。
	 */
	const canToFloor = function (toFloorId) {
		let floorId = core.status.floorId;
		let index = core.floorIds.indexOf(floorId);
		let toIndex = core.floorIds.indexOf(toFloorId);

		// --- 逻辑修正 ---
		// 如果是同层传送，则检查是否能到达本层的下楼梯处
		if (index === toIndex) {
			// 第一个参数 "downFloor" 指目标是下楼梯
			// 第二个参数 "index" 指当前楼层
			// 第三个参数 null 表示从勇士当前位置开始寻路
			return canToStair("downFloor", index, null);
		}

		if (index < toIndex) { // 上楼
			for (let i = index; i < toIndex; i++) {
				// 对第一段路程(i === index)，起点是勇士当前位置(null)
				// 对后续路程，起点是上一层的上楼梯口，即本层的下楼梯口("downFloor")
				if (!canToStair("upFloor", i, i === index ? null : "downFloor")) {
					return false;
				}
			}
			return true;
		} else { // 下楼
			for (let i = index; i > toIndex; i--) {
				// 对第一段路程(i === index)，起点是勇士当前位置(null)
				// 对后续路程，起点是上一层的下楼梯口，即本层的上楼梯口("upFloor")
				if (!canToStair("downFloor", i, i === index ? null : "upFloor")) {
					return false;
				}
			}
			return true;
		}
	};

	// --- 以下为文件中原有的其他函数，保持不变 ---

	const flyTo = function () {
		let floorId = core.floorIds[core.status.event.data];
		if (core.status.maps[floorId].canFlyTo && core.hasVisitedFloor(floorId)) {
			if (canToFloor(floorId)) {
				core.flyTo(floorId);
			} else {
				core.ui.statusBar.print("勇士无法到达指定楼层");
			}
		} else {
			core.ui.statusBar.print("勇士无法到达指定楼层");
		}
	}

	core.ui.drawFly = function (page) {
		core.status.event.data = page;
		let floorId = core.floorIds[page];
		let index = core.floorIds.indexOf(floorId)
		let title = "魔塔 第" + index.toString().padStart(2, "0") + "层"
		core.clearMap('ui');
		core.setAlpha('ui', 0.85);
		core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000');
		core.setAlpha('ui', 1);
		core.setTextAlign('ui', 'center');
		let str = "\r[\#FFFFFF]" + '楼层跳跃';
		if (core.getFlag('楼传削弱')) {
			if (core.status.maps[floorId].canFlyTo) {
				if (core.hasVisitedFloor(floorId)) {
					// 此处调用canToFloor，其内部的canToStair会智能地使用混合缓存策略
					if (canToFloor(floorId)) {
						str += '（\r[\#00FF00]允许传送\r[\#FFFFFF]）';
					} else {
						str += '（\r[\#FF8247]路线堵塞\r[\#FFFFFF]）';
					}
				} else {
					str += '（\r[\#FF8247]尚未抵达\r[\#FFFFFF]）';
				}
			} else {
				str += '（\r[\#FF0000]禁止传送\r[\#FFFFFF]）';
			}
		}

		core.fillText('ui', str, this.HPIXEL, 40, '#FFFFFF', this._buildFont(20, true));
		core.fillText('ui', '返回游戏', this.HPIXEL, this.PIXEL - 13, null, this._buildFont(15, true))

		if (core.getFlag('楼传削弱')) {
			if (core.status.showOnlyReachableFloors == null) {
				core.status.showOnlyReachableFloors = true;
			}
			let modeText = core.status.showOnlyReachableFloors ? '仅可到达' : '显示全部';
			core.fillText('ui', modeText, 80, this.PIXEL - 13, null, this._buildFont(15, true));
		}

		core.setTextAlign('ui', 'center');

		let middle = this.HPIXEL + 39;

		let lines = core.splitLines('ui', title, 120, this._buildFont(19, true));
		let start_y = middle - (lines.length - 1) * 11;
		for (let i in lines) {
			core.fillText('ui', lines[i], this.PIXEL - 60, start_y, '#FFFFFF');
			start_y += 22;
		}
		let radio = core.status.maps[floorId].ratio
		let wallNum = flags["wallNum" + radio]
		if (wallNum && wallNum[floorId]) {
			let num = wallNum[floorId]
			let lines = core.splitLines('ui', title, 120, this._buildFont(19, true));
			core.fillText('ui', "(" + num + ")", this.PIXEL - 30, start_y, '#FFFF00');
		}

		if (core.actions._getNextFlyFloor(1) != page) {
			core.fillText('ui', '▲', this.PIXEL - 40, middle - 64, '#FFFFFF', this._buildFont(17, false));
			core.fillText('ui', '▲', this.PIXEL - 40, middle - 96);
			core.fillText('ui', '▲', this.PIXEL - 40, middle - 96 - 7);
		}
		if (core.actions._getNextFlyFloor(-1) != page) {
			core.fillText('ui', '▼', this.PIXEL - 40, middle + 64, '#FFFFFF', this._buildFont(17, false));
			core.fillText('ui', '▼', this.PIXEL - 40, middle + 96);
			core.fillText('ui', '▼', this.PIXEL - 40, middle + 96 + 7);
		}
		let size = this.PIXEL - 93;
		core.strokeRect('ui', 20, 53, size, size, '#FFFFFF', 2);
		if (floorId === core.status.floorId) {
			core.drawThumbnail(floorId, null, { ctx: 'ui', x: 20, y: 53, size: size, damage: true, heroLoc: core.status.hero.loc });
		} else {
			core.drawThumbnail(floorId, null, { ctx: 'ui', x: 20, y: 53, size: size, damage: true });
		}
	}

	core.actions._getNextFlyFloor = function (delta, index) {
		if (index == null) index = core.status.event.data;
		if (delta == 0) return index;
		var sign = Math.sign(delta);
		delta = Math.abs(delta);
		var ans = index;
		while (true) {
			index += sign;
			if (index < 0 || index >= core.floorIds.length) break;
			var floorId = core.floorIds[index];

			var canFly = core.status.maps[floorId].canFlyTo && core.hasVisitedFloor(floorId);
			if (canFly) {
				if (core.getFlag('楼传削弱') && core.status.showOnlyReachableFloors) {
					if (canToFloor(floorId)) {
						delta--;
						ans = index;
					}
				} else {
					delta--;
					ans = index;
				}
			}
			if (delta == 0) break;
		}
		return ans;
	}

	core.actions._clickFly = function (x, y, px, py) {
		var button_x = core.__PIXELS__ - 40;
		var button_y = core.__PIXELS__ / 2 + 39;
		var size = core.__PIXELS__ - 93;
		if ((px >= button_x - 16 && px <= button_x + 16) && (py >= button_y + 64 - 16 && py <= button_y + 64 + 16)) {
			core.playSound('光标移动');
			core.ui.drawFly(this._getNextFlyFloor(-1));
		}
		if ((px >= button_x - 16 && px <= button_x + 16) && (py >= button_y - 64 - 7 - 16 && py <= button_y - 64 + 16)) {
			core.playSound('光标移动');
			core.ui.drawFly(this._getNextFlyFloor(1));
		}
		if ((px >= button_x - 16 && px <= button_x + 16) && (py >= button_y + 96 - 16 && py <= button_y + 96 + 7 + 16)) {
			core.playSound('光标移动');
			core.ui.drawFly(this._getNextFlyFloor(-10));
		}
		if ((px >= button_x - 16 && px <= button_x + 16) && (py >= button_y - 96 - 16 && py <= button_y - 96 - 7 + 16)) {
			core.playSound('光标移动');
			core.ui.drawFly(this._getNextFlyFloor(10));
		}

		if (core.getFlag('楼传削弱')) {
			if ((px >= 63 && px <= 97) && (py >= core.__PIXELS__ - 28 && py <= core.__PIXELS__ - 9)) {
				core.playSound('光标移动');
				core.status.showOnlyReachableFloors = !core.status.showOnlyReachableFloors;
				core.ui.drawFly(core.status.event.data);
			}
		}

		if (x >= this.HSIZE - 1 && x <= this.HSIZE + 1 && y == this.LAST) {
			core.updateCheckBlock(core.status.floorId);
			core.playSound('取消');
			core.ui.closePanel();
		}
		if ((px >= 20 && px <= 20 + size) && (py >= 53 && py <= 53 + size)) {
			if (core.getFlag('楼传削弱'))
				flyTo();
			else
				core.flyTo(core.floorIds[core.status.event.data]);
		}
		return;
	}

	core.actions._keyUpFly = function (keycode) {
		if (keycode == 71 || keycode == 27) {
			core.updateCheckBlock(core.status.floorId);
			core.playSound('取消');
			core.ui.closePanel();
		}
		if (keycode == 88) {
			if (core.hasItem("book")) core.openBook(true);
		}
		if (keycode == 13 || keycode == 32 || keycode == 67) {
			if (core.getFlag('楼传削弱'))
				flyTo();
			else
				core.flyTo(core.floorIds[core.status.event.data]);
		}
		if (core.getFlag('楼传削弱') && keycode == 81) {
			core.playSound('光标移动');
			core.status.showOnlyReachableFloors = !core.status.showOnlyReachableFloors;
			core.ui.drawFly(core.status.event.data);
		}
		return;
	}

	events.prototype.useFly = function (fromUserAction) {
		if (core.isReplaying()) return;
		if (core.getFlag('楼传削弱')) {
			if (!this._checkStatus('fly', fromUserAction, true)) return;
		} else {
			if (core.status.event.id == 'viewMaps') {
				core.playSound('操作失败');
				core.drawTip('此功能已移除');
				return;
			}
			if (!this._checkStatus('fly', fromUserAction, true)) return;

			// --- 逻辑修正 ---
			// 如果flyNearStair为true，则检查是否能到达楼梯，而不是是否在楼梯边
			if (core.flags.flyNearStair) {
				if (!core.nearStair() && !core.maps._canMoveDirectly_checkGlobal()) {
					core.playSound('操作失败');
					core.drawTip("只能在楼梯边使用！");
					core.unlockControl();
					core.status.event.data = null;
					core.status.event.id = null;
					return;
				}
				let floorId = core.status.floorId;
				let index = core.floorIds.indexOf(floorId);
				// 检查是否能从当前位置到达上楼或下楼梯口
				let canReachUp = canToStair("upFloor", index, null);
				let canReachDown = canToStair("downFloor", index, null);

				if (!canReachUp && !canReachDown) {
					core.playSound('操作失败');
					core.drawTip("附近没有可到达的楼梯，无法使用" + core.material.items['fly'].name, 'fly');
					core.unlockControl();
					core.status.event.data = null;
					core.status.event.id = null;
					return;
				}
			}

			if (!core.canUseItem('fly')) {
				core.playSound('操作失败');
				core.drawTip(core.material.items['fly'].name + "好像失效了", 'fly');
				core.unlockControl();
				core.status.event.data = null;
				core.status.event.id = null;
				return;
			}
		}
		if (core.status.showOnlyReachableFloors == null) {
			core.status.showOnlyReachableFloors = true;
		}
		core.playSound('打开界面');
		core.useItem('fly', true);
		return;
	}

	control.prototype._replayAction_fly = function (action) {
		if (action.indexOf("fly:") != 0) return false;

		// --- 录像回放安全校验 ---
		// 增加与 useFly 中相同的校验，防止通过录像绕过限制
		// 楼传未削弱时，如果 flyNearStair 为 true，检查是否能到达楼梯
		if (!core.getFlag('楼传削弱') && core.flags.flyNearStair) {
			if (!core.nearStair() && !core.maps._canMoveDirectly_checkGlobal()) {
				// 无法到达楼梯，此步操作在当时是非法的，中止回放
				core.control._replay_error(action);
				return false;
			}
			let currentFloorId = core.status.floorId;
			let index = core.floorIds.indexOf(currentFloorId);
			let canReachUp = canToStair("upFloor", index, null);
			let canReachDown = canToStair("downFloor", index, null);

			if (!canReachUp && !canReachDown) {
				// 无法到达楼梯，此步操作在当时是非法的，中止回放
				core.control._replay_error(action);
				return false;
			}
		}

		var floorId = action.substring(4);
		var toIndex = core.floorIds.indexOf(floorId);

		// 检查目标楼层是否存在
		if (toIndex < 0) {
			core.control._replay_error(action);
			return false;
		}

		if (!core.canUseItem('fly')) return false;

		// --- 原有回放逻辑 ---
		core.ui.drawFly(toIndex);
		if (core.status.replay.speed == 24) {
			if (!core.flyTo(floorId, core.replay))
				core.control._replay_error(action);
			return true;
		}
		setTimeout(function () {
			if (!core.flyTo(floorId, core.replay))
				core.control._replay_error(action);
		}, core.control.__replay_getTimeout());
		return true;
	}


	////// 点击怪物手册时的打开操作 //////
	core.events.openBook = function (fromUserAction) {
		if (core.isReplaying()) return;
		// 如果能恢复事件（从callBook事件触发）
		if (core.status.event.id == 'book' && core.events.recoverEvents(core.status.event.interval))
			return;
		// 当前是book，且从“浏览地图”打开
		if (core.status.event.id == 'book' && core.status.event.ui) {
			core.status.boxAnimateObjs = [];
			core.ui.drawFly(core.status.event.ui);
			return;
		}
		// 从“浏览地图”页面打开
		if (core.status.event.id == 'viewMaps' || core.status.event.id == 'fly') {
			fromUserAction = false;
			core.status.event.ui = core.status.event.data;
		}
		if (!this._checkStatus('book', fromUserAction, true)) return;
		core.playSound('打开界面');
		core.useItem('book', true);
	}

	////// 怪物手册界面时，放开某个键的操作 //////
	core.actions._keyUpBook = function (keycode) {
		if (keycode == 27 || keycode == 88) {
			core.playSound('取消');
			if (core.events.recoverEvents(core.status.event.interval)) {
				return;
			} else if (core.status.event.ui != null) {
				core.status.boxAnimateObjs = [];
				if (typeof core.status.event.ui === "number") {
					core.status.event.id = "fly"
					core.ui.drawFly(core.status.event.ui);
				} else {
					core.ui._drawViewMaps(core.status.event.ui);
				}
			} else core.ui.closePanel();
			return;
		}
		if (keycode == 13 || keycode == 32 || keycode == 67) {
			var data = core.status.event.data;
			if (data != null) {
				core.ui._drawBookDetail(data);
			}
			return;
		}
	}

	////// 怪物手册界面的点击操作 //////
	core.actions._clickBook = function (x, y) {
		var pageinfo = core.ui._drawBook_pageinfo();
		// 上一页
		if ((x == this.HSIZE - 2 || x == this.HSIZE - 3) && y == this.LAST) {
			core.playSound('光标移动');
			core.ui.drawBook(core.status.event.data - pageinfo.per_page);
			return;
		}
		// 下一页
		if ((x == this.HSIZE + 2 || x == this.HSIZE + 3) && y == this.LAST) {
			core.playSound('光标移动');
			core.ui.drawBook(core.status.event.data + pageinfo.per_page);
			return;
		}
		// 返回
		if (x >= this.LAST - 2 && y == this.LAST) {
			core.playSound('取消');
			if (core.events.recoverEvents(core.status.event.interval)) {
				return;
			} else if (core.status.event.ui != null) {
				core.status.boxAnimateObjs = [];
				if (typeof core.status.event.ui === "number") {
					core.status.event.id = "fly"
					core.ui.drawFly(core.status.event.ui);
				} else {
					core.ui._drawViewMaps(core.status.event.ui);
				}
			} else core.ui.closePanel();
			return;
		}
		// 怪物信息
		var data = core.status.event.data;
		if (data != null && y < this.LAST) {
			var per_page = pageinfo.per_page,
				page = parseInt(data / per_page);
			var u = this.LAST / per_page;
			for (var i = 0; i < per_page; ++i) {
				if (y >= u * i && y < u * (i + 1)) {
					var index = per_page * page + i;
					core.ui.drawBook(index);
					core.ui._drawBookDetail(index);
					break;
				}
			}
			return;
		}
		return;
	}

	////// 绘制怪物手册 //////
	ui.prototype.drawBook = function (index) {
		var floorId = core.status.floorId;
		if (core.status.event.ui) {
			if (typeof core.status.event.ui === "number") {
				floorId = core.floorIds[core.status.event.ui]
			} else {
				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, true));
	}
	ui.prototype._drawBookDetail_getInfo = function (index) {
		var floorId = core.status.floorId;
		if (core.status.event.ui) {
			if (typeof core.status.event.ui === "number") {
				floorId = core.floorIds[core.status.event.ui]
			} else {
				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);
		if (enemys.length == 0) return [];
		index = core.clamp(index, 0, enemys.length - 1);
		var enemy = enemys[index],
			enemyId = enemy.id;
		var texts = core.enemys.getSpecialHint(enemyId);
		if (texts.length == 0) texts.push("该怪物无特殊属性。");
		if (enemy.description) texts.push(enemy.description + "\r");
		texts.push("");
		this._drawBookDetail_getTexts(enemy, floorId, texts);
		return [enemy, texts];
	}

}
}