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

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

	this._afterLoadResources = function () {
		// 本函数将在所有资源加载完毕后，游戏开启前被执行
		// 可以在这个函数里面对资源进行一些操作。
		// 若需要进行切分图片，可以使用 core.splitImage() 函数，或直接在全塔属性-图片切分中操作
		// 
		core.registerReplayAction("skilltree", (action) => {
			if (action.indexOf("skilltree:") !== 0) return false;
			var pos = action.substr(10);
			var level = core.getFlag("skill_level", level);
			core.plugin.get_removal()[pos][level[pos]]();
			level[pos] += 1;
			core.setFlag("skill_level", level);
			core.status.route.push(action);
			core.replay();
			return true;
		});
		core.ui.statusBar.init();
	}

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

	// 在不修改libs的情况下将页面适配为11x11
	core._WIDTH_ = 11;
	core._HEIGHT_ = 11;
	core.__SIZE__ = 11;
	core.__PIXELS__ = core.__SIZE__ * 32;
	core.__HALF_SIZE__ = 5;
	core._PX_ = core.__PIXELS__;
	core._PY_ = core.__PIXELS__;


	// 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';
	var outerBackground = document.createElement('canvas');
	outerBackground.style.position = 'absolute';
	outerBackground.style.zIndex = 5;
	if (!core.domStyle.isVertical) { //横屏
		outerBackground.width = 1920;
		outerBackground.height = 1266;
	} else { //竖屏
		outerBackground.width = 1200;
		outerBackground.height = 1986;
	}
	outerBackground.id = 'outerBackground';
	main.dom.outerBackground = outerBackground;
	main.dom.startPanel.insertAdjacentElement('afterend', outerBackground);
	var outerUI = document.createElement('canvas');
	outerUI.style.position = 'absolute';
	outerUI.style.zIndex = 165;
	if (!core.domStyle.isVertical) { //横屏
		outerUI.width = 1920;
		outerUI.height = 1266;
	} else { //竖屏
		outerUI.width = 1200;
		outerUI.height = 1986;
	}
	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) { //outerUI点击效果
		try {
			e.preventDefault();
			if (!core.isPlaying()) return false;
			var left = core.dom.gameGroup.offsetLeft + 3;
			var top = core.dom.gameGroup.offsetTop + 3;
			var 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);
		}
	}

	var _resize_gameGroup = function (obj) {
		var 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";

		var startBackground = core.domStyle.isVertical ? (main.styles.startVerticalBackground || main.styles.startBackground) : main.styles.startBackground;
		if (main.dom.startBackground.getAttribute('__src__') != startBackground) {
			main.dom.startBackground.setAttribute('__src__', startBackground);
			main.dom.startBackground.src = startBackground;
		}
	}

	var _resize_canvas = function (obj) {
		main.dom.outerBackground.style.width = obj.totalWidth + 'px';
		main.dom.outerBackground.style.height = obj.totalHeight + 'px';
		main.dom.outerUI.style.width = obj.totalWidth + 'px';
		main.dom.outerUI.style.height = obj.totalHeight + 'px';
		if (core.domStyle.isVertical) core.domStyle.scale *= 0.95;
		var innerSize = (obj.canvasWidth * core.domStyle.scale) + "px";
		for (var i = 0; i < core.dom.gameCanvas.length; ++i)
			core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[i].style.height = innerSize;
		core.dom.gif.style.width = core.dom.gif.style.height = innerSize;
		core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize;
		core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = innerSize;
		core.dom.gameDraw.style.top = obj.borderHeight * core.domStyle.scale + "px";
		core.dom.startButtons.style.fontSize = 1.2 * core.domStyle.scale + "rem"
		core.dom.startButtons.style.width = 1.2 * core.domStyle.scale * 10 + "rem"
		core.dom.levelChooseButtons.style.fontSize = 1.2 * core.domStyle.scale + "rem"
		core.dom.levelChooseButtons.style.width = 1.2 * core.domStyle.scale * 10 + "rem"
		if (core.domStyle.isVertical) core.dom.gameDraw.style.top = obj.borderHeight * core.domStyle.scale * 1.13 + "px";
		core.dom.gameDraw.style.right = (obj.borderWidth + obj.barWidth + 1) * core.domStyle.scale + "px";
		if (core.domStyle.isVertical) core.dom.gameDraw.style.right = (obj.borderWidth + obj.barWidth) * core.domStyle.scale * 1.8 + "px"
		// resize bigmap
		core.bigmap.canvas.forEach(function (cn) {
			var ratio = core.canvas[cn].canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1;
			core.canvas[cn].canvas.style.width = core.canvas[cn].canvas.width / ratio * core.domStyle.scale + "px";
			core.canvas[cn].canvas.style.height = core.canvas[cn].canvas.height / ratio * core.domStyle.scale + "px";
		});
		// resize dynamic canvas
		for (var name in core.dymCanvas) {
			var ctx = core.dymCanvas[name],
				canvas = ctx.canvas;
			var ratio = canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1;
			canvas.style.width = canvas.width / ratio * core.domStyle.scale + "px";
			canvas.style.height = canvas.height / ratio * core.domStyle.scale + "px";
			canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px";
			canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px";
		}
		// resize next
		main.dom.next.style.width = main.dom.next.style.height = 5 * core.domStyle.scale + "px";
		main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px";

	}

	core.control.resize = function () {
		if (main.mode == 'editor') return;
		var clientWidth = main.dom.body.clientWidth,
			clientHeight = main.dom.body.clientHeight;
		var CANVAS_WIDTH = core.__PIXELS__,
			infoBarHeight = 22,
			BAR_WIDTH = Math.round(core.__PIXELS__ * 0.31);
		var BORDER = 3;
		var extendToolbar = core.flags.extendToolbar;
		var horizontalMaxRatio = (clientHeight - 2 * BORDER - (extendToolbar ? BORDER : 0)) / (CANVAS_WIDTH + (extendToolbar ? 38 : 0));


		if (clientWidth - 3 * BORDER - 175 >= CANVAS_WIDTH + BAR_WIDTH || (clientWidth > clientHeight && horizontalMaxRatio < 1)) {
			// 横屏
			core.domStyle.isVertical = false;
			main.dom.outerBackground.width = 1920;
			main.dom.outerBackground.height = 1266;
			main.dom.outerUI.width = 1920;
			main.dom.outerUI.height = 1266;
			var borderWidth = 19,
				barWidth = 125,
				borderHeight = 24;
			horizontalMaxRatio = clientHeight / (CANVAS_WIDTH + infoBarHeight + 2 * borderHeight);
			core.domStyle.availableScale = [];
			[1, 1.25, 1.5, 1.75, 2].forEach(function (v) {
				if (clientWidth >= v * (CANVAS_WIDTH + barWidth * 2 + borderWidth * 2) && horizontalMaxRatio >= v) {
					core.domStyle.availableScale.push(v);


				}
			});
			if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) {
				core.domStyle.scale = Math.min(1, horizontalMaxRatio);
			} else if (core.getLocalStorage('scale') == null && core.domStyle.availableScale.length >= 2) {
				core.domStyle.scale = core.domStyle.availableScale[core.domStyle.availableScale.length - 2];
				core.setLocalStorage('scale', core.domStyle.scale);
			}
		} else {
			// 竖屏
			core.domStyle.isVertical = true;
			main.dom.outerBackground.width = 1200;
			main.dom.outerBackground.height = 1986;
			main.dom.outerUI.width = 1200;
			main.dom.outerUI.height = 1986;
			var borderWidth = 6,
				barWidth = 6,
				borderHeight = 125;
			horizontalMaxRatio = (clientWidth - 2 * BORDER - (extendToolbar ? BORDER : 0)) / (CANVAS_WIDTH + 24);
			core.domStyle.scale = Math.min(1, horizontalMaxRatio);
			core.domStyle.availableScale = [];
		}
		var totalWidth = (CANVAS_WIDTH + 2 * barWidth + 2 * borderWidth) * core.domStyle.scale;
		var totalHeight = (CANVAS_WIDTH + 2 * borderHeight + infoBarHeight) * core.domStyle.scale;
		var obj = {
			clientWidth: clientWidth,
			clientHeight: clientHeight,
			canvasWidth: CANVAS_WIDTH,
			barWidth: barWidth,
			borderWidth: borderWidth,
			borderHeight: borderHeight,
			infoBarHeight: infoBarHeight,
			totalWidth: totalWidth,
			totalHeight: totalHeight,
			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);
		core.updateStatusBar();
	}


	function statusBar() { //道具列表
		this.itemMx = [ //道具栏1
			["book", "wand", "snow", "fly"],
			["knife", "superPotion", "pickaxe", "bomb"],
			["downFly", "centerFly", "upFly", "bigKey"],
			["cross", "coin", "earthquake", "none"],
		];

		this.itemMxp1 = [ //此处是额外的道具栏2
			["none", "none", "none"],
			["CP1", "shenxuan", "jicheng"],
			["jianban", "paoying", "shengfang"],
		];
		this.itemMxp2 = [
			["none", "none", "none"],
			["CP2", "hongcha", "shenqiang"],
			["zb", "jj", "xuejing"],
		];

		this.itemMx2 = [ //默认道具栏2
			["none", "none", "none"],
			["none", "none", "none"],
			["none", "none", "none"],
		];
	}
	statusBar.prototype.init = function () {

		if (!core.domStyle.isVertical) { //横屏背景图
			var bgx = 1920,
				bgy = 1200;
			var bg = core.material.images.images["statusBackground.jpg"];
		} else { //竖屏背景图
			var bgx = 1200,
				bgy = 1920;
			var bg = core.material.images.images["statusBackground3.jpg"];
		}
		var bgctx = document.getElementById("outerBackground").getContext("2d");
		bgctx.drawImage(bg, 0, 0, bgx, bgy);
		bgctx.fillStyle = "#484b6c";
		bgctx.fillRect(0, bgy, bgx, 66);
		core.setTextAlign('outerUI', 'center');


		this.toolbarAction = [ //工具栏列表
			[main.core.openKeyBoard, main.core.openQuickShop, main.core.openToolbox],
			[main.core.save, main.core.load, main.core.openSettings]
		];
		this.replayAction = [ //回放功能列表
			[core.triggerReplay, core.stopReplay, core.rewindReplay],
			[core.speedDownReplay, core.speedUpReplay, core.saveReplay]
		];

	}
	statusBar.prototype.update = function () { //更新状态栏
		if (!core.isPlaying) return;
		statusBar.prototype.init();
		this._update_props();
		this._update_items();
		this._update_equips();
		this._update_keys();
		this._update_infoWindow();
	}
	// 更新属性
	statusBar.prototype._update_props = function (updatedFloorTitle) {
		var exatk = 0;
		var exdef = 0;
		var tc = "#E1E1E1";
		//////额外属性设置的例子
		/*if (core.getFlag('nowWeapon', 0) == "jicheng") //此处exatk代表攻击力上升量，exdef代表防御力上升量，可为负。在此输入你使用技能让玩家攻防转换的值，将自动显示（+40）（-40）之类的提示
				exatk += 40;
			if (core.getFlag('shourenskill', 0) == 1)
				exdef += 50;*/
		if (!core.domStyle.isVertical) { //横屏模式
			core.clearMap("outerUI", 30, 120, 315, 630);
			core.setFont("outerUI", '48px beeB');
			// 设置楼层名
			if (!updatedFloorTitle && core.status.floorId) {
				updatedFloorTitle = core.status.maps[core.status.floorId].title;
			}
			if (updatedFloorTitle) {
				core.fillText("outerUI", updatedFloorTitle, 186, 183, tc);
			}
			core.setFont("outerUI", '48px num');
			var statusList = ['hp', 'atk', 'def', 'money'];
			var curh = 279;
			core.setTextAlign('outerUI', 'right');
			core.fillText("outerUI", core.formatBigNumber(core.getFlag("hp", core.getRealStatus('hp'))), 330, 279, tc);
			if (exatk > 0) {
				core.fillText("outerUI", core.getRealStatus('atk') + exatk, 330, 351, tc);
				core.setFont("outerUI", '26px num');
				core.setTextAlign('outerUI', 'left');
				core.fillText("outerUI", "（+" + exatk + "）", 131, 361, '#66E166');
				core.setFont("outerUI", '48px num');
				core.setTextAlign('outerUI', 'right');
			} else if (exatk < 0) {
				core.fillText("outerUI", core.getRealStatus('atk') + exatk, 330, 351, tc);
				core.setFont("outerUI", '26px num');
				core.setTextAlign('outerUI', 'left');
				core.fillText("outerUI", "（" + exatk + "）", 131, 361, '#E16666');
				core.setFont("outerUI", '48px num');
				core.setTextAlign('outerUI', 'right');
			} else {
				core.fillText("outerUI", core.getRealStatus('atk'), 330, 351, tc);
			}
			if (exdef > 0) {
				core.fillText("outerUI", core.getRealStatus('def') + exdef, 330, 423, tc);
				core.setFont("outerUI", '26px num');
				core.setTextAlign('outerUI', 'left');
				core.fillText("outerUI", "（+" + exdef + "）", 131, 433, '#66E166');
				core.setFont("outerUI", '48px num');
				core.setTextAlign('outerUI', 'right');
			} else if (exdef < 0) {
				core.fillText("outerUI", core.getRealStatus('def') + exdef, 330, 423, tc);
				core.setFont("outerUI", '26px num');
				core.setTextAlign('outerUI', 'left');
				core.fillText("outerUI", "（" + exdef + "）", 131, 433, '#E16666');
				core.setFont("outerUI", '48px num');
				core.setTextAlign('outerUI', 'right');
			} else {
				core.fillText("outerUI", core.getRealStatus('def'), 330, 423, tc);
			}

			core.fillText("outerUI", core.getRealStatus('money'), 330, 495, tc);
			core.setFont("outerUI", '36px beeB');
			//core.fillText("outerUI", "红雾弥漫", 342, 559, tc); //此处为物品栏状态额外显示
			core.setFont("outerUI", '48px beeB');
			//core.fillText("outerUI", (core.enemys.getEnemyInfo("darkKnight").atk + "+" + core.material.enemys.darkKnight.atk), 110, 69, tc);
			core.setTextAlign('outerUI', 'center');
		} else { //竖屏模式
			core.clearMap("outerUI", 30, 30, 340, 365);
			//core.fillRect("outerUI", 30, 30, 340, 365, "#000000");
			core.clearMap("outerUI", 420, 330, 360, 65);
			//core.fillRect("outerUI", 420, 330, 360, 65, "#000000");

			core.setFont("outerUI", '48px beeB');
			// 设置楼层名
			if (!updatedFloorTitle && core.status.floorId) {
				updatedFloorTitle = core.status.maps[core.status.floorId].title;
			}
			if (updatedFloorTitle) {
				core.fillText("outerUI", updatedFloorTitle, 600, 380, tc);
			}
			core.setFont("outerUI", '48px num');
			var statusList = ['hp', 'atk', 'def', 'money'];
			var curh = 130,
				shux = 340;
			core.setTextAlign('outerUI', 'right');
			core.fillText("outerUI", core.formatBigNumber(core.getFlag("hp", core.getRealStatus('hp'))), shux, curh, tc);
			if (exatk > 0) {
				core.fillText("outerUI", core.getRealStatus('atk') + exatk, shux, curh + 72, tc);
				core.setFont("outerUI", '26px num');
				core.setTextAlign('outerUI', 'left');
				core.fillText("outerUI", "(+" + exatk + ")", 131, curh + 66, '#66E166');
				core.setFont("outerUI", '48px num');
				core.setTextAlign('outerUI', 'right');
			} else if (exatk < 0) {
				core.fillText("outerUI", core.getRealStatus('atk') + exatk, shux, curh + 72, tc);
				core.setFont("outerUI", '26px num');
				core.setTextAlign('outerUI', 'left');
				core.fillText("outerUI", "(" + exatk + ")", 131, curh + 66, '#E16666');
				core.setFont("outerUI", '48px num');
				core.setTextAlign('outerUI', 'right');
			} else {
				core.fillText("outerUI", core.getRealStatus('atk'), shux, curh + 72, tc);
			}
			if (exdef > 0) {
				core.fillText("outerUI", core.getRealStatus('def') + exdef, shux, curh + 144, tc);
				core.setFont("outerUI", '26px num');
				core.setTextAlign('outerUI', 'left');
				core.fillText("outerUI", "(+" + exdef + ")", 131, curh + 138, '#66E166');
				core.setFont("outerUI", '48px num');
				core.setTextAlign('outerUI', 'right');
			} else if (exdef < 0) {
				core.fillText("outerUI", core.getRealStatus('def') + exdef, shux, curh + 144, tc);
				core.setFont("outerUI", '26px num');
				core.setTextAlign('outerUI', 'left');
				core.fillText("outerUI", "(" + exdef + ")", 131, curh + 138, '#E16666');
				core.setFont("outerUI", '48px num');
				core.setTextAlign('outerUI', 'right');
			} else {
				core.fillText("outerUI", core.getRealStatus('def'), shux, curh + 144, tc);
			}

			core.fillText("outerUI", core.getRealStatus('money'), shux, curh + 216, tc);
			core.setFont("outerUI", '36px beeB');
			core.setTextAlign('outerUI', 'center');
			core.clearMap(500, 1520, 230, 60);
			//core.fillRect("outerUI", 500, 1520, 230, 60, "#000");
			//core.fillText("outerUI", "红雾弥漫", 610, 1560, tc);//此处为物品栏状态额外显示
			core.setTextAlign('outerUI', 'right');
			core.setFont("outerUI", '48px beeB');
			//core.fillText("outerUI", (core.enemys.getEnemyInfo("darkKnight").atk + "+" + core.material.enemys.darkKnight.atk), 110, 69, tc);
			core.setTextAlign('outerUI', 'center');
		}
	}
	//更新道具栏
	statusBar.prototype._update_items = function () {
		let P2 = core.getFlag('2P', 0);
		let sp = function (atk) {
			return Math.round(10 * atk + 5 * core.getRealStatus('def'));
		};
		var skill01 = this.itemMx2;
		switch (core.getFlag('Player', 0)) { //此处判定条件来显示哪个物品栏2，此处修改只会影响图标，使用效果下面的onClick也要改
		case 1:
			skill01 = this.itemMxp1;
			break;
		case 2:
			skill01 = this.itemMxp2;
			break;
		default:
			skill01 = this.itemMx2;
			break;
		}
		if (!core.domStyle.isVertical) { //横屏模式
			core.clearMap("outerUI", 30, 630, 315, 288);
			for (var i = 0; i < this.itemMx.length; i++) {
				for (var j = 0; j < this.itemMx[i].length; j++) {
					var item = this.itemMx[i][j];
					if (core.hasItem(item)) {
						var icon = core.material.icons.items[item],
							image = core.material.images.items;
						var posx = 42 + j * 74.25,
							posy = 573 + i * 72;
						core.drawImage('outerUI', image, 0, 32 * icon, 32, 32, posx, posy, 72, 72);
						if (item == "superPotion") {
							core.fillBoldText('outerUI', sp(core.getRealStatus('atk')), posx + 35, posy + 28, '#FFFFFF', '#000000', "bold 24px cjk", 52);
						}
						if (item == "centerFly" || item == "hammer" || item == "pickaxe" || item == "bigKey" || item == "superPotion")
							core.fillBoldText('outerUI', core.itemCount(item), posx + 59.25, posy + 68, '#FFFFFF', '#000000', "bold 36px cjk");
						// if (this.selectId == item)
						//     core.strokeRect('outerUI', posx + 17, posy - 4, 40, 40, '#FFD700');
					}
				}
			}
			core.clearMap("outerUI", 30, 918, 330, 288);

			for (var i = 0; i < skill01.length; i++) {
				for (var j = 0; j < skill01[i].length; j++) {
					var item2 = skill01[i][j];
					if (core.hasItem(item2)) {
						var icon = core.material.icons.items[item2],
							image = core.material.images.items;
						var posx = 39 + j * 99,
							posy = 858 + i * 96;
						core.drawImage('outerUI', image, 0, 32 * icon, 32, 32, posx, posy, 96, 96);

						if (item2 == "hongcha") //此处显示道具栏2道具数量，此为范例
							core.fillBoldText('outerUI', core.itemCount(item2), posx + 75, posy + 84, '#FFFFFF', '#000000', "bold 36px cjk");
						// if (this.selectId == item)
						//     core.strokeRect('outerUI', posx + 17, posy - 4, 40, 40, '#FFD700');
					}
				}
			}
			var pos1x = 270,
				pos1y = 933;
			switch (core.getFlag('Player', 0)) { //这里是显示当前角色的部分，不需要可以注释掉，默认显示勇者名称。
			case "BEE":
				core.fillText('outerUI', '小蜜蜂', pos1x, pos1y, '#FFFFFF', "36px beeB");
				break;
			case 1:
				core.fillText('outerUI', core.status.hero.name, pos1x, pos1y, '#FFFFFF', "36px beeB");
				break;
			default:
				core.fillText('outerUI', core.status.hero.name, pos1x, pos1y, '#FFFFFF', "36px beeB");
				break;
			}
		} else { //竖屏模式

			core.clearMap("outerUI", 50, 1548, 377, 333);
			//core.fillRect("outerUI", 50, 1548, 377, 333, "#00F");
			for (var i = 0; i < this.itemMx.length; i++) {
				for (var j = 0; j < this.itemMx[i].length; j++) {
					var item = this.itemMx[i][j];
					if (core.hasItem(item)) {
						var icon = core.material.icons.items[item],
							image = core.material.images.items;
						var posx = 62 + j * 82.5,
							posy = 1551 + i * 80;
						core.drawImage('outerUI', image, 0, 32 * icon, 32, 32, posx, posy, 80, 80);
						if (item == "superPotion") {
							core.fillBoldText('outerUI', sp(core.getRealStatus('atk')), posx + 38.75, posy + 31, '#FFFFFF', '#000000', "bold 27px cjk", 52);
						}
						if (item == "centerFly" || item == "hammer" || item == "pickaxe" || item == "bigKey" || item == "superPotion")
							core.fillBoldText('outerUI', core.itemCount(item), posx + 65.83, posy + 75.5, '#FFFFFF', '#000000', "bold 40px cjk");
						// if (this.selectId == item)
						//     core.strokeRect('outerUI', posx + 17, posy - 4, 40, 40, '#FFD700');
					}
				}
			}
			core.clearMap("outerUI", 450, 1576, 340, 300);
			//core.fillRect("outerUI", 450, 1576, 340, 300, "#F00");

			for (var i = 0; i < skill01.length; i++) {
				for (var j = 0; j < skill01[i].length; j++) {
					var item2 = skill01[i][j];
					if (core.hasItem(item2)) {
						var icon = core.material.icons.items[item2],
							image = core.material.images.items;
						var posx = 459 + j * 99,
							posy = 1579 + i * 96;
						core.drawImage('outerUI', image, 0, 32 * icon, 32, 32, posx, posy, 96, 96);

						if (item2 == "hongcha") //此处显示道具栏2道具数量，此为范例
							core.fillBoldText('outerUI', core.itemCount(item2), posx + 75, posy + 84, '#FFFFFF', '#000000', "bold 36px cjk");
						// if (this.selectId == item)
						//     core.strokeRect('outerUI', posx + 17, posy - 4, 40, 40, '#FFD700');
					}
				}
			}
			var pos1x = 690,
				pos1y = 1654;

			switch (core.getFlag('Player', 0)) { //这里是显示当前角色的部分，不需要可以注释掉，默认显示勇者名称。
			case "BEE":
				core.fillText('outerUI', '小蜜蜂', pos1x, pos1y, '#FFFFFF', "36px beeB");
				break;
			case 1:
				core.fillText('outerUI', core.status.hero.name, pos1x, pos1y, '#FFFFFF', "36px beeB");
				break;
			default:
				core.fillText('outerUI', core.status.hero.name, pos1x, pos1y, '#FFFFFF', "36px beeB");
				break;
			}
		}

	}
	//更新装备
	statusBar.prototype._update_equips = function () {
		var drawEquip = function (baseX, baseY, id, color, back) {
			if (!id) core.fillText("outerUI", back, baseX + 150, baseY + 66, color);
			else {
				core.fillText("outerUI", core.material.items[id].name, baseX + 96, baseY + 66, color, '48px cjk', 160);
				var icon = core.material.icons.items[id];
				core.drawImage('outerUI', core.material.images.items, 0, 32 * icon, 32, 32, baseX + 192, baseY, 96, 96);
			}
		}
		if (!core.domStyle.isVertical) { //横屏模式
			core.clearMap("outerUI", 1575, 120, 315, 285);
			core.setFont("outerUI", '48px cjk');
			drawEquip(1575, 144, core.getFlag("nowWeapon"), "#FFCFAE", "无武器");
			drawEquip(1575, 288, core.getFlag("nowShield"), "#D1CEFF", "无防具");
		} else { //竖屏模式
			core.clearMap("outerUI", 860, 45, 300, 315);
			//core.fillRect("outerUI", 860, 45, 300, 315, "#000000");
			core.setFont("outerUI", '48px cjk');
			drawEquip(860, 85, core.getFlag("nowWeapon"), "#FFCFAE", "无武器");
			drawEquip(860, 229, core.getFlag("nowShield"), "#D1CEFF", "无防具");
		}
	}
	//更新钥匙盒
	statusBar.prototype._update_keys = function () {
		var todraw = [],
			keyList = ['yellowKey', 'blueKey', 'redKey', "greenKey"];
		keyList.forEach(function (key, i) {
			todraw[i] = core.itemCount(key);
		});
		var colork = ["#FFD188", "#B6A6FF", "#FFAAAA", "#AAFFBD"];
		var keycount = 0;
		for (var m = 0; m <= 3; m++) {
			keycount += todraw[m];
		}
		if (!core.domStyle.isVertical) { //横屏模式
			core.clearMap("outerUI", 1545, 420, 345, 225);

			//core.fillText("outerUI", keycount, 1584, 550, "#FFFFFF", '48px cjk', 160);

			if (keycount <= 28) {
				var baseX = 1584,
					baseY = 426,
					dn = 3,
					dc = 0;
				while (dn >= 0 && dc < 28) {
					if (todraw[dn]) {
						this.drawKey(keyList[dn], baseX + (dc % 7) * 42, baseY + parseInt(dc / 7) * 51);
						todraw[dn]--, dc++;
					} else dn--;
				}

			} else {
				var baseX = 1584,
					baseY = 426,
					dn = 3,
					dc = 0;
				while (dn >= 0 && dc < 21) {
					if (todraw[dn]) {
						this.drawKey(keyList[dn], baseX + (dc % 7) * 42, baseY + parseInt(dc / 7) * 51);
						todraw[dn]--, dc++;
					} else dn--;
				}
				core.setAlpha("outerUI", 0.3);
				core.fillRoundRect("outerUI", 1568, 572, 314, 60, 16, "#000000");
				core.setAlpha("outerUI", 1);
				if (core.itemCount(keyList[3]) != 0) {
					for (var k1 = 3; k1 >= 0; k1--) {
						//core.setAlpha("outerUI", 0.6);
						this.drawKey(keyList[k1], 1584 + 75 * k1, 576);
						//core.setAlpha("outerUI", 1);
						core.fillBoldText("outerUI", core.itemCount(keyList[k1]), 1638 + 75 * k1, 620, colork[k1], "#000000", '32px num', 160);
					}
				} else {
					for (var k1 = 2; k1 >= 0; k1--) {
						//core.setAlpha("outerUI", 0.6);
						this.drawKey(keyList[k1], 1584 + 100 * k1, 576);
						//core.setAlpha("outerUI", 1);
						core.fillBoldText("outerUI", core.itemCount(keyList[k1]), 1650 + 100 * k1, 620, colork[k1], "#000000", '32px num', 160);
					}
				}
			}
		} else { //竖屏模式
			var basekeyx = 800;
			core.clearMap("outerUI", basekeyx - 9, 1545, 150, 345);
			//core.fillRect("outerUI", basekeyx - 9, 1545, 150, 345, "#EEE");

			//core.fillText("outerUI", keycount, 1584, 550, "#FFFFFF", '48px cjk', 160);

			if (keycount <= -1) {
				var baseX = 426,
					baseY = 1584,
					dn = 3,
					dc = 0;
				while (dn >= 0 && dc < 28) {
					if (todraw[dn]) {
						this.drawKey(keyList[dn], baseX + (dc % 7) * 42, baseY + parseInt(dc / 7) * 51);
						todraw[dn]--, dc++;
					} else dn--;
				}

			} else {
				core.setAlpha("outerUI", 0.3);
				//core.fillRoundRect("outerUI", basekeyx, 1568, 80, 314, 16, "#000000");
				core.setAlpha("outerUI", 1);
				if (core.itemCount(keyList[3]) != 0) {
					for (var k1 = 3; k1 >= 0; k1--) {
						//core.setAlpha("outerUI", 0.6);
						this.drawKey(keyList[k1], basekeyx + 14, 1584 + 75 * k1);
						//core.setAlpha("outerUI", 1);
						core.fillBoldText("outerUI", core.itemCount(keyList[k1]), basekeyx + 85, 1632 + 75 * k1, colork[k1], "#000000", '40px num', 160);
					}
				} else {
					for (var k1 = 2; k1 >= 0; k1--) {
						//core.setAlpha("outerUI", 0.6);
						this.drawKey(keyList[k1], basekeyx + 14, 1598 + 100 * k1);
						//core.setAlpha("outerUI", 1);
						core.fillBoldText("outerUI", core.itemCount(keyList[k1]), basekeyx + 85, 1648 + 100 * k1, colork[k1], "#000000", '40px num', 160);
					}
				}
			}
		}
	}
	//绘制钥匙
	statusBar.prototype.drawKey = function (key, x, y) {

		var sx = 0,
			sy = 0;
		if (key == "blueKey") sx += 26;
		else if (key == "yellowKey") sx += 13;
		else if (key == "greenKey") sx += 39;
		core.drawImage("outerUI", 'maba.png', sx, sy, 12, 18, x, y, 36, 54);
	}
	//清除道具说明
	statusBar.prototype._update_infoWindow = function () {
		if (!core.domStyle.isVertical) { //横屏模式
			core.clearMap("outerUI", 1575, 660, 315, 300);
		} else { //竖屏模式
			core.clearMap("outerUI", 360, 50, 480, 280);
			//core.fillRect("outerUI", 360, 50, 480, 280, "#0F0");
		}
		this.selectedItem = null;
	}
	//绘制道具说明
	statusBar.prototype.showInfo = function (itemId) {
		var icon = core.material.icons.items[itemId];
		if (!core.domStyle.isVertical) { //横屏模式
			core.clearMap("outerUI", 1575, 660, 315, 300);
			core.fillText("outerUI", core.material.items[itemId].name, 1580 + 96, 660 + 75, "#D1CEFF", '48px cjk', 170);
			core.drawImage('outerUI', core.material.images.items, 0, 32 * icon, 32, 32, 1575 + 192, 672, 96, 96);
			core.setFont("outerUI", '48px cjk');
			core.ui.drawTextContent("outerUI", core.material.items[itemId].text, {
				left: 1578,
				top: 768,
				maxWidth: 305,
				maxHeight: 290,
				color: "#D1CEFF",
				font: "cjk",
				fontSize: 27
			});
		} else { //竖屏模式
			core.clearMap("outerUI", 360, 50, 480, 280);
			core.fillText("outerUI", core.material.items[itemId].name, 365 + 179, 50 + 75, "#D1CEFF", '48px cjk', 335);
			core.drawImage('outerUI', core.material.images.items, 0, 32 * icon, 32, 32, 360 + 357, 62, 96, 96);
			core.setFont("outerUI", '48px cjk');
			core.ui.drawTextContent("outerUI", core.material.items[itemId].text, {
				left: 368,
				top: 158,
				maxWidth: 470,
				maxHeight: 290,
				color: "#D1CEFF",
				font: "cjk",
				fontSize: 30
			});
		}
		this.selectedItem = itemId;
	}
	//更新工具栏
	statusBar.prototype._update_toolBox = function () {
		var tools = [];
		if (core.isReplaying()) {
			tools = [
				[core.status.replay.pausing ? "play" : "pause", "stop", "rewind"],
				["speedDown", "speedUp", "save"],
			]
		} else {
			tools = [
				["keyboard", "shop", "itembag"],
				["save", "load", "settings"],
			]
		}
		if (!core.domStyle.isVertical) { //横屏模式
			core.clearMap("outerUI", 1560, 960, 345, 240);
			for (var i = 0; i < tools.length; i++) {
				for (var j = 0; j < tools[i].length; j++) {
					core.drawIcon("outerUI", tools[i][j], 1575 + j * 102, 954 + i * 102, 96, 96);
				}
			}
		} else { //竖屏模式
			core.clearMap("outerUI", 960, 1560, 240, 345);
			for (var i = 0; i < tools.length; i++) {
				for (var j = 0; j < tools[i].length; j++) {
					core.drawIcon("outerUI", tools[i][j], 954 + i * 102, 1575 + j * 102, 96, 96);
				}
			}
		}
	}
	//点击效果
	statusBar.prototype.onclick = function (x, y) {
		if (!core.domStyle.isVertical) { //横屏模式
			if (x <= 112 && x >= 13 && y <= 285 && y >= 190) {
				if (core.isReplaying() || core.status.lockControl || core.isMoving()) return;
				var itemId = this.itemMx[Math.floor((y - 187) / 24)][Math.floor((x - 10) / 24.75)];
				if (!core.hasItem(itemId)) return;
				if (core.material.items[itemId].cls == "constants") {
					switch (itemId) {
					case "book":
						core.openBook();
						break;
					case "fly":
						core.setFlag("平面楼传", true);
						if (core.flags.flyRecordPosition && core.getFlag("平面楼传")) { core.plugin.drawFlyMap() } else { core.useFly(true) }
						break;
					case "wand":
						core.insertAction({
							type: "useItem",
							id: itemId
						});
						break;
					case "snow":
						core.useItem("snow");
						break;
					default:
						this.showInfo(itemId);
					}
				} else if (itemId != this.selectedItem) {
					this.showInfo(itemId);
				} else {
					switch (itemId) {
					case "centerFly":
						core.ui.drawCenterFly();
						break;
					default:
						core.useItem(itemId);
					}
				}
				return;
			}
			if (x <= 112 && x >= 13 && y <= 440 && y >= 285) {
				if (core.isReplaying() || core.status.lockControl || core.isMoving()) return;
				var itemId2 = this.itemMx2[Math.floor((y - 285) / 32)][Math.floor((x - 13) / 33)];
				switch (core.getFlag('Player', 0)) { //这里同样也要判断新的道具栏2
				case 1:
					itemId2 = this.itemMxp1[Math.floor((y - 285) / 32)][Math.floor((x - 13) / 33)];
					break;
				case 2:
					itemId2 = this.itemMxp2[Math.floor((y - 285) / 32)][Math.floor((x - 13) / 33)];
					break;
				default:
					itemId2 = this.itemMx2[Math.floor((y - 285) / 32)][Math.floor((x - 13) / 33)];
					break;
				}

				if (!core.hasItem(itemId2)) return;
				if (itemId2 != this.selectedItem) {
					this.showInfo(itemId2);
				} else {
					switch (itemId2) {
					default:
						core.useItem(itemId2);
					}
				}
				return;
			}
			if (x <= 625 && x >= 525 && y <= 382 && y >= 317) {
				var row = Math.floor((x - 525) / 32),
					col = Math.floor((y - 317) / 32);
				if (core.isReplaying()) {
					this.replayAction[col][row].call(core);
				} else if (core.isPlaying()) {
					this.toolbarAction[col][row].call(core, true);
				}
				return;
			}
		} else { //竖屏模式
			if (x <= 114 && x >= 16 && y <= 616 && y >= 516) {
				if (core.isReplaying() || core.status.lockControl || core.isMoving()) return;
				var itemId = this.itemMx[Math.floor((y - 513) / 27.5)][Math.floor((x - 13) / 27)];
				if (!core.hasItem(itemId)) return;
				if (core.material.items[itemId].cls == "constants") {
					switch (itemId) {
					case "book":
						core.openBook();
						break;
					case "fly":
						core.setFlag("平面楼传", true);
						if (core.flags.flyRecordPosition && core.getFlag("平面楼传")) { core.plugin.drawFlyMap() } else { core.useFly(true) }
						break;
					case "wand":
						core.insertAction({
							type: "useItem",
							id: itemId
						});
						break;
					case "snow":
						core.useItem("snow");
						break;
					default:
						this.showInfo(itemId);
					}
				} else if (itemId != this.selectedItem) {
					this.showInfo(itemId);
				} else {
					switch (itemId) {
					case "centerFly":
						core.ui.drawCenterFly();
						break;
					default:
						core.useItem(itemId);
					}
				}
				return;
			}
			if (x <= 264 && x >= 150 && y <= 625 && y >= 525) {
				if (core.isReplaying() || core.status.lockControl || core.isMoving()) return;
				var itemId2 = this.itemMx2[Math.floor((y - 522) / 33)][Math.floor((x - 147) / 32)];
				switch (core.getFlag('Player', 0)) { //这里同样也要判断新的道具栏2
				case 1:
					itemId2 = this.itemMxp1[Math.floor((y - 522) / 33)][Math.floor((x - 147) / 32)];
					break;
				case 2:
					itemId2 = this.itemMxp2[Math.floor((y - 522) / 33)][Math.floor((x - 147) / 32)];
					break;
				default:
					itemId2 = this.itemMx2[Math.floor((y - 522) / 33)][Math.floor((x - 147) / 32)];
					break;
				}

				if (!core.hasItem(itemId2)) return;
				if (itemId2 != this.selectedItem) {
					this.showInfo(itemId2);
				} else {
					switch (itemId2) {
					default:
						core.useItem(itemId2);
					}
				}
				return;
			}
			if (x <= 382 && x >= 317 && y <= 625 && y >= 525) {
				var row = Math.floor((y - 525) / 32),
					col = Math.floor((x - 317) / 32);
				if (core.isReplaying()) {
					this.replayAction[col][row].call(core);
				} else if (core.isPlaying()) {
					this.toolbarAction[col][row].call(core, true);
				}
				return;
			}
		}
	}
	//绘制文字提示
	statusBar.prototype.print = function (text, cnt) {
		if (!core.domStyle.isVertical) { //横屏模式
			if (!text) {
				setTimeout(function () {
					if (!this.infocnt) core.clearMap("outerUI", 0, 1200, 1920, 66);
				}, 200);
				this.infocnt = 0;
				return;
			}
			core.clearMap("outerUI", 0, 1200, 1920, 66);
			core.setFont("outerUI", '48px cjk');
			core.setTextAlign('outerUI', 'left');
			core.fillText("outerUI", text, 30, 1250, "#E1E1E1");
			core.setTextAlign('outerUI', 'center');
			this.infocnt = cnt || 1;
		} else { //竖屏模式
			if (!text) {
				setTimeout(function () {
					if (!this.infocnt) core.clearMap("outerUI", 0, 1920, 1200, 66);
				}, 200);
				this.infocnt = 0;
				return;
			}
			core.clearMap("outerUI", 0, 1920, 1200, 66);
			core.setFont("outerUI", '48px cjk');
			core.setTextAlign('outerUI', 'left');
			core.fillText("outerUI", text, 30, 1970, "#E1E1E1");
			core.setTextAlign('outerUI', 'center');
			this.infocnt = cnt || 1;
		}
	}
	//提示语
	statusBar.prototype.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: "你看到了一个小偷" },
		{ id: "bee", text: "你看到了一只蜜蜂" },
		{ id: "ali", text: "你看到了一只红色狐狸" },
	]
	//检测周围图块输出提示语
	statusBar.prototype.printEnvironmentInfo = function () {
		if (!this.infocnt) {
			var ids = [];
			for (var i in core.status.thisMap.blocks) {
				var block = core.status.thisMap.blocks[i];
				if (!block.disable && core.nearHero(block.x, block.y)) ids.push(block.event.id);
			}
			for (var i in this.infoRules) {
				if (ids.indexOf(this.infoRules[i].id) >= 0) {
					this.print(this.infoRules[i].text);
					return;
				}
			}
		}
	}
	//清除提示
	statusBar.prototype.clearInfo = function (etype) {
		if (this.infocnt == 1) this.print();
		else if (this.infocnt > 1) this.infocnt--;
	}
	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
	};

	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.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.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.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.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('zone.mp3');
			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.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();
		} 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();
	}

	core.control._drawDamage_draw = function (ctx, onMap) {
		if (!core.hasItem('book')) return;

		core.setFont(ctx, "12px num");
		core.setTextAlign(ctx, 'left');
		core.status.damage.data.forEach(function (one) {
			var px = one.px,
				py = one.py;
			if (onMap && core.bigmap.v2) {
				px -= core.bigmap.posX * 32;
				py -= core.bigmap.posY * 32;
				if (px < -32 * 2 || px > core.__PIXELS__ + 32 || py < -32 || py > core.__PIXELS__ + 32)
					return;
			}
			core.fillBoldText(ctx, one.text, px, py, one.color);
		});

		core.setTextAlign(ctx, 'center');
		core.status.damage.extraData.forEach(function (one) {
			var px = one.px,
				py = one.py;
			if (onMap && core.bigmap.v2) {
				px -= core.bigmap.posX * 32;
				py -= core.bigmap.posY * 32;
				if (px < -32 || px > core.__PIXELS__ + 32 || py < -32 || py > core.__PIXELS__ + 32)
					return;
			}
			var alpha = core.setAlpha(ctx, one.alpha);
			core.fillBoldText(ctx, one.text, px, py, one.color);
			core.setAlpha(ctx, alpha);
		});
	}
	core.maps._initDetachedBlock = function (blockInfo, x, y, displayDamage) {
		var headCanvas = null,
			bodyCanvas = '__body_' + x + "_" + y,
			damageCanvas = null;
		// head
		if (!blockInfo.bigImage && blockInfo.height > 32) {
			headCanvas = "__head_" + x + "_" + y;
			core.createCanvas(headCanvas, 0, 0, 32, blockInfo.height - 32, 55);
		}
		// body
		if (blockInfo.bigImage) {
			var bigImageInfo = this._getBigImageInfo(blockInfo.bigImage, blockInfo.face, blockInfo.posX);
			core.createCanvas(bodyCanvas, 0, 0, bigImageInfo.per_width, bigImageInfo.per_height, 35);
		} else {
			core.createCanvas(bodyCanvas, 0, 0, 32, 32, 35);
		}
		// damage
		var damage = null,
			damageColor = null;
		if (blockInfo.cls.indexOf('enemy') == 0 && core.hasItem('book') && displayDamage) {
			var damageString = core.enemys.getDamageString(blockInfo.id, x, y);
			damage = damageString.damage;
			damageColor = damageString.color;
		}
		if (damage != null) {
			damageCanvas = "__damage_" + x + "_" + y;
			var ctx = core.createCanvas(damageCanvas, 0, 0, 32, 32, 65);
			ctx.textAlign = 'left';
			ctx.font = "12px num";
			core.fillBoldText(ctx, damage, 1, 31, damageColor);
			if (core.flags.displayCritical) {
				var critical = core.enemys.nextCriticals(blockInfo.id);
				if (critical.length > 0) critical = critical[0];
				critical = core.formatBigNumber(critical[0], true);
				if (critical == '???') critical = '?';
				core.fillBoldText(ctx, critical, 1, 21, '#FFFFFF');
			}
		}
		return {
			"headCanvas": headCanvas,
			"bodyCanvas": bodyCanvas,
			"damageCanvas": damageCanvas
		}
	}
},
    "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 () {
	/* 宝石血瓶左下角显示数值
 * 注意！！！不要在道具属性中直接操作flags，使用core.status.hero.flags或core.setFlag系列函数代替！
 * 需要将 变量：itemDetail改为true才可正常运行
 * 请尽量减少勇士的属性数量，否则可能会出现严重卡顿（划掉，现在你放一万个属性也不会卡）
 * 注意：这里的属性必须是core.status.hero里面的，flag无法显示
 * 如果不想显示，可以core.setFlag("itemDetail", false);
 * 然后再core.getItemDetail();
 * 如有bug在大群或造塔群@古祠
 */

// 忽略的道具

const ignore = ['superPotion'];

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

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

    // 没有怪物手册
    if (!core.hasItem('book')) return;
    core.status.damage.posX = core.bigmap.posX;
    core.status.damage.posY = core.bigmap.posY;
    if (!onMap) {
        const width = core.floors[floorId].width,
            height = core.floors[floorId].height;
        // 地图过大的缩略图不绘制显伤
        if (width * height > core.bigmap.threshold) return;
    }
    this._updateDamage_damage(floorId, onMap);
    this._updateDamage_extraDamage(floorId, onMap);
    core.getItemDetail(floorId); // 宝石血瓶详细信息
    this.drawDamage(ctx);
};
// 获取宝石信息 并绘制
this.getItemDetail = function (floorId) {
    if (!core.getFlag('itemDetail')) return;
    if (!core.status.thisMap) return;
    floorId = floorId ?? core.status.thisMap.floorId;
    const beforeRatio = core.status.thisMap.ratio;
    core.status.thisMap.ratio = core.status.maps[floorId].ratio;
    let diff = {};
    const before = core.status.hero;
    const hero = core.clone(core.status.hero);
    const handler = {
        set(target, key, v) {
            diff[key] = v - (target[key] || 0);
            if (!diff[key]) diff[key] = void 0;
            return true;
        }
    };
    core.status.hero = new Proxy(hero, handler);
    core.status.maps[floorId].blocks.forEach(function (block) {
        if (
            block.event.cls !== 'items' ||
            ignore.includes(block.event.id) ||
            block.disable
        )
            return;
        const x = block.x,
            y = block.y;
        // v2优化，只绘制范围内的部分
        if (core.bigmap.v2) {
            if (
                x < core.bigmap.posX - core.bigmap.extend ||
                x > core.bigmap.posX + core._WIDTH_ + core.bigmap.extend ||
                y < core.bigmap.posY - core.bigmap.extend ||
                y > core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend
            ) {
                return;
            }
        }
        diff = {};
        const id = block.event.id;
        const item = core.material.items[id];
        if (item.cls === 'equips') {
            // 装备也显示
            const diff = item.equip.value ?? {};
            const per = item.equip.percentage ?? {};
            for (const name in per) {
                diff[name + 'per'] = per[name].toString() + '%';
            }
            drawItemDetail(diff, x, y);
            return;
        }
        // 跟数据统计原理一样 执行效果 前后比较
        core.setFlag('__statistics__', true);
        try {
            eval(item.itemEffect);
        } catch (error) { }
        drawItemDetail(diff, x, y);
    });
    core.status.thisMap.ratio = beforeRatio;
    core.status.hero = before;
    window.hero = before;
    window.flags = before.flags;
};

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

        if (typeof diff[name] === 'number')
            content = core.formatBigNumber(diff[name], true);
        else content = diff[name];
        switch (name) {
            case 'atk':
            case 'atkper':
                color = '#FF7A7A';
                break;
            case 'def':
            case 'defper':
                color = '#00E6F1';
                break;
            case 'mdef':
            case 'mdefper':
                color = '#6EFF83';
                break;
            case 'hp':
                color = '#A4FF00';
                break;
            case 'hpmax':
            case 'hpmaxper':
                color = '#F9FF00';
                break;
            case 'mana':
                color = '#c66';
                break;
        }
        // 绘制
        core.status.damage.data.push({
            text: content,
            px: px,
            py: py - 10 * i,
            color: color
        });
        i++;
    }
}
},
    "手册区分特殊属性怪物": function () {
	// 在此增加新插件
	enemys.prototype.getCurrentEnemys = function (floorId) {
		floorId = floorId || core.status.floorId;
		var enemys = [],
			used = {};
		core.extractBlocks(floorId);
		core.status.maps[floorId].blocks.forEach(function (block) {
			if (!block.disable && block.event.cls.indexOf('enemy') == 0) {
				this._getCurrentEnemys_addEnemy(block.event.id, enemys, used, block.x, block.y, floorId);
			}
		}, this);
		return this._getCurrentEnemys_sort(enemys);
	}

	enemys.prototype._getCurrentEnemys_getEnemy = function (enemyId) {
		var enemy = core.material.enemys[enemyId];
		if (!enemy) return null;

		// 检查朝向；displayIdInBook
		return core.material.enemys[enemy.displayIdInBook] || core.material.enemys[(enemy.faceIds || {}).down] || enemy;
	}

	enemys.prototype._getCurrentEnemys_addEnemy = function (enemyId, enemys, used, x, y, floorId) {
		var enemy = this._getCurrentEnemys_getEnemy(enemyId);
		if (enemy == null) return;

		var id = enemy.id;

		var enemyInfo = this.getEnemyInfo(enemy, null, null, null, floorId);
		var locEnemyInfo = this.getEnemyInfo(enemy, null, x, y, floorId);
		if (!core.flags.enableEnemyPoint || (locEnemyInfo.atk == enemyInfo.atk && locEnemyInfo.def == enemyInfo.def && locEnemyInfo.hp == enemyInfo.hp && JSON.stringify(locEnemyInfo.special) == JSON.stringify(enemyInfo.special))) {
			x = null;
			y = null;
		} else {
			// 检查enemys里面是否使用了存在的内容
			for (var i = 0; i < enemys.length; ++i) {
				var one = enemys[i];
				if (id == one.id && one.locs != null &&
					locEnemyInfo.atk == one.atk && locEnemyInfo.def == one.def && locEnemyInfo.hp == one.hp && JSON.stringify(locEnemyInfo.special) == JSON.stringify(one.special)) {
					one.locs.push([x, y]);
					return;

				}
			}
			enemyInfo = locEnemyInfo;
		}

		var id = enemy.id + ":" + x + ":" + y;
		if (used[id]) return;
		used[id] = true;

		var specialText = core.enemys.getSpecialText(enemy, x, y, floorId);
		var specialColor = core.enemys.getSpecialColor(enemy, x, y, floorId);

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

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

	enemys.prototype._getCurrentEnemys_addEnemy_defDamage = function (enemy, x, y, floorId) {
		var ratio = core.status.maps[floorId || core.status.floorId].ratio || 1;
		return this.getDefDamage(enemy, ratio, x, y, floorId);
	}

	enemys.prototype._getCurrentEnemys_sort = function (enemys) {
		return enemys.sort(function (a, b) {
			if (a.damage == b.damage) {
				return a.money - b.money;
			}
			if (a.damage == null) {
				return 1;
			}
			if (b.damage == null) {
				return -1;
			}
			return a.damage - b.damage;
		});
	}

	////// 获得所有特殊属性的名称 //////
	enemys.prototype.getSpecialText = function (enemy, x, y, floorId) {
		if (typeof enemy == 'string') enemy = this.getEnemyInfo(enemy, null, x, y, floorId);
		if (!enemy) return [];
		var special = enemy.special;
		var text = [];

		var specials = this.getSpecials();
		if (specials) {
			for (var i = 0; i < specials.length; i++) {
				if (this.hasSpecial(special, specials[i][0]))
					text.push(this._calSpecialContent(enemy, specials[i][1]));
			}
		}
		return text;
	}

	////// 获得所有特殊属性的颜色 //////
	enemys.prototype.getSpecialColor = function (enemy, x, y, floorId) {
		if (typeof enemy == 'string') enemy = this.getEnemyInfo(enemy, null, x, y, floorId);
		if (!enemy) return [];
		var special = enemy.special;
		var colors = [];

		var specials = this.getSpecials();
		if (specials) {
			for (var i = 0; i < specials.length; i++) {
				if (this.hasSpecial(special, specials[i][0]))
					colors.push(specials[i][3] || null);
			}
		}
		return colors;

	}

	////// 获得所有特殊属性的额外标记 //////
	enemys.prototype.getSpecialFlag = function (enemy, x, y, floorId) {
		if (typeof enemy == 'string') enemy = getEnemyInfo(enemy, null, x, y, floorId);
		if (!enemy) return [];
		var special = enemy.special;
		var flag = 0;

		var specials = this.getSpecials();
		if (specials) {
			for (var i = 0; i < specials.length; i++) {
				if (this.hasSpecial(special, specials[i][0]))
					flag |= (specials[i][4] || 0);
			}
		}
		return flag;
	}

	////// 获得每个特殊属性的说明 //////
	enemys.prototype.getSpecialHint = function (enemy, special) {
		var specials = this.getSpecials();
		if (special == null) {
			if (specials == null) return [];
			var hints = [];
			for (var i = 0; i < specials.length; i++) {
				if (this.hasSpecial(enemy, specials[i][0]))
					hints.push("\r[" + core.arrayToRGBA(specials[i][3] || "#FF6A6A") + "]\\d" + this._calSpecialContent(enemy, specials[i][1]) +
						"：\\d\r[]" + this._calSpecialContent(enemy, specials[i][2]));
			}
			return hints;
		}

		if (specials == null) return "";
		for (var i = 0; i < specials.length; i++) {
			if (special == specials[i][0])
				return "\r[#FF6A6A]\\d" + this._calSpecialContent(enemy, specials[i][1]) + "：\\d\r[]" + this._calSpecialContent(enemy, specials[i][2]);
		}
		return "";
	}
	ui.prototype._drawBook_drawName = function (index, enemy, top, left, width) {
		// 绘制第零列（名称和特殊属性）
		// 如果需要添加自己的比如怪物的称号等，也可以在这里绘制
		core.setTextAlign('ui', 'center');
		if (core.enemys.getSpecialText(enemy).length == 0) {
			core.fillText('ui', enemy.name, left + width / 2,
				top + 35, '#DDDDDD', this._buildFont(17, true), width);
		} else {
			core.fillText('ui', enemy.name, left + width / 2,
				top + 28, '#DDDDDD', this._buildFont(17, true), width);
			switch (core.enemys.getSpecialText(enemy).length) {
			case 1:
				core.fillText('ui', core.enemys.getSpecialText(enemy)[0], left + width / 2,
					top + 50, core.arrayToRGBA((core.enemys.getSpecialColor(enemy) || [])[0] || '#FF6A6A'),
					this._buildFont(15, true), width);
				break;
			case 2:
				// Step 1: 计算字体
				var text = core.enemys.getSpecialText(enemy)[0] + "  " + core.enemys.getSpecialText(enemy)[1];
				core.setFontForMaxWidth('ui', text, width, this._buildFont(15, true));
				// Step 2: 计算总宽度
				var totalWidth = core.calWidth('ui', text);
				var leftWidth = core.calWidth('ui', core.enemys.getSpecialText(enemy)[0]);
				var rightWidth = core.calWidth('ui', core.enemys.getSpecialText(enemy)[1]);
				// Step 3: 绘制
				core.fillText('ui', core.enemys.getSpecialText(enemy)[0], left + (width + leftWidth - totalWidth) / 2,
					top + 50, core.arrayToRGBA((core.enemys.getSpecialColor(enemy) || [])[0] || '#FF6A6A'));
				core.fillText('ui', core.enemys.getSpecialText(enemy)[1], left + (width + totalWidth - rightWidth) / 2,
					top + 50, core.arrayToRGBA((core.enemys.getSpecialColor(enemy) || [])[1] || '#FF6A6A'));
				break;
			default:
				core.fillText('ui', '多属性...', left + width / 2,
					top + 50, '#FF6A6A', this._buildFont(15, true), width);
			}
		}
	}
	ui.prototype._drawBookDetail_getInfo = function (index) {
		var floorId = core.floorIds[(core.status.event.ui || {}).index] || core.status.floorId;
		// 清除浏览地图时的光环缓存
		if (floorId != core.status.floorId && core.status.checkBlock) {
			core.status.checkBlock.cache = {};
		}
		var enemys = core.enemys.getCurrentEnemys(floorId);
		console.log(123);
		if (enemys.length == 0) return [];
		index = core.clamp(index, 0, enemys.length - 1);
		var enemy = enemys[index];
		var texts = core.enemys.getSpecialHint(enemy);
		if (texts.length == 0) texts.push("该怪物无特殊属性。");
		if (enemy.description) texts.push(enemy.description + "\r");
		this._drawBookDetail_getTexts(enemy, floorId, texts);
		texts.push("");
		return [enemy, texts];
	}

},
    "夹击激光动画": function () {

	// 在此增加新插件
	function createCanvas(name, zIndex) {
		if (!name) return;
		var canvas = document.createElement('canvas');
		canvas.id = name;
		canvas.className = 'gameCanvas';
		// 将图层插入进游戏内容
		document.getElementById('gameDraw').appendChild(canvas);
		canvas.style.zIndex = zIndex || 0;
		var ctx = canvas.getContext('2d');
		core.canvas[name] = ctx;
		canvas.width = core.__PIXELS__;
		canvas.height = core.__PIXELS__;
		return canvas;
	}

	var bg3Canvas = createCanvas('bg3', 25);
	if (main.mode == "editor") { // 与编辑器显伤的神秘联动（（（
		editor.dom.mapEdit.insertBefore(bg3Canvas, core.canvas.event.canvas);
		bg3Canvas.style.zIndex = null;
	}
	core.bigmap.canvas = ['bg', 'bg3', 'event', 'event2', 'fg', 'damage'];

	core.plugin._betCanvas = "bg3";
	this._drawBetweenAttack = function (x, y, pos, frame) {
		var ctx = core.plugin._betCanvas;
		var w = 32,
			h = 68,
			ix = (x - 1) * 32,
			iy = (y - 1) * 32;
		if (main.mode == "editor") {
			ix = x * 32;
			iy = y * 32;
		}
		// 左右夹击
		if (pos[0]) core.drawImage(ctx, "light.png", 32 * (frame - 1), 0, 32, 68, ix, iy - 18, w, h, 90 * Math.PI / 180);
		// 上下夹击
		if (pos[1]) core.drawImage(ctx, "light.png", 32 * (frame - 1), 0, 32, 68, ix, iy - 18, w, h);
	}

	core.registerAnimationFrame("betweenAttack", true, function (timestamp) {
		if (!flags.betweenAttackData) {
			core.clearMap(core.plugin._betCanvas, 0, 0, core.__PIXELS__, core.__PIXELS__);
			return;
		}
		var time = core.events._timestamp;
		if (time && timestamp - time < 400) return;

		core.clearMap(core.plugin._betCanvas, 0, 0, core.__PIXELS__, core.__PIXELS__);
		core.events._timestamp = timestamp;
		var data = flags.betweenAttackData || {};
		flags._frame = flags._frame || 1;
		var frame = flags._frame;

		for (var loc in data) {
			var l = loc.split(",");
			var x = parseInt(l[0]),
				y = parseInt(l[1]);
			core.plugin._drawBetweenAttack(x, y, data[loc], frame);
		}
		flags._frame = frame + 1;
		if (flags._frame > 4) flags._frame = 1;
	});
	var origin_extraDamage = core.control._updateDamage_extraDamage;
	core.control._updateDamage_extraDamage = function (floorId, onMap) {
		flags.betweenAttackData = null;
		if (!flags.useBetweenLight) return origin_extraDamage.call(core.control, floorId, onMap);
		else {
			core.status.damage.extraData = [];
			if (!core.flags.displayExtraDamage) return;

			var width = core.floors[floorId].width,
				height = core.floors[floorId].height;
			var startX = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posX - core.bigmap.extend) : 0;
			var endX = onMap && core.bigmap.v2 ? Math.min(width, core.bigmap.posX + core.__SIZE__ + core.bigmap.extend + 1) : width;
			var startY = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posY - core.bigmap.extend) : 0;
			var endY = onMap && core.bigmap.v2 ? Math.min(height, core.bigmap.posY + core.__SIZE__ + core.bigmap.extend + 1) : height;
			const find = function (x, y) {
				return core.status.damage.extraData.find(function (data) {
					return data.x == x && data.y == y
				})
			}
			for (var x = startX; x < endX; x++) {
				for (var y = startY; y < endY; y++) {
					var alpha = 1;
					if (core.noPass(x, y, floorId)) {
						if (core.flags.extraDamageType == 2) alpha = 0;
						else if (core.flags.extraDamageType == 1) alpha = 0.6;
					}
					var loc = x + "," + y;
					var damage = core.status.checkBlock.damage[loc] || 0;
					var getEnemy = function (x, y) {
						var id = core.getBlockId(x, y, floorId);
						var e = core.material.enemys[id];
						if (main.mode == "editor") e = core.enemys.enemys[id];
						return e;
					}
					if (damage > 0) { // 该点伤害
						damage = core.formatBigNumber(damage, true);
						var left = false,
							top = false;
						var e_left = getEnemy(x - 1, y),
							e_right = getEnemy(x + 1, y);
						var e_bottom = getEnemy(x, y - 1),
							e_top = getEnemy(x, y + 1);

						if (core.hasSpecial(e_left, 16) && core.hasSpecial(e_right, 16) && e_left.id == e_right.id)
							left = true;
						if (core.hasSpecial(e_bottom, 16) && core.hasSpecial(e_top, 16) && e_bottom.id == e_top.id)
							top = true;
						flags.betweenAttackData = flags.betweenAttackData || {};
						if (flags.betweenAttackData[x + "," + y]) continue;
						var data = [left, top];
						let [px, py] = [32 * x + 16, 32 * (y + 1) - 14];
						if (left || top) {
							px += 15;
							py -= 10;
							flags.betweenAttackData[x + "," + y] = data;
						}
						core.plugin._drawBetweenAttack(x, y, data, 1);

						if (left) {
							if (!find(x - 1, y)) core.status.damage.extraData.push({ x: x, y: y, text: damage, px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha });
						} else if (top) {
							if (!find(x, y - 1)) core.status.damage.extraData.push({ x: x, y: y, text: damage, px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha });
						} else {
							core.status.damage.extraData.push({ text: damage, px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha });
						}
					} else { // 检查捕捉
						if (core.status.checkBlock.ambush[x + "," + y]) {
							core.status.damage.extraData.push({ text: '!', px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha });
						}
					}
				}
			}
		}
	}
},
    "编辑器显伤": function () {
	// 在此增加新插件
	/////// 用户设置 ///////
	// 将__enable置为false将关闭插件
	var __enable = true;
	// 魔防攻速之类的属性可以在这里加 ['atk', 'def', 'mdef']
	var heroStatus = ['atk', 'def', 'mdef', 'hp'];
	// saveHero为true 将会把每次造塔测试时的角色数据存下来 否则会读取初始属性
	// 用不着可以关了 节约缓存空间 (虽然根本没多少 还没一个存档大
	// 也可以手动清理 控制台输入core.removeLocalStorage('editorHero')即可
	var saveHero = true;

	// 下为具体实现 懒得写注释了 大概就是写HTML然后注册交互
	if (!__enable || main.mode != 'editor') return;
	core.plugin.initEditorDamage = false;
	if (heroStatus.length >= 4 && !editor.isMobile) editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + 'px';
	editor.statusRatio = core.getLocalStorage('statusRatio', 1);
	editor.saveHero = saveHero;
	editor._heroStatus = heroStatus;
	editor.dom.mapEdit.appendChild(core.canvas.damage.canvas)
	var HTML = "<input type='button' value='←'/><input type='button' value='↑'/><input type='button' value='↓'/><input type='button' value='→'/><input type='button' id='bigmapBtn' value='大地图'' style='margin-left: '5px'/>";

	//if (heroStatus.length >= 4 && !editor.isMobile) editor.dom.mid2.style.top = 650 + 30 * (heroStatus.length - 3) + 'px';
	heroStatus.forEach(function (status) {
		var id = status + 'set',
			id2 = status + 'add',
			id3 = status + 'rec',
			id4 = status + 'help';
		HTML += "<br/><input type='text' size='15' id='" + id + "'><input type='button' id='" + id2 + "' value = '+'><input type='button' id='" + id3 + "' value = '-'><input type='button' value='?' id = '" + id4 + "'>"
	});
	document.getElementById('viewportButtons').innerHTML = HTML;
	['set', 'add', 'rec', 'help'].forEach(function (e) {
		heroStatus.forEach(function (status) {
			editor.dom[status + e] = document.getElementById(status + e);
		});
	});
	var _hasItem = core.items.hasItem;
	core.items.hasItem = function (itemId) {
		if (itemId == 'book' && main.mode == 'editor') return true;
		return _hasItem.call(core.items, itemId);
	}
	if (main.mode == "editor") {
		var applyList = ["getDamageString", "nextCriticals", "getEnemyInfo", "getEnemyValue"];
		applyList.forEach(function (name) {
			var func = core.enemys[name];
			core.enemys[name] = function () {
				var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
				if (typeof args[0] == "string") args[0] = core.enemys.enemys[args[0]];
				return func.apply(core.enemys, args);
			}
		});
	}

	////// 获得勇士属性 //////
	core.control.getStatus = function (name) {
		if (!core.status.hero) return null;
		if (name == 'x' || name == 'y' || name == 'direction')
			return this.getHeroLoc(name);
		/*if ( main.mode == 'editor' && !core.hasFlag('__statistics__')) {
			return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero[name];
		}*/
		return core.status.hero[name];
	}

	core.control.updateDamage = function (floorId, ctx) {
		floorId = floorId || core.status.floorId;
		if (!floorId || core.status.gameOver) return;
		var onMap = ctx == null;
		if (main.mode == 'editor') {
			ctx = core.canvas.damage;
			core.updateCheckBlock();
			core.clearMap(ctx);
			if (editor.uivalues.bigmap) return;
		}

		// 没有怪物手册
		if (!core.hasItem('book')) return;
		core.status.damage.posX = core.bigmap.posX;
		core.status.damage.posY = core.bigmap.posY;
		if (!onMap) {
			var width = core.floors[floorId].width,
				height = core.floors[floorId].height;
			// 地图过大的缩略图不绘制显伤
			if (width * height > core.bigmap.threshold) return;
		}
		this._updateDamage_damage(floorId, onMap);
		this._updateDamage_extraDamage(floorId, onMap);
		this.drawDamage(ctx);
	}

	core.control.drawDamage = function (ctx) {
		if (core.status.gameOver || !core.status.damage /* || main.mode != 'play'*/ ) return;
		var onMap = false;
		if (ctx == null) {
			ctx = core.canvas.damage;
			core.clearMap('damage');
			onMap = true;
		}

		if (onMap && core.bigmap.v2) {
			// 检查是否需要重算...
			if (Math.abs(core.bigmap.posX - core.status.damage.posX) >= core.bigmap.extend - 1 ||
				Math.abs(core.bigmap.posY - core.status.damage.posY) >= core.bigmap.extend - 1) {
				return this.updateDamage();
			}
		}
		return this._drawDamage_draw(ctx, onMap);
	}

	////// 以x,y的形式返回每个点的事件 //////
	core.maps.getMapBlocksObj = function (floorId, noCache) {
		floorId = floorId || core.status.floorId;
		if (core.status.mapBlockObjs[floorId] && !noCache && main.mode != 'editor')
			return core.status.mapBlockObjs[floorId];

		var obj = {};
		core.extractBlocks(floorId);
		core.status.maps[floorId].blocks.forEach(function (block) {
			obj[block.x + "," + block.y] = block;
		});
		core.status.mapBlockObjs[floorId] = obj
		return obj;
	}

	this.bignum = function (num, defaultValue) {
		if (num == null || num == "") return defaultValue;
		num = num + "";
		var list = {
			'w': 1e4,
			'e': 1e8,
			'z': 1e12,
			'j': 1e16,
			'g': 1e20
		};
		// 浮点数问题
		function checkFloat(num) {
			if (!core.isset(num)) return 0;
			num = num + "";
			var index = num.indexOf(".");
			if (index < 0) return 0;
			else return num.slice(index + 1).length;
		}
		var index = num.search(/w|e|z|j|g/);
		if (index <= 0) {
			num = parseInt(num);
			if (core.isset(num)) return num;
			else {
				alert('不正确的输入');
				return defaultValue;
			}
		}
		for (; index > 0; index = num.search(/w|e|z|j|g/)) {
			var p = num[index],
				q = list[p],
				n = num.slice(0, index),
				m = Math.pow(10, checkFloat(n));
			num = n * m * q / m + num.slice(index + 1);
		}
		return parseInt(num);
	}

	this.updateEditorDamage = function (noSave) {
		core.updateDamage();
		heroStatus.forEach(function (status) {
			editor.dom[status + 'set'].value = core.status.hero[status];
		});
		if (!noSave && editor.saveHero) core.setLocalStorage('editorHero', core.status.hero);
	}

	var _resizeMap = core.maps.resizeMap;
	core.maps.resizeMap = function (floorId) {
		_resizeMap.call(core.maps, floorId);
		if (!core.plugin.initEditorDamage && main.mode == 'editor') {
			core.plugin.initEditorDamage = true;
			var editorHero = core.getLocalStorage('editorHero');
			if (editorHero && saveHero) core.status.hero = editorHero;
			else core.removeLocalStorage('editorHero');
			editor._heroStatus.forEach(function (e) {
				editor.dom[e + 'set'].onchange = function () {
					var status = this.id.slice(0, -3);
					core.status.hero[status] = core.bignum(this.value, core.status.hero[status]);
					core.updateEditorDamage();
				}
				editor.dom[e + 'add'].onclick = function () {
					var status = this.id.slice(0, -3);
					core.status.hero[status] += editor.statusRatio;
					core.updateEditorDamage();
				}
				editor.dom[e + 'rec'].onclick = function () {
					var status = this.id.slice(0, -3);
					core.status.hero[status] -= editor.statusRatio;
					core.updateEditorDamage();
				}
				editor.dom[e + 'help'].onclick = function () {
					var status = this.id.slice(0, -4),
						name = core.getStatusLabel(status);
					var ratio = parseInt(prompt("当前属性:" + name + "\n现在的点击按钮变化值:" + editor.statusRatio + ",请输入按下一次+/-按钮的属性变化量,可以写4w 10.2e这种字母缩写"));
					if (!core.isset(ratio)) {
						printe('不合法的输入');
						return;
					}
					editor.statusRatio = ratio;
					core.setLocalStorage('statusRatio', ratio);
				}
			});
			var _updateMap = editor.updateMap;
			editor.updateMap = function () {
				_updateMap.call(editor);
				core.updateEditorDamage(true);
			}
			editor.mode.onmode = function (mode, callback) {
				if (editor_mode.mode != mode) {
					if (mode === 'save') {
						editor_mode.doActionList(editor_mode.mode, editor_mode.actionList, function () {
							if (callback) callback();
							core.updateEditorDamage();
						});
					}
					if (editor_mode.mode === 'nextChange' && mode) editor_mode.showMode(mode);
					if (mode !== 'save') editor_mode.mode = mode;
					editor_mode.actionList = [];
				}
			}
		}
	}
},
    "热重载": function () {
	// 在此增加新插件
	/* ---------- 功能说明 ---------- *

1. 当 libs/ main.js index.html 中的任意一个文件被更改后，会自动刷新塔的页面
2. 修改楼层文件后自动在塔的页面上显示出来，不需要刷新
3. 修改脚本编辑或插件编写后也能自动更新更改的插件或脚本，但不保证不会出问题（一般都不会有问题的
4. 修改图块属性、怪物属性等后会自动更新
5. 当全塔属性被修改时，会自动刷新塔的页面
6. 样板的 styles.css 被修改后也可以直接显示，不需要刷新
7. 其余内容修改后不会自动更新也不会刷新

/* ---------- 使用方式 ---------- *

1. 前往 https://nodejs.org/en/ 下载node.js的LTS版本（点左边那个绿色按钮）并安装
2. 将该插件复制到插件编写中
3. 在造塔群的群文件-魔塔样板·改中找到server.js，下载并放到塔的根目录（与启动服务同一级）
4. 在该目录下按下shift+鼠标右键（win11只按右键即可），选择在终端打开或在powershell打开
5. 运行node server.js即可

*/

	if (main.mode !== 'play' || main.replayChecking) return;

	/**
	 * 发送请求
	 * @param {string} url
	 * @param {string} type
	 * @param {string} data
	 * @returns {Promise<string>}
	 */
	async function post(url, type, data) {
		const xhr = new XMLHttpRequest();
		xhr.open(type, url);
		xhr.send(data);
		const res = await new Promise(res => {
			xhr.onload = e => {
				if (xhr.status !== 200) {
					console.error(`hot reload: http ${xhr.status}`);
					res('@error');
				} else res('success');
			};
			xhr.onerror = e => {
				res('@error');
				console.error(`hot reload: error on connection`);
			};
		});
		if (res === 'success') return xhr.response;
		else return '@error';
	}

	/**
	 * 热重载css
	 * @param {string} data
	 */
	function reloadCss(data) {
		const all = Array.from(document.getElementsByTagName('link'));
		all.forEach(v => {
			if (v.rel !== 'stylesheet') return;
			if (v.href === `http://127.0.0.1:3000/${data}`) {
				v.remove();
				const link = document.createElement('link');
				link.rel = 'stylesheet';
				link.type = 'text/css';
				link.href = data;
				document.head.appendChild(link);
				console.log(`css hot reload: ${data}`);
			}
		});
	}

	/**
	 * 热重载楼层
	 * @param {string} data
	 */
	async function reloadFloor(data) {
		// 首先重新加载main.floors对应的楼层
		await import(`/project/floors/${data}.js?v=${Date.now()}`);
		// 然后写入core.floors并解析
		core.floors[data] = main.floors[data];
		const floor = core.loadFloor(data);
		if (core.isPlaying()) {
			core.status.maps[data] = floor;
			delete core.status.mapBlockObjs[data];
			core.extractBlocks(data);
			if (data === core.status.floorId) {
				core.drawMap(data);
				core.setWeather(
					core.animateFrame.weather.type,
					core.animateFrame.weather.level
				);
			}
			core.updateStatusBar(true, true);
		}
		console.log(`floor hot reload: ${data}`);
	}

	/**
	 * 热重载脚本编辑及插件编写
	 * @param {string} data
	 */
	async function reloadScript(data) {
		if (data === 'plugins') {
			// 插件编写比较好办
			const before = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1;
			// 这里不能用动态导入，因为动态导入会变成模块，变量就不是全局的了
			const script = document.createElement('script');
			script.src = `/project/plugins.js?v=${Date.now()}`;
			document.body.appendChild(script);
			await new Promise(res => {
				script.onload = () => res('success');
			});
			const after = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1;
			// 找到差异的函数
			for (const id in before) {
				const fn = before[id];
				if (typeof fn !== 'function') continue;
				if (fn.toString() !== after[id]?.toString()) {
					try {
						core.plugin[id] = after[id];
						core.plugin[id].call(core.plugin);
						core.updateStatusBar(true, true);
						console.log(`plugin hot reload: ${id}`);
					} catch (e) {
						console.error(e);
					}
				}
			}
		} else if (data === 'functions') {
			// 脚本编辑略微麻烦点
			const before = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a;
			// 这里不能用动态导入，因为动态导入会变成模块，变量就不是全局的了
			const script = document.createElement('script');
			script.src = `/project/functions.js?v=${Date.now()}`;
			document.body.appendChild(script);
			await new Promise(res => {
				script.onload = () => res('success');
			});
			const after = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a;
			// 找到差异的函数
			for (const mod in before) {
				const fns = before[mod];
				for (const id in fns) {
					const fn = fns[id];
					if (typeof fn !== 'function' || id === 'hasSpecial')
						continue;
					const now = after[mod][id];
					if (fn.toString() !== now.toString()) {
						try {
							if (mod === 'events') {
								core.events.eventdata[id] = now;
							} else if (mod === 'enemys') {
								core.enemys.enemydata[id] = now;
							} else if (mod === 'actions') {
								core.actions.actionsdata[id] = now;
							} else if (mod === 'control') {
								core.control.controldata[id] = now;
							} else if (mod === 'ui') {
								core.ui.uidata[id] = now;
							}
							core.updateStatusBar(true, true);
							console.log(
								`function hot reload: ${mod}.${id}`
							);
						} catch (e) {
							console.error(e);
						}
					}
				}
			}
		}
	}

	/**
	 * 属性热重载，包括全塔属性等
	 * @param {string} data
	 */
	async function reloadData(data) {
		const script = document.createElement('script');
		script.src = `/project/${data}.js?v=${Date.now()}`;
		document.body.appendChild(script);
		await new Promise(res => {
			script.onload = () => res('success');
		});

		let after;
		if (data === 'data')
			after = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
		if (data === 'enemys')
			after = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80;
		if (data === 'icons')
			after = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1;
		if (data === 'items')
			after = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a;
		if (data === 'maps')
			after = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
		if (data === 'events')
			after = events_c12a15a8_c380_4b28_8144_256cba95f760;

		if (data === 'enemys') {
			core.enemys.enemys = after;
			for (var enemyId in after) {
				core.enemys.enemys[enemyId].id = enemyId;
			}
			core.material.enemys = core.getEnemys();
		} else if (data === 'icons') {
			core.icons.icons = after;
			core.material.icons = core.getIcons();
		} else if (data === 'items') {
			core.items.items = after;
			for (var itemId in after) {
				core.items.items[itemId].id = itemId;
			}
			core.material.items = core.getItems();
		} else if (data === 'maps') {
			core.maps.blocksInfo = after;
			core.status.mapBlockObjs = {};
			core.status.number2block = {};
			Object.values(core.status.maps).forEach(v => delete v.blocks);
			core.extractBlocks();
			core.setWeather(
				core.animateFrame.weather.type,
				core.animateFrame.weather.level
			);
			core.drawMap();
		} else if (data === 'events') {
			core.events.commonEvent = after.commonEvent;
		} else if (data === 'data') {
			location.reload();
		}
		core.updateStatusBar(true, true);
		console.log(`data hot reload: ${data}`);
	}

	// 初始化
	(async function () {
		const data = await post('/reload', 'POST', 'test');
		if (data === '@error') {
			console.log(`未检测到node服务，热重载插件将无法使用`);
		} else {
			console.log(`热重载插件加载成功`);
			// reload
			setInterval(async () => {
				const res = await post('/reload', 'POST');
				if (res === '@error') return;
				if (res === 'true') location.reload();
				else return;
			}, 1000);

			// hot reload
			setInterval(async () => {
				const res = await post('/hotReload', 'POST');
				const data = res.split('@@');
				data.forEach(v => {
					if (v === '') return;
					const [type, file] = v.split(':');
					if (type === 'css') reloadCss(file);
					if (type === 'data') reloadData(file);
					if (type === 'floor') reloadFloor(file);
					if (type === 'script') reloadScript(file);
				});
			}, 1000);
		}
	})();
},
    "实用功能函数集": function () {
	// 在此增加新插件
	// 功能函数集，具体有哪些函数看每个函数前的注释即可
	// 安装方式：直接复制到插件里面，注意新建插件自带的 function () { } 不能删
	// 使用方式：可以直接使用对象解构按需引入
	// 例如：const { has, slide } = core.plugin.utils;
	// slide([1, 2, 3], -1); // [2, 3, 1]

	/**
	 * 滑动数组，使数组元素平移若干项
	 * @example slide([1, 2, 3], -1); // [2, 3, 1]
	 * @example slide([1, 3, 5], 10); // [5, 3, 1];
	 * @param {any[]} arr 需要滑动的数组
	 * @param {number} delta 滑动的项数，正负均可
	 */
	function slide(arr, delta) {
		if (delta === 0) return arr;
		delta %= arr.length;
		if (delta > 0) {
			arr.unshift(...arr.splice(arr.length - delta, delta));
			return arr;
		}
		if (delta < 0) {
			arr.push(...arr.splice(0, -delta));
			return arr;
		}
	};

	/**
	 * 获取一个方向的反方向
	 * @example backDir('up'); // 'down'
	 * @example backDir('leftup'); // 'rightdown'
	 * @param {string} dir 方向
	 */
	function backDir(dir) {
		const map = {
			up: 'down',
			down: 'up',
			left: 'right',
			right: 'left',
			leftup: 'rightdown',
			leftdown: 'rightup',
			rightdown: 'leftup',
			rightup: 'leftdown'
		};
		if (!dir in map) {
			throw new TypeError(`Wrong dir is delivered when getting back direction.`);
		}
		return map[dir];
	};

	/**
	 * 判断一个值是否不是undefined和null
	 * @example has(0); // true
	 * @example has(false); // true
	 * @example has(NaN); // true
	 * @example has(null); // false
	 * @param {any} v 要判断的值
	 */
	function has(v) {
		return v !== null && v !== void 0;
	};

	/**
	 * 解析css字符串为CSSStyleDeclaration对象
	 * @example 
	 *     parseCss('background-color: cyan; cursor: pointer; user-select: none'); 
	 *     // 输出 { backgroundColor: 'cyan', cursor: 'pointer', userSelect: 'none' }
	 * @param {string} css 要解析的css字符串
	 */
	function parseCss(css) {
		const str = css.replace(/[\n\s\t]*/g, '').replace(/;*/g, ';');
		const styles = str.split(';');
		const res = {};

		for (const one of styles) {
			const [key, data] = one.split(':');
			const cssKey = key.replace(/\-([a-z])/g, (str, $1) => $1.toUpperCase());
			res[cssKey] = data;
		}
		return res;
	}

	/**
	 * 等待一段时间，需在async function中使用，否则报错
	 * @example await sleep(500); // 等待500毫秒
	 * @param {number} time 等待的毫秒数
	 */
	async function sleep(time) {
		return new Promise(res => setTimeout(res, time));
	}

	/**
	 * 在下一帧的下一帧执行一个函数
	 * @example nextFrame(() => console.log(1)); // 两帧后在控制台输出1
	 * @param cb 执行的函数
	 */
	function nextFrame(cb) {
		requestAnimationFrame(() => {
			requestAnimationFrame(cb);
		});
	}

	/**
	 * 将一个css颜色解析成一个rgba数组
	 * 目前仅支持 #RGB #RGBA #RRGGBB #RRGGBBAA rgb() rgba() hsl() hsla() css自带颜色 这几种的转换
	 * @exmaple parseColor('#fff'); // [255, 255, 255]
	 * @example parseColor('#abcd'); // [170, 187, 204, 0.8666666666666667]
	 * @example parseColor('rgba(170, 230, 13, 0.2)'); // [170, 230, 13, 0.2]
	 * @example parseColor('cyan'); // [0, 255, 255]
	 * @example parseColor('lightcoral'); // [240, 128, 128]
	 * @example parseColor('hsla(0.2, 0.3, 0.4, 0.2)'); // [120, 133, 71, 0.2]
	 * @example parseColor('rgba(20%, 50, 33%, 0.2)'); // [51, 50, 84.15, 0.2]
	 * @param color 要解析的颜色字符串
	 */
	function parseColor(color) {
		if (color.startsWith('rgb')) {
			// rgb
			const match = color.match(/rgba?\([\d\,\s\.%]+\)/);
			if (!has(match)) throw new Error(`Invalid color is delivered!`);
			const l = color.includes('a');
			return match[0]
				.slice(l ? 5 : 4, -1)
				.split(',')
				.map((v, i) => {
					const vv = v.trim();
					if (vv.endsWith('%')) {
						if (i === 3) {
							return parseInt(vv) / 100;
						} else {
							return (parseInt(vv) * 255) / 100;
						}
					} else return parseFloat(vv);
				})
				.slice(0, l ? 4 : 3);
		} else if (color.startsWith('#')) {
			// 十六进制
			const content = color.slice(1);
			if (![3, 4, 6, 8].includes(content.length)) {
				throw new Error(`Invalid color is delivered!`);
			}

			if (content.length <= 4) {
				const res = content
					.split('')
					.map(v => Number(`0x${v}${v}`));
				if (res.length === 4) res[3] /= 255;
				return res;
			} else {
				const res = Array(content.length / 2)
					.fill(1)
					.map((v, i) =>
						Number(`0x${content[i * 2]}${content[i * 2 + 1]}`)
					);
				if (res.length === 4) res[3] /= 255;
				return res;
			}
		} else if (color.startsWith('hsl')) {
			// hsl，转成rgb后输出
			const match = color.match(/hsla?\([\d\,\s\.%]+\)/);
			if (!has(match)) throw new Error(`Invalid color is delivered!`);
			const l = color.includes('a');
			const hsl = match[0]
				.slice(l ? 5 : 4, -1)
				.split(',')
				.map(v => {
					const vv = v.trim();
					if (vv.endsWith('%')) return parseInt(vv) / 100;
					else return parseFloat(vv);
				});
			const rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
			return (l ? rgb.concat([hsl[3]]) : rgb);
		} else {
			// 单词
			const rgb = cssColors[color];
			if (!has(rgb)) {
				throw new Error(`Invalid color is delivered!`);
			}
			return parseColor(rgb);
		}
	}

	/**
	 * hsl转rgb
	 * @param h 色相
	 * @param s 饱和度
	 * @param l 亮度
	 */
	function hslToRgb(h, s, l) {
		if (s == 0) {
			return [0, 0, 0];
		} else {
			const hue2rgb = (p, q, t) => {
				if (t < 0) t += 1;
				if (t > 1) t -= 1;
				if (t < 1 / 6) return p + (q - p) * 6 * t;
				if (t < 1 / 2) return q;
				if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
				return p;
			};

			const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
			const p = 2 * l - q;
			const r = hue2rgb(p, q, h + 1 / 3);
			const g = hue2rgb(p, q, h);
			const b = hue2rgb(p, q, h - 1 / 3);
			return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
		}
	}

	/**
	 * 确保一个变量是一个数组，不是的话转为数组并返回，是的话直接返回传入的数组
	 * @param arr 要判断的变量
	 * @example ensureArray(1); // [1]
	 * @example ensureArray([1, 2]); // [1, 2]
	 * @example ensureArray('test'); // ['test']
	 */
	function ensureArray(arr) {
		// @ts-ignore
		return arr instanceof Array ? arr : [arr];
	}

	/**
	 * 返回一个坐标在某个方向上移动 d 格后的坐标
	 * @param d 移动多少格，默认为1
	 * @example ofDir(7, 7, 'left'); // [6, 7]
	 * @example ofDir(10, 8, 'leftup', 5); // [5, 3]
	 */
	function ofDir(x, y, dir, d = 1) {
		const { x: dx, y: dy } = core.utils.scan2[dir];
		return [x + dx * d, y + dy * d];
	}

	const cssColors = {
		black: '#000000',
		silver: '#c0c0c0',
		gray: '#808080',
		white: '#ffffff',
		maroon: '#800000',
		red: '#ff0000',
		purple: '#800080',
		fuchsia: '#ff00ff',
		green: '#008000',
		lime: '#00ff00',
		olive: '#808000',
		yellow: '#ffff00',
		navy: '#000080',
		blue: '#0000ff',
		teal: '#008080',
		aqua: '#00ffff',
		orange: '#ffa500',
		aliceblue: '#f0f8ff',
		antiquewhite: '#faebd7',
		aquamarine: '#7fffd4',
		azure: '#f0ffff',
		beige: '#f5f5dc',
		bisque: '#ffe4c4',
		blanchedalmond: '#ffebcd',
		blueviolet: '#8a2be2',
		brown: '#a52a2a',
		burlywood: '#deb887',
		cadetblue: '#5f9ea0',
		chartreuse: '#7fff00',
		chocolate: '#d2691e',
		coral: '#ff7f50',
		cornflowerblue: '#6495ed',
		cornsilk: '#fff8dc',
		crimson: '#dc143c',
		cyan: '#00ffff',
		darkblue: '#00008b',
		darkcyan: '#008b8b',
		darkgoldenrod: '#b8860b',
		darkgray: '#a9a9a9',
		darkgreen: '#006400',
		darkgrey: '#a9a9a9',
		darkkhaki: '#bdb76b',
		darkmagenta: '#8b008b',
		darkolivegreen: '#556b2f',
		darkorange: '#ff8c00',
		darkorchid: '#9932cc',
		darkred: '#8b0000',
		darksalmon: '#e9967a',
		darkseagreen: '#8fbc8f',
		darkslateblue: '#483d8b',
		darkslategray: '#2f4f4f',
		darkslategrey: '#2f4f4f',
		darkturquoise: '#00ced1',
		darkviolet: '#9400d3',
		deeppink: '#ff1493',
		deepskyblue: '#00bfff',
		dimgray: '#696969',
		dimgrey: '#696969',
		dodgerblue: '#1e90ff',
		firebrick: '#b22222',
		floralwhite: '#fffaf0',
		forestgreen: '#228b22',
		gainsboro: '#dcdcdc',
		ghostwhite: '#f8f8ff',
		gold: '#ffd700',
		goldenrod: '#daa520',
		greenyellow: '#adff2f',
		grey: '#808080',
		honeydew: '#f0fff0',
		hotpink: '#ff69b4',
		indianred: '#cd5c5c',
		indigo: '#4b0082',
		ivory: '#fffff0',
		khaki: '#f0e68c',
		lavender: '#e6e6fa',
		lavenderblush: '#fff0f5',
		lawngreen: '#7cfc00',
		lemonchiffon: '#fffacd',
		lightblue: '#add8e6',
		lightcoral: '#f08080',
		lightcyan: '#e0ffff',
		lightgoldenrodyellow: '#fafad2',
		lightgray: '#d3d3d3',
		lightgreen: '#90ee90',
		lightgrey: '#d3d3d3',
		lightpink: '#ffb6c1',
		lightsalmon: '#ffa07a',
		lightseagreen: '#20b2aa',
		lightskyblue: '#87cefa',
		lightslategray: '#778899',
		lightslategrey: '#778899',
		lightsteelblue: '#b0c4de',
		lightyellow: '#ffffe0',
		limegreen: '#32cd32',
		linen: '#faf0e6',
		magenta: '#ff00ff',
		mediumaquamarine: '#66cdaa',
		mediumblue: '#0000cd',
		mediumorchid: '#ba55d3',
		mediumpurple: '#9370db',
		mediumseagreen: '#3cb371',
		mediumslateblue: '#7b68ee',
		mediumspringgreen: '#00fa9a',
		mediumturquoise: '#48d1cc',
		mediumvioletred: '#c71585',
		midnightblue: '#191970',
		mintcream: '#f5fffa',
		mistyrose: '#ffe4e1',
		moccasin: '#ffe4b5',
		navajowhite: '#ffdead',
		oldlace: '#fdf5e6',
		olivedrab: '#6b8e23',
		orangered: '#ff4500',
		orchid: '#da70d6',
		palegoldenrod: '#eee8aa',
		palegreen: '#98fb98',
		paleturquoise: '#afeeee',
		palevioletred: '#db7093',
		papayawhip: '#ffefd5',
		peachpuff: '#ffdab9',
		peru: '#cd853f',
		pink: '#ffc0cb',
		plum: '#dda0dd',
		powderblue: '#b0e0e6',
		rosybrown: '#bc8f8f',
		royalblue: '#4169e1',
		saddlebrown: '#8b4513',
		salmon: '#fa8072',
		sandybrown: '#f4a460',
		seagreen: '#2e8b57',
		seashell: '#fff5ee',
		sienna: '#a0522d',
		skyblue: '#87ceeb',
		slateblue: '#6a5acd',
		slategray: '#708090',
		slategrey: '#708090',
		snow: '#fffafa',
		springgreen: '#00ff7f',
		steelblue: '#4682b4',
		tan: '#d2b48c',
		thistle: '#d8bfd8',
		tomato: '#ff6347',
		turquoise: '#40e0d0',
		violet: '#ee82ee',
		wheat: '#f5deb3',
		whitesmoke: '#f5f5f5',
		yellowgreen: '#9acd32',
		transparent: '#0000'
	};


	if (has(core.plugin.utils)) {
		throw new ReferenceError(`core.plugin上已经有'utils'属性，因此功能函数插件将无法使用！`);
	}
	core.plugin.utils = {
		has,
		slide,
		backDir,
		parseCss,
		sleep,
		nextFrame,
		parseColor,
		hslToRgb,
		ensureArray,
		ofDir
	};
},
    "五图层": function () {
	// 在此增加新插件
	// 是否启用五图层（增加背景2层和前景2层） 将__enable置为true即会启用；启用后请保存后刷新编辑器
	// 背景层2将会覆盖背景层 被事件层覆盖 前景层2将会覆盖前景层
	// 另外 请注意加入两个新图层 会让大地图的性能降低一些
	// 插件作者：ad
	var __enable = ture;
	if (!__enable) return;

	// 创建新图层
	function createCanvas(name, zIndex) {
		if (!name) return;
		var canvas = document.createElement('canvas');
		canvas.id = name;
		canvas.className = 'gameCanvas anti-aliasing';
		// 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高
		if (main.mode != "editor") canvas.style.zIndex = zIndex || 0;
		// 将图层插入进游戏内容
		document.getElementById('gameDraw').appendChild(canvas);
		var ctx = canvas.getContext('2d');
		core.canvas[name] = ctx;
		canvas.width = core._PX_ || core.__PIXELS__;
		canvas.height = core._PY_ || core.__PIXELS__;
		return canvas;
	}

	var bg2Canvas = createCanvas('bg2', 20);
	var fg2Canvas = createCanvas('fg2', 63);
	// 大地图适配
	core.bigmap.canvas = ["bg2", "fg2", "bg", "event", "event2", "fg", "damage"];
	core.initStatus.bg2maps = {};
	core.initStatus.fg2maps = {};

	if (main.mode == 'editor') {
		/*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/
		// 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层)
		// 背景层2(bg2) 插入事件层(event)之前(即bg与event之间)
		document.getElementById('mapEdit').insertBefore(bg2Canvas, document.getElementById('event'));
		// 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后)
		document.getElementById('mapEdit').insertBefore(fg2Canvas, document.getElementById('ebm'));
		// 原本有三个图层 从4开始添加
		var num = 4;
		// 新增图层存入editor.dom中
		editor.dom.bg2c = core.canvas.bg2.canvas;
		editor.dom.bg2Ctx = core.canvas.bg2;
		editor.dom.fg2c = core.canvas.fg2.canvas;
		editor.dom.fg2Ctx = core.canvas.fg2;
		editor.dom.maps.push('bg2map', 'fg2map');
		editor.dom.canvas.push('bg2', 'fg2');

		// 创建编辑器上的按钮
		var createCanvasBtn = function (name) {
			// 电脑端创建按钮
			var input = document.createElement('input');
			// layerMod4/layerMod5
			var id = 'layerMod' + num++;
			// bg2map/fg2map
			var value = name + 'map';
			input.type = 'radio';
			input.name = 'layerMod';
			input.id = id;
			input.value = value;
			editor.dom[id] = input;
			input.onchange = function () {
				editor.uifunctions.setLayerMod(value);
			}
			return input;
		};

		var createCanvasBtn_mobile = function (name) {
			// 手机端往选择列表中添加子选项
			var input = document.createElement('option');
			var id = 'layerMod' + num++;
			var value = name + 'map';
			input.name = 'layerMod';
			input.value = value;
			editor.dom[id] = input;
			return input;
		};
		if (!editor.isMobile) {
			var input = createCanvasBtn('bg2');
			var input2 = createCanvasBtn('fg2');
			// 获取事件层及其父节点
			var child = document.getElementById('layerMod'),
				parent = child.parentNode;
			// 背景层2插入事件层前
			parent.insertBefore(input, child);
			// 不能直接更改背景层2的innerText 所以创建文本节点
			var txt = document.createTextNode('bg2');
			// 插入事件层前(即新插入的背景层2前)
			parent.insertBefore(txt, child);
			// 向最后插入前景层2(即插入前景层后)
			parent.appendChild(input2);
			var txt2 = document.createTextNode('fg2');
			parent.appendChild(txt2);
			parent.childNodes[2].replaceWith("bg");
			parent.childNodes[6].replaceWith("事件");
			parent.childNodes[8].replaceWith("fg");
		} else {
			var input = createCanvasBtn_mobile('bg2');
			var input2 = createCanvasBtn_mobile('fg2');
			// 手机端因为是选项 所以可以直接改innerText
			input.innerText = '背景层2';
			input2.innerText = '前景层2';
			var parent = document.getElementById('layerMod');
			parent.insertBefore(input, parent.children[1]);
			parent.appendChild(input2);
		}
	}

	var _loadFloor_doNotCopy = core.maps._loadFloor_doNotCopy;
	core.maps._loadFloor_doNotCopy = function () {
		return ["bg2map", "fg2map"].concat(_loadFloor_doNotCopy());
	}
	////// 绘制背景和前景层 //////
	core.maps._drawBg_draw = function (floorId, toDrawCtx, cacheCtx, config) {
		config.ctx = cacheCtx;
		core.maps._drawBg_drawBackground(floorId, config);
		// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块；后绘制的覆盖先绘制的。
		core.maps._drawFloorImages(floorId, config.ctx, 'bg', null, null, config.onMap);
		core.maps._drawBgFgMap(floorId, 'bg', config);
		if (config.onMap) {
			core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0);
			core.clearMap('bg2');
			core.clearMap(cacheCtx);
		}
		core.maps._drawBgFgMap(floorId, 'bg2', config);
		if (config.onMap) core.drawImage('bg2', cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0);
		config.ctx = toDrawCtx;
	}
	core.maps._drawFg_draw = function (floorId, toDrawCtx, cacheCtx, config) {
		config.ctx = cacheCtx;
		// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块；后绘制的覆盖先绘制的。
		core.maps._drawFloorImages(floorId, config.ctx, 'fg', null, null, config.onMap);
		core.maps._drawBgFgMap(floorId, 'fg', config);
		if (config.onMap) {
			core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0);
			core.clearMap('fg2');
			core.clearMap(cacheCtx);
		}
		core.maps._drawBgFgMap(floorId, 'fg2', config);
		if (config.onMap) core.drawImage('fg2', cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0);
		config.ctx = toDrawCtx;
	}
	////// 移动判定 //////
	core.maps._generateMovableArray_arrays = function (floorId) {
		return {
			bgArray: this.getBgMapArray(floorId),
			fgArray: this.getFgMapArray(floorId),
			eventArray: this.getMapArray(floorId),
			bg2Array: this._getBgFgMapArray('bg2', floorId),
			fg2Array: this._getBgFgMapArray('fg2', floorId)
		};
	}
},
    "新道具栏/装备栏": function () {
	// 在此增加新插件
	// 注：///// *** 裹起来的区域： 该区域内参数可以随意更改调整ui绘制 不会影响总体布局
	// 请尽量修改该区域而不是其他区域 修改的时候最好可以对照现有ui修改

	///// *** 道具类型
	// cls对应name
	var itemClsName = {
		"constants": "永久道具",
		"tools": "消耗道具",
	}
	// 一页最大放的道具数量 将把整个道具左栏分成num份 每份是一个道具项
	var itemNum = 12;
	///// ***

	// 背景设置
	this.drawBoxBackground = function (ctx) {
		core.setTextAlign(ctx, "left");
		core.clearMap(ctx);
		core.deleteCanvas("_selector");
		var info = core.status.thisUIEventInfo || {};

		///// *** 背景设置
		var max = core.__PIXELS__;
		var x = 2,
			y = x,
			w = max - x * 2,
			h = w;
		var borderWidth = 2,
			borderRadius = 5, // radius:圆角矩形的圆角半径
			borderStyle = "#fff";
		var backgroundColor = "gray";
		// 设置背景不透明度(0.85)
		var backgroundAlpha = 0.85;
		///// ***

		var start_x = x + borderWidth / 2,
			start_y = y + borderWidth / 2,
			width = max - start_x * 2,
			height = max - start_y * 2;

		// 渐变色背景的一个例子(黑色渐变白色)：
		// 有关渐变色的具体知识请网上搜索canvas createGradient了解
		/*
		   var grd = ctx.createLinearGradient(x, y, x + w, y);
		   grd.addColorStop(0, "black");
		   grd.addColorStop(1, "white");
		   backgroundColor = grd;
		*/
		// 使用图片背景要注释掉下面的strokeRect和fillRoundRect
		// 图片背景的一个例子：
		/*
		   core.drawImage(ctx, "xxx.png", x, y, w, h);
		   core.strokeRect(ctx, x, y, w, h, borderStyle, borderWidth);
		*/
		core.setAlpha(ctx, backgroundAlpha);
		core.strokeRoundRect(ctx, x, y, w, h, borderRadius, borderStyle, borderWidth);
		core.fillRoundRect(ctx, start_x, start_y, width, height, borderRadius, backgroundColor);
		core.setAlpha(ctx, 1);

		///// *** 左栏配置
		var leftbar_height = height;
		// 左边栏宽度(width*0.6) 本身仅为坐标使用 需要与底下的rightbar_width(width*0.4)同时更改
		var leftbar_width = width * 0.6;
		///// ***

		// xxx_right参数 代表最右侧坐标
		var leftbar_right = start_x + leftbar_width - borderWidth / 2;
		var leftbar_bottom = start_y + leftbar_height;
		var leftbar_x = start_x;
		var leftbar_y = start_y;

		///// *** 道具栏配置
		var boxName_color = "#fff";
		var boxName_fontSize = 15;
		var boxName_font = core.ui._buildFont(boxName_fontSize, true);
		var arrow_x = 10 + start_x;
		var arrow_y = 10 + start_y;
		var arrow_width = 20;
		var arrow_style = "white";
		// 暂时只能是1 否则不太行 等待新样板(2.7.3)之后对drawArrow做优化
		var arrow_lineWidth = 1;
		// 右箭头
		var rightArrow_right = leftbar_right - 10;
		// 道具内栏顶部坐标 本质是通过该项 控制(道具栏顶部文字和箭头)与道具内栏顶部的间隔
		var itembar_top = arrow_y + 15;
		///// ***

		var itembar_right = rightArrow_right;
		var boxName = core.status.event.id == "toolbox" ? "\r[yellow]道具栏\r | 装备栏" : "道具栏 | \r[yellow]装备栏\r";
		core.drawArrow(ctx, arrow_x + arrow_width, arrow_y, arrow_x, arrow_y, arrow_style, arrow_lineWidth);
		core.drawArrow(ctx, rightArrow_right - arrow_width, arrow_y, rightArrow_right, arrow_y, arrow_style, arrow_lineWidth);
		core.setTextAlign(ctx, "center");
		core.setTextBaseline(ctx, "middle");
		var changeBox = function () {
			var id = core.status.event.id;
			core.closePanel();
			if (id == "toolbox") core.openEquipbox();
			else core.openToolbox();
		}
		core.fillText(ctx, boxName, (leftbar_right + leftbar_x) / 2, arrow_y + 2, boxName_color, boxName_font);

		///// *** 底栏按钮
		var pageBtn_radius = 8;
		// xxx_left 最左侧坐标
		var pageBtn_left = leftbar_x + 3;
		var pageBtn_right = leftbar_right - 3;
		// xxx_bottom 最底部坐标
		var pageBtn_bottom = leftbar_bottom - 2;
		var pageBtn_borderStyle = "#fff";
		var pageBtn_borderWidth = 2;
		var pageText_color = "#fff";
		// 底部按钮与上面的道具内栏的间隔大小
		var bottomSpace = 8;
		///// ***

		core.drawItemListbox_setPageBtn(ctx, pageBtn_left, pageBtn_right, pageBtn_bottom, pageBtn_radius, pageBtn_borderStyle, pageBtn_borderWidth);
		var page = info.page || 1;
		var pageFontSize = pageBtn_radius * 2 - 4;
		var pageFont = core.ui._buildFont(pageFontSize);
		core.setPageItems(page);
		var num = itemNum;
		if (core.status.event.id == "equipbox") num -= 5;
		var maxPage = info.maxPage;
		var pageText = page + " / " + maxPage;
		core.setTextAlign(ctx, "center");
		core.setTextBaseline(ctx, "bottom");
		core.fillText(ctx, pageText, (leftbar_x + leftbar_right) / 2, pageBtn_bottom, pageText_color, pageFont);
		core.addUIEventListener(start_x, start_y, leftbar_right - start_x, arrow_y - start_y + 13, changeBox);
		var itembar_height = Math.ceil(pageBtn_bottom - pageBtn_radius * 2 - pageBtn_borderWidth / 2 - bottomSpace - itembar_top);
		var oneItemHeight = (itembar_height - 4) / itemNum;
		return {
			x: start_x,
			y: start_y,
			width: width,
			height: height,
			leftbar_right: leftbar_right,
			obj: {
				x: arrow_x,
				y: itembar_top,
				width: itembar_right - arrow_x,
				height: itembar_height,
				oneItemHeight: oneItemHeight
			}
		}
	}

	this.drawItemListbox = function (ctx, obj) {
		ctx = ctx || core.canvas.ui;
		var itembar_x = obj.x,
			itembar_y = obj.y,
			itembar_width = obj.width,
			itembar_height = obj.height,
			itemNum = obj.itemNum,
			oneItemHeight = obj.oneItemHeight;
		var itembar_right = itembar_x + itembar_width;
		var info = core.status.thisUIEventInfo || {};
		var obj = {};
		var page = info.page || 1,
			index = info.index,
			select = info.select || {};

		///// *** 道具栏内栏配置
		var itembar_style = "black";
		var itembar_alpha = 0.7;
		// 一个竖屏下减少道具显示的例子:
		// if (core.domStyle.isVertical) itemNum = 10;
		// 每个道具项的上下空隙占总高度的比例
		var itembar_marginHeightRatio = 0.2;
		// 左右间隔空隙
		var item_marginLeft = 2;
		var item_x = itembar_x + 2,
			item_y = itembar_y + 2,
			item_right = itembar_right - 2,
			itemName_color = "#fff";
		// 修改此项以更换闪烁光标
		var item_selector = "winskin.png";
		///// ***

		core.setAlpha(ctx, itembar_alpha);
		core.fillRect(ctx, itembar_x, itembar_y, itembar_width, itembar_height, itembar_style);
		core.setAlpha(ctx, 1);
		var pageItems = core.setPageItems(page);
		var marginHeight = itembar_marginHeightRatio * oneItemHeight;
		core.setTextBaseline(ctx, "middle");
		var originColor = itemName_color;
		for (var i = 0; i < pageItems.length; i++) {
			itemName_color = originColor;
			var item = pageItems[i];
			// 设置某个的字体颜色的一个例子
			// if (item.id == "xxx") itemName_color = "green";
			core.drawItemListbox_drawItem(ctx, item_x, item_right, item_y, oneItemHeight, item_marginLeft, marginHeight, itemName_color, pageItems[i]);
			if (index == i + 1) core.ui._drawWindowSelector(item_selector, item_x + 1, item_y - 1, item_right - item_x - 2, oneItemHeight - 2);
			item_y += oneItemHeight;
		}
	}

	this.drawToolboxRightbar = function (ctx, obj) {
		ctx = ctx || core.canvas.ui;
		var info = core.status.thisUIEventInfo || {};
		var page = info.page || 1,
			index = info.index || 1,
			select = info.select || {};
		var start_x = obj.x,
			start_y = obj.y,
			width = obj.width,
			height = obj.height;
		var toolboxRight = start_x + width,
			toolboxBottom = start_y + height;


		///// *** 侧边栏(rightbar)背景设置(物品介绍)
		var rightbar_width = width * 0.4;
		var rightbar_height = height;
		var rightbar_lineWidth = 2;
		var rightbar_lineStyle = "#fff";
		///// ***

		var rightbar_x = toolboxRight - rightbar_width - rightbar_lineWidth / 2;
		var rightbar_y = start_y;
		core.drawLine(ctx, rightbar_x, rightbar_y, rightbar_x, rightbar_y + rightbar_height, rightbar_lineStyle, rightbar_lineWidth);

		// 获取道具id(有可能为null)
		var itemId = select.id;
		var item = core.material.items[itemId];

		///// *** 侧边栏物品Icon信息
		var iconRect_y = rightbar_y + 10;
		// space：间距
		// 这里布局设定iconRect与侧边栏左边框 itemName与工具栏右边框 itemRect与itemName的间距均为space
		var space = 15;
		var iconRect_x = rightbar_x + space;
		var iconRect_radius = 2,
			iconRect_width = 32,
			iconRect_height = 32,
			iconRect_style = "#fff",
			iconRect_lineWidth = 2;
		///// ***

		var iconRect_bottom = iconRect_y + iconRect_height,
			iconRect_right = iconRect_x + iconRect_width;

		///// *** 侧边栏各项信息
		var itemTextFontSize = 15,
			itemText_x = iconRect_x - 4,
			itemText_y = Math.floor(start_y + rightbar_height * 0.25), // 坐标取整防止模糊
			itemClsFontSize = 15,
			itemClsFont = core.ui._buildFont(itemClsFontSize),
			itemClsColor = "#fff",
			itemCls_x = itemText_x - itemClsFontSize / 2,
			itemCls_middle = (iconRect_bottom + itemText_y) / 2, //_middle代表文字的中心y坐标
			itemNameFontSize = 18,
			itemNameColor = "#fff",
			itemNameFont = core.ui._buildFont(itemNameFontSize, true);
		var itemName_x = iconRect_right + space;
		var itemName_middle = iconRect_y + iconRect_height / 2 + iconRect_lineWidth;
		// 修改这里可以编辑未选中道具时的默认值
		var defaultItem = {
			cls: "constants",
			name: "未知道具",
			text: "没有道具最永久"
		}
		var defaultEquip = {
			cls: "equips",
			name: "未知装备",
			text: "一无所有，又何尝不是一种装备",
			equip: {
				type: "装备"
			}
		}
		///// ***

		var originItem = item;
		if (core.status.event.id == "equipbox") item = item || defaultEquip;
		item = item || defaultItem;
		var itemCls = item.cls,
			itemName = item.name,
			itemText = item.text;
		itemText = core.replaceText(itemText);
		/* 一个根据道具id修改道具名字(右栏)的例子
		 * if (item.id == "xxx") itemNameColor = "red";
		 */
		var itemClsName = core.getItemClsName(item);
		var itemNameMaxWidth = rightbar_width - iconRect_width - iconRect_lineWidth * 2 - space * 2;
		core.strokeRoundRect(ctx, iconRect_x, iconRect_y, iconRect_width, iconRect_height, iconRect_radius, iconRect_style, iconRect_lineWidth);
		if (item.id)
			core.drawIcon(ctx, item.id, iconRect_x + iconRect_lineWidth / 2, iconRect_y + iconRect_lineWidth / 2, iconRect_width - iconRect_lineWidth, iconRect_height - iconRect_lineWidth);
		core.setTextAlign(ctx, "left");
		core.setTextBaseline(ctx, "middle");
		core.fillText(ctx, itemName, itemName_x, itemName_middle, itemNameColor, itemNameFont, itemNameMaxWidth);
		core.fillText(ctx, "【" + itemClsName + "】", itemCls_x, itemCls_middle, itemClsColor, itemClsFont);
		var statusText = "";
		if (core.status.event.id == "equipbox") {
			var type = item.equip.type;
			if (typeof type == "string") type = core.getEquipTypeByName(type);
			var compare = core.compareEquipment(item.id, core.getEquip(type));
			if (info.select.action == "unload") compare = core.compareEquipment(null, item.id);
			// --- 变化值...
			for (var name in core.status.hero) {
				if (typeof core.status.hero[name] != 'number') continue;
				var nowValue = core.getRealStatus(name);
				// 查询新值
				var newValue = Math.floor((core.getStatus(name) + (compare.value[name] || 0)) *
					(core.getBuff(name) * 100 + (compare.percentage[name] || 0)) / 100);
				if (nowValue == newValue) continue;
				var color = newValue > nowValue ? '#00FF00' : '#FF0000';
				nowValue = core.formatBigNumber(nowValue);
				newValue = core.formatBigNumber(newValue);
				statusText += core.getStatusLabel(name) + " " + nowValue + "->\r[" + color + "]" + newValue + "\r\n";
			}
		}
		itemText = statusText + itemText;
		core.drawTextContent(ctx, itemText, {
			left: itemText_x,
			top: itemText_y,
			bold: false,
			color: "white",
			align: "left",
			fontSize: itemTextFontSize,
			maxWidth: rightbar_width - (itemText_x - rightbar_x) * 2 + itemTextFontSize / 2
		});

		///// *** 退出按钮设置
		var btnRadius = 10;
		var btnBorderWidth = 2;
		var btnRight = toolboxRight - 2;
		var btnBottom = toolboxBottom - 2;
		var btnBorderStyle = "#fff";
		///// ***

		// 获取圆心位置
		var btn_x = btnRight - btnRadius - btnBorderWidth / 2,
			btn_y = btnBottom - btnRadius - btnBorderWidth / 2;
		core.drawToolbox_setExitBtn(ctx, btn_x, btn_y, btnRadius, btnBorderStyle, btnBorderWidth);

		///// *** 使用按钮设置
		var useBtnHeight = btnRadius * 2;
		// 这里不设置useBtnWidth而是根据各项数据自动得出width
		var useBtnRadius = useBtnHeight / 2;
		var useBtn_x = rightbar_x + 4,
			useBtn_y = btnBottom - useBtnHeight;
		var useBtnBorderStyle = "#fff";
		var useBtnBorderWidth = btnBorderWidth;
		///// ***

		core.drawToolbox_setUseBtn(ctx, useBtn_x, useBtn_y, useBtnRadius, useBtnHeight, useBtnBorderStyle, useBtnBorderWidth);

	}

	this.drawEquipbox_drawOthers = function (ctx, obj) {
		var info = core.status.thisUIEventInfo;

		///// *** 装备格设置
		var equipList_lineWidth = 2;
		var equipList_boxSize = 32;
		var equipList_borderWidth = 2;
		var equipList_borderStyle = "#fff";
		var equipList_nameColor = "#fff";
		///// ***

		var equipList_x = obj.x + 4,
			equipList_bottom = obj.obj.y - equipList_lineWidth,
			equipList_y = equipList_bottom - obj.obj.oneItemHeight * reduceItem - 2,
			equipList_height = equipList_bottom - equipList_y;
		var equipList_right = obj.leftbar_right,
			equipList_width = equipList_right - equipList_x;
		core.drawLine(ctx, obj.x, equipList_bottom + equipList_lineWidth / 2, equipList_right, equipList_bottom + equipList_lineWidth / 2, equipList_borderStyle, equipList_lineWidth);
		var toDrawList = core.status.globalAttribute.equipName,
			len = toDrawList.length;

		///// *** 装备格设置
		var maxItem = 4;
		var box_width = 32,
			box_height = 32,
			box_borderStyle = "#fff",
			box_selectBorderStyle = "gold", // 选中的装备格的颜色
			box_borderWidth = 2;
		var boxName_fontSize = 14,
			boxName_space = 2,
			boxName_color = "#fff"; // 装备格名称与上面的装备格框的距离
		var maxLine = Math.ceil(len / maxItem);
		///// ***
		var l = Math.sqrt(len)
		if (Math.pow(l) == len && len != 4) {
			if (l <= maxItem) maxItem = l;
		}
		maxItem = Math.min(toDrawList.length, maxItem);
		info.equips = maxItem;

		var boxName_font = core.ui._buildFont(boxName_fontSize);
		// 总宽高减去所有装备格宽高得到空隙大小
		var oneBoxWidth = box_width + box_borderWidth * 2;
		var oneBoxHeight = box_height + boxName_fontSize + boxName_space + 2 * box_borderWidth;
		var space_y = (equipList_height - maxLine * oneBoxHeight) / (1 + maxLine),
			space_x = (equipList_width - maxItem * oneBoxWidth) / (1 + maxItem);
		var box_x = equipList_x + space_x,
			box_y = equipList_y + space_y;
		for (var i = 0; i < len; i++) {
			var id = core.getEquip(i),
				name = toDrawList[i];
			var selectBorder = false;
			if (core.status.thisUIEventInfo.select.type == i) selectBorder = true;
			var borderStyle = selectBorder ? box_selectBorderStyle : box_borderStyle;
			core.drawEquipbox_drawOne(ctx, name, id, box_x, box_y, box_width, box_height, boxName_space, boxName_font, boxName_color, borderStyle, box_borderWidth);
			var todo = new Function("core.clickOneEquipbox('" + id + "'," + i + ")");
			core.addUIEventListener(box_x - box_borderWidth / 2, box_y - box_borderWidth / 2, oneBoxWidth, oneBoxHeight, todo);
			box_x += space_x + oneBoxWidth;
			if ((i + 1) % maxItem == 0) {
				box_x = equipList_x + space_x;
				box_y += space_y + oneBoxHeight;
			}
		}
	}

	this.drawToolbox = function (ctx) {
		ctx = ctx || core.canvas.ui;
		core.status.thisEventClickArea = [];

		var info = core.drawBoxBackground(ctx);
		info.itemNum = itemNum;
		core.drawItemListbox(ctx, info.obj);
		core.drawToolboxRightbar(ctx, info);
		core.setTextBaseline(ctx, "alphabetic");
		core.setTextAlign("left");
	}

	var reduceItem = 4;
	this.drawEquipbox = function (ctx) {
		ctx = ctx || core.canvas.ui;
		core.status.thisEventClickArea = [];
		var info = core.drawBoxBackground(ctx);
		info.itemNum = itemNum - reduceItem;
		info.obj.y += info.obj.oneItemHeight * reduceItem;
		info.obj.height -= info.obj.oneItemHeight * reduceItem;
		core.drawItemListbox(ctx, info.obj);
		core.drawEquipbox_drawOthers(ctx, info);
		core.drawToolboxRightbar(ctx, info);
		core.setTextBaseline(ctx, "alphabetic");
		core.setTextAlign("left");
	}


	this.drawEquipbox_drawOne = function (ctx, name, id, x, y, width, height, space, font, color, style, lineWidth) {
		if (id) core.drawIcon(ctx, id, x + lineWidth / 2, y + lineWidth / 2, width, height);
		core.strokeRect(ctx, x, y, width + lineWidth, height + lineWidth, style, lineWidth);
		core.setTextAlign(ctx, "center");
		core.setTextBaseline(ctx, "top");
		var tx = (x + x + lineWidth / 2 + width) / 2,
			ty = y + height + lineWidth / 2 * 3 + space;
		core.fillText(ctx, name, tx, ty, color, font);
		core.setTextBaseline(ctx, "alphabetic");
		core.setTextAlign("left");
	}

	this.drawItemListbox_drawItem = function (ctx, left, right, top, height, marginLeft, marginHeight, style, id) {
		var info = core.status.thisUIEventInfo;
		var nowClick = info.index;
		var item = core.material.items[id] || {};
		var name = item.name || "???";
		var num = core.itemCount(id) || 0;
		var fontSize = Math.floor(height - marginHeight * 2);
		core.setTextAlign(ctx, "right");
		var numText = "x" + num;
		core.fillText(ctx, numText, right - marginLeft, top + height / 2, style, core.ui._buildFont(fontSize));
		if (name != "???") core.drawIcon(ctx, id, left + marginLeft, top + marginHeight, fontSize, fontSize);
		var text_x = left + marginLeft + fontSize + 2;
		var maxWidth = right - core.calWidth(ctx, numText) - text_x;
		core.setTextAlign(ctx, "left");
		core.fillText(ctx, name, text_x, top + height / 2, style, core.ui._buildFont(fontSize), maxWidth);
		var todo = new Function("core.clickItemFunc('" + id + "');");
		core.addUIEventListener(left, top, right - left, height, todo);
	}

	this.setPageItems = function (page) {
		var num = itemNum;
		if (core.status.event.id == "equipbox") num -= reduceItem;
		var info = core.status.thisUIEventInfo;
		if (!info) return;
		page = page || info.page;
		var items = core.getToolboxItems(core.status.event.id == "toolbox" ? "all" : "equips");
		info.allItems = items;
		var maxPage = Math.ceil(items.length / num);
		info.maxPage = maxPage;
		var pageItems = items.slice((page - 1) * num, page * num);
		info.pageItems = pageItems;
		info.maxItem = pageItems.length;
		if (items.length == 0 && pageItems.length == 0) info.index = null;
		if (pageItems.length == 0 && info.page > 1) {
			info.page = Math.max(1, info.page - 1);
			return core.setPageItems(info.page);
		}
		return pageItems;
	}

	this.drawToolbox_setExitBtn = function (ctx, x, y, r, style, lineWidth) {
		core.strokeCircle(ctx, x, y, r, style, lineWidth);
		ctx.textAlign = "center";
		ctx.textBaseline = "middle";
		var textSize = Math.sqrt(2) * r;
		core.fillText(ctx, "x", x, y, style, core.ui._buildFont(textSize), textSize);
		core.setTextAlign(ctx, "start");
		core.setTextBaseline(ctx, "top");

		var todo = function () {
			core.closePanel();
		}
		core.addUIEventListener(x - r, y - r, r * 2, r * 2, todo);
	}

	this.drawToolbox_setUseBtn = function (ctx, x, y, r, h, style, lineWidth) {
		core.setTextAlign(ctx, "left");
		core.setTextBaseline(ctx, "top");
		var fontSize = h - 4;
		var font = core.ui._buildFont(fontSize);
		var text = core.status.event.id == "toolbox" ? "使用" : "装备";
		if (core.status.thisUIEventInfo.select.action == "unload") text = "卸下";
		var w = core.calWidth(ctx, text, font) + 2 * r + lineWidth / 2;

		core.strokeRoundRect(ctx, x, y, w, h, r, style, lineWidth);
		core.fillText(ctx, text, x + r, y + lineWidth / 2 + 2, style, font);

		var todo = function () {
			core.useSelectItemInBox();
		}
		core.addUIEventListener(x, y, w, h, todo);
	}

	this.drawItemListbox_setPageBtn = function (ctx, left, right, bottom, r, style, lineWidth) {
		var offset = lineWidth / 2 + r;

		var x = left + offset;
		var y = bottom - offset;
		var pos = Math.sqrt(2) / 2 * (r - lineWidth / 2);
		core.fillPolygon(ctx, [
			[x - pos, y],
			[x + pos - 2, y - pos],
			[x + pos - 2, y + pos]
		], style);
		core.strokeCircle(ctx, x, y, r, style, lineWidth);
		var todo = function () {
			core.addItemListboxPage(-1);
		}
		core.addUIEventListener(x - r - 2, y - r - 2, r * 2 + 4, r * 2 + 4, todo);

		x = right - offset;
		core.fillPolygon(ctx, [
			[x + pos, y],
			[x - pos + 2, y - pos],
			[x - pos + 2, y + pos]
		], style);
		core.strokeCircle(ctx, x, y, r, style, lineWidth);
		var todo = function () {
			core.addItemListboxPage(1);
		}
		core.addUIEventListener(x - r - 2, y - r - 2, r * 2 + 4, r * 2 + 4, todo);
	}

	this.clickItemFunc = function (id) {
		var info = core.status.thisUIEventInfo;
		if (!info) return;
		if (info.select.id == id) return core.useSelectItemInBox();
		info.select = {};
		info.select.id = id;
		core.setIndexAndSelect('index');
		core.refreshBox();
	}

	this.clickOneEquipbox = function (id, type) {
		var info = core.status.thisUIEventInfo;
		if (!info) return;
		if (info.select.id == id && info.select.type == type) core.useSelectItemInBox();
		else core.status.thisUIEventInfo.select = {
			id: id,
			type: type,
			action: "unload"
		}
		return core.refreshBox();
	}

	core.ui.getToolboxItems = function (cls) {
		var list = Object.keys(core.status.hero.items[cls] || {});
		if (cls == "all") {
			for (var name in core.status.hero.items) {
				if (name == "equips") continue;
				list = list.concat(Object.keys(core.status.hero.items[name]));
			}
			return list.filter(function (id) {
				return !core.material.items[id].hideInToolbox;
			}).sort();
		}

		if (this.uidata.getToolboxItems) {
			return this.uidata.getToolboxItems(cls);
		}
		return list.filter(function (id) {
			return !core.material.items[id].hideInToolbox;
		}).sort();
	}

	this.useSelectItemInBox = function () {
		var info = core.status.thisUIEventInfo;
		if (!info) return;
		if (!info.select.id) return;
		var id = info.select.id;
		if (core.status.event.id == "toolbox") {
			core.events.tryUseItem(id);
			// core.closePanel();
		} else if (core.status.event.id == "equipbox") {
			var action = info.select.action || "load";
			info.index = 1;
			if (action == "load") {
				var type = core.getEquipTypeById(id);
				core.loadEquip(id, function () {
					core.status.route.push("equip:" + id);
					info.select.type = type;
					core.setIndexAndSelect("select");
					core.drawEquipbox();
				});
			} else {
				var type = info.select.type;
				core.unloadEquip(type, function () {
					core.status.route.push("unEquip:" + type);
					info.select.type = type;
					//info.select.action = 'load'
					core.setIndexAndSelect("select");
					core.drawEquipbox();
				});
			}
		}
	}
	this.setIndexAndSelect = function (toChange) {
		var info = core.status.thisUIEventInfo;
		if (!info) return;
		core.setPageItems(info.page);
		var index = info.index || 1;
		var items = info.pageItems;
		if (info.select.type != null) {
			var type = info.select.type;
			id = core.getEquip(type);
			info.index = null;
			info.select = {
				id: id,
				action: "unload",
				type: type
			};
			return;
		} else {
			info.select.action = null;
			info.select.type = null;
			if (toChange == "index") info.index = items.indexOf(info.select.id) + 1;
			info.select.id = items[info.index - 1];
		}

	}

	this.addItemListboxPage = function (num) {
		var info = core.status.thisUIEventInfo;
		if (!info) return;
		var maxPage = info.maxPage || 1;
		info.page = info.page || 1;
		info.page += num;
		if (info.page <= 0) info.page = maxPage;
		if (info.page > maxPage) info.page = 1;
		info.index = 1;
		core.setPageItems(info.page);
		core.setIndexAndSelect("select");
		core.refreshBox();
	}

	this.addItemListboxIndex = function (num) {
		var info = core.status.thisUIEventInfo;
		if (!info) return;
		var maxItem = info.maxItem || 0;
		info.index = info.index || 0;
		info.index += num;
		if (info.index <= 0) info.index = 1;
		if (info.index > maxItem) info.index = maxItem;
		core.setIndexAndSelect("select");
		core.refreshBox();
	}

	this.addEquipboxType = function (num) {
		var info = core.status.thisUIEventInfo;
		var type = info.select.type;
		if (type == null && num > 0) info.select.type = 0;
		else info.select.type = type + num;
		var max = core.status.globalAttribute.equipName.length;
		if (info.select.type >= max) {
			info.select = {};
			core.setIndexAndSelect("select")
			return core.addItemListboxPage(0);
		} else {
			var m = Math.abs(info.select.type);
			if (info.select.type < 0) info.select.type = max - m;
			core.setIndexAndSelect("select")
			core.refreshBox();
			return;
		}
	}

	core.actions._keyDownToolbox = function (keycode) {
		if (!core.status.thisEventClickArea) return;
		if (keycode == 37) { // left
			core.addItemListboxPage(-1);
			return;
		}
		if (keycode == 38) { // up
			core.addItemListboxIndex(-1);
			return;
		}
		if (keycode == 39) { // right
			core.addItemListboxPage(1);
			return;
		}
		if (keycode == 40) { // down
			core.addItemListboxIndex(1);
			return;
		}
	}

	////// 工具栏界面时，放开某个键的操作 //////
	core.actions._keyUpToolbox = function (keycode) {
		if (keycode == 81) {
			core.ui.closePanel();
			if (core.isReplaying())
				core.control._replay_equipbox();
			else
				core.openEquipbox();
			return;
		}
		if (keycode == 84 || keycode == 27 || keycode == 88) {
			core.closePanel();
			return;
		}
		if (keycode == 13 || keycode == 32 || keycode == 67) {
			var info = core.status.thisUIEventInfo;
			if (info.select) {
				core.useSelectItemInBox();
			}
			return;
		}
	}

	core.actions._keyDownEquipbox = function (keycode) {
		if (!core.status.thisEventClickArea) return;
		if (keycode == 37) { // left
			var info = core.status.thisUIEventInfo;
			if (info.index != null) return core.addItemListboxPage(-1);
			return core.addEquipboxType(-1);
		}
		if (keycode == 38) { // up
			var info = core.status.thisUIEventInfo;
			if (info.index == 1) {
				info.select.type = core.status.globalAttribute.equipName.length - 1;
				core.setIndexAndSelect();
				return core.refreshBox();
			}
			if (info.index) return core.addItemListboxIndex(-1);
			return core.addEquipboxType(-1 * info.equips);
		}
		if (keycode == 39) { // right
			var info = core.status.thisUIEventInfo;
			if (info.index != null) return core.addItemListboxPage(1);
			return core.addEquipboxType(1);
		}
		if (keycode == 40) { // down
			var info = core.status.thisUIEventInfo;
			if (info.index) return core.addItemListboxIndex(1);
			return core.addEquipboxType(info.equips);
		}
	}

	core.actions._keyUpEquipbox = function (keycode, altKey) {
		if (altKey && keycode >= 48 && keycode <= 57) {
			core.items.quickSaveEquip(keycode - 48);
			return;
		}
		if (keycode == 84) {
			core.ui.closePanel();
			if (core.isReplaying())
				core.control._replay_toolbox();
			else
				core.openToolbox();
			return;
		}
		if (keycode == 81 || keycode == 27 || keycode == 88) {
			core.closePanel();
			return;
		}
		if (keycode == 13 || keycode == 32 || keycode == 67) {
			var info = core.status.thisUIEventInfo;
			if (info.select) core.useSelectItemInBox();
			return;
		}
	}

	core.registerAction("ondown", "inEventClickAction", function (x, y, px, py) {
		if (!core.status.thisEventClickArea) return false;
		// console.log(px + "," + py);
		var info = core.status.thisEventClickArea;
		for (var i = 0; i < info.length; i++) {
			var obj = info[i];
			if (px >= obj.x && px <= obj.x + obj.width && py > obj.y && py < obj.y + obj.height) {
				if (obj.todo) obj.todo();
				break;
			}
		}
		return true;
	}, 51);
	core.registerAction("onclick", "stopClick", function () {
		if (core.status.thisEventClickArea) return true;
	}, 51);

	this.addUIEventListener = function (x, y, width, height, todo) {
		if (!core.status.thisEventClickArea) return;
		var obj = {
			x: x,
			y: y,
			width: width,
			height: height,
			todo: todo
		}
		core.status.thisEventClickArea.push(obj);
	}

	this.initThisEventInfo = function () {
		core.status.thisUIEventInfo = {
			page: 1,
			select: {}
		};
		core.status.thisEventClickArea = [];
	}

	this.refreshBox = function () {
		if (!core.status.event.id) return;
		if (core.status.event.id == "toolbox") core.drawToolbox();
		else core.drawEquipbox();
	}

	core.ui.closePanel = function () {
		if (core.status.hero && core.status.hero.flags) {
			// 清除全部临时变量
			Object.keys(core.status.hero.flags).forEach(function (name) {
				if (name.startsWith("@temp@") || /^arg\d+$/.test(name)) {
					delete core.status.hero.flags[name];
				}
			});
		}
		this.clearUI();
		core.maps.generateGroundPattern();
		core.updateStatusBar(true);
		core.unlockControl();
		core.status.event.data = null;
		core.status.event.id = null;
		core.status.event.selection = null;
		core.status.event.ui = null;
		core.status.event.interval = null;
		core.status.thisUIEventInfo = null;
		core.status.thisEventClickArea = null
	}

	this.getItemClsName = function (item) {
		if (item == null) return itemClsName;
		if (item.cls == "equips") {
			if (typeof item.equip.type == "string") return item.equip.type;
			var type = core.getEquipTypeById(item.id);
			return core.status.globalAttribute.equipName[type];
		} else return itemClsName[item.cls] || item.cls;
	}

	core.events.openToolbox = function (fromUserAction) {
		if (core.isReplaying()) return;
		if (!this._checkStatus('toolbox', fromUserAction)) return;
		core.initThisEventInfo();
		let info = core.status.thisUIEventInfo
		info.index = 1
		core.setIndexAndSelect('select')
		core.drawToolbox();
	}

	core.events.openEquipbox = function (fromUserAction) {
		if (core.isReplaying()) return;
		if (!this._checkStatus('equipbox', fromUserAction)) return;
		core.initThisEventInfo();
		let info = core.status.thisUIEventInfo
		info.select.type = 0
		core.setIndexAndSelect('select')
		core.drawEquipbox();
	}

	core.control._replay_toolbox = function () {
		if (!core.isPlaying() || !core.isReplaying()) return;
		if (!core.status.replay.pausing) return core.drawTip("请先暂停录像");
		if (core.isMoving() || core.status.replay.animate || core.status.event.id)
			return core.drawTip("请等待当前事件的处理结束");

		core.lockControl();
		core.status.event.id = 'toolbox';
		core.drawToolbox();
	}

	core.control._replay_equipbox = function () {
		if (!core.isPlaying() || !core.isReplaying()) return;
		if (!core.status.replay.pausing) return core.drawTip("请先暂停录像");
		if (core.isMoving() || core.status.replay.animate || core.status.event.id)
			return core.drawTip("请等待当前事件的处理结束");

		core.lockControl();
		core.status.event.id = 'equipbox';
		core.drawEquipbox();
	}

	core.control._replayAction_item = function (action) {
		if (action.indexOf("item:") != 0) return false;
		var itemId = action.substring(5);
		if (!core.canUseItem(itemId)) return false;
		if (core.material.items[itemId].hideInReplay || core.status.replay.speed == 24) {
			core.useItem(itemId, false, core.replay);
			return true;
		}
		core.status.event.id = "toolbox";
		core.initThisEventInfo();
		var info = core.status.thisUIEventInfo;
		var items = core.getToolboxItems("all");
		core.setPageItems(1);
		var index = items.indexOf(itemId) + 1;
		info.page = Math.ceil(index / info.maxItem);
		info.index = index % info.maxItem || info.maxItem;
		core.setIndexAndSelect("select");
		core.setPageItems(info.page);
		core.drawToolbox();
		setTimeout(function () {
			core.ui.closePanel();
			core.useItem(itemId, false, core.replay);
		}, core.control.__replay_getTimeout());
		return true;
	}

	core.control._replayAction_equip = function (action) {
		if (action.indexOf("equip:") != 0) return false;
		var itemId = action.substring(6);
		var items = core.getToolboxItems('equips');
		var index = items.indexOf(itemId) + 1;
		if (index < 1) return false;
		core.status.route.push(action);
		if (core.material.items[itemId].hideInReplay || core.status.replay.speed == 24) {
			core.loadEquip(itemId, core.replay);
			return true;
		}
		core.status.event.id = "equipbox";
		core.initThisEventInfo();
		var info = core.status.thisUIEventInfo;
		core.setPageItems(1);
		info.page = Math.ceil(index / info.maxItem);
		info.index = index % info.maxItem || info.maxItem;
		core.setIndexAndSelect("select");
		core.setPageItems(info.page);
		core.drawEquipbox();
		setTimeout(function () {
			core.ui.closePanel();
			core.loadEquip(itemId, core.replay);
		}, core.control.__replay_getTimeout());
		return true;
	}

	core.control._replayAction_unEquip = function (action) {
		if (action.indexOf("unEquip:") != 0) return false;
		var equipType = parseInt(action.substring(8));
		if (!core.isset(equipType)) return false;
		core.status.route.push(action);
		if (core.status.replay.speed == 24) {
			core.unloadEquip(equipType, core.replay);
			return true;
		}
		core.status.event.id = "equipbox";
		core.initThisEventInfo();
		var info = core.status.thisUIEventInfo;
		core.setPageItems(1);
		info.select.type = equipType;
		core.setIndexAndSelect();
		core.drawEquipbox();
		setTimeout(function () {
			core.ui.closePanel();
			core.unloadEquip(equipType, core.replay);
		}, core.control.__replay_getTimeout());
		return true;
	}


	core.registerReplayAction("item", core.control._replayAction_item);
	core.registerReplayAction("equip", core.control._replayAction_equip);
	core.registerReplayAction("unEquip", core.control._replayAction_unEquip);
},
    "itemShop": function () {
	// 在此增加新插件
	// 道具商店相关的插件
	// 可在全塔属性-全局商店中使用「道具商店」事件块进行编辑（如果找不到可以在入口方块中找）

	var shopId = null; // 当前商店ID
	var type = 0; // 当前正在选中的类型，0买入1卖出
	var selectItem = 0; // 当前正在选中的道具
	var selectCount = 0; // 当前已经选中的数量
	var page = 0;
	var totalPage = 0;
	var totalMoney = 0;
	var list = [];
	var shopInfo = null; // 商店信息
	var choices = []; // 商店选项
	var use = 'money';
	var useText = '金币';

	var bigFont = core.ui._buildFont(20, false),
		middleFont = core.ui._buildFont(18, false);

	this._drawItemShop = function () {
		// 绘制道具商店

		// Step 1: 背景和固定的几个文字
		core.ui._createUIEvent();
		core.clearMap('uievent');
		core.ui.clearUIEventSelector();
		core.setTextAlign('uievent', 'left');
		core.setTextBaseline('uievent', 'top');
		core.fillRect('uievent', 0, 0, 416, 416, 'black');
		core.drawWindowSkin('winskin.png', 'uievent', 0, 0, 416, 56);
		core.drawWindowSkin('winskin.png', 'uievent', 0, 56, 312, 56);
		core.drawWindowSkin('winskin.png', 'uievent', 0, 112, 312, 304);
		core.drawWindowSkin('winskin.png', 'uievent', 312, 56, 104, 56);
		core.drawWindowSkin('winskin.png', 'uievent', 312, 112, 104, 304);
		core.setFillStyle('uievent', 'white');
		core.setStrokeStyle('uievent', 'white');
		core.fillText("uievent", "购买", 32, 74, 'white', bigFont);
		core.fillText("uievent", "卖出", 132, 74);
		core.fillText("uievent", "离开", 232, 74);
		core.fillText("uievent", "当前" + useText, 324, 66, null, middleFont);
		core.setTextAlign("uievent", "right");
		core.fillText("uievent", core.formatBigNumber(core.status.hero[use]), 405, 89);
		core.setTextAlign("uievent", "left");
		core.ui.drawUIEventSelector(1, "winskin.png", 22 + 100 * type, 66, 60, 33);
		if (selectItem != null) {
			core.setTextAlign('uievent', 'center');
			core.fillText("uievent", type == 0 ? "买入个数" : "卖出个数", 364, 320, null, bigFont);
			core.fillText("uievent", "<   " + selectCount + "   >", 364, 350);
			core.fillText("uievent", "确定", 364, 380);
		}

		// Step 2：获得列表并展示
		list = choices.filter(function (one) {
			if (one.condition != null && one.condition != '') {
				try { if (!core.calValue(one.condition)) return false; } catch (e) {}
			}
			return (type == 0 && one.money != null) || (type == 1 && one.sell != null);
		});
		var per_page = 6;
		totalPage = Math.ceil(list.length / per_page);
		page = Math.floor((selectItem || 0) / per_page) + 1;

		// 绘制分页
		if (totalPage > 1) {
			var half = 156;
			core.setTextAlign('uievent', 'center');
			core.fillText('uievent', page + " / " + totalPage, half, 388, null, middleFont);
			if (page > 1) core.fillText('uievent', '上一页', half - 80, 388);
			if (page < totalPage) core.fillText('uievent', '下一页', half + 80, 388);
		}
		core.setTextAlign('uievent', 'left');

		// 绘制每一项
		var start = (page - 1) * per_page;
		for (var i = 0; i < per_page; ++i) {
			var curr = start + i;
			if (curr >= list.length) break;
			var item = list[curr];
			core.drawIcon('uievent', item.id, 10, 125 + i * 40);
			core.setTextAlign('uievent', 'left');
			core.fillText('uievent', core.material.items[item.id].name, 50, 132 + i * 40, null, bigFont);
			core.setTextAlign('uievent', 'right');
			core.fillText('uievent', (type == 0 ? core.calValue(item.money) : core.calValue(item.sell)) + useText + "/个", 300, 133 + i * 40, null, middleFont);
			core.setTextAlign("uievent", "left");
			if (curr == selectItem) {
				// 绘制描述，文字自动放缩
				var text = core.material.items[item.id].text || "该道具暂无描述";
				try { text = core.replaceText(text); } catch (e) {}
				for (var fontSize = 20; fontSize >= 8; fontSize -= 2) {
					var config = { left: 10, fontSize: fontSize, maxWidth: 403 };
					var height = core.getTextContentHeight(text, config);
					if (height <= 50) {
						config.top = (56 - height) / 2;
						core.drawTextContent("uievent", text, config);
						break;
					}
				}
				core.ui.drawUIEventSelector(2, "winskin.png", 8, 120 + i * 40, 295, 40);
				if (type == 0 && item.number != null) {
					core.fillText("uievent", "存货", 324, 132, null, bigFont);
					core.setTextAlign("uievent", "right");
					core.fillText("uievent", item.number, 406, 132, null, null, 40);
				} else if (type == 1) {
					core.fillText("uievent", "数量", 324, 132, null, bigFont);
					core.setTextAlign("uievent", "right");
					core.fillText("uievent", core.itemCount(item.id), 406, 132, null, null, 40);
				}
				core.setTextAlign("uievent", "left");
				core.fillText("uievent", "预计" + useText, 324, 250);
				core.setTextAlign("uievent", "right");
				totalMoney = selectCount * (type == 0 ? core.calValue(item.money) : core.calValue(item.sell));
				core.fillText("uievent", core.formatBigNumber(totalMoney), 405, 280);

				core.setTextAlign("uievent", "left");
				core.fillText("uievent", type == 0 ? "已购次数" : "已卖次数", 324, 170);
				core.setTextAlign("uievent", "right");
				core.fillText("uievent", (type == 0 ? item.money_count : item.sell_count) || 0, 405, 200);
			}
		}

		core.setTextAlign('uievent', 'left');
		core.setTextBaseline('uievent', 'alphabetic');
	}

	var _add = function (item, delta) {
		if (item == null) return;
		selectCount = core.clamp(
			selectCount + delta, 0,
			Math.min(type == 0 ? Math.floor(core.status.hero[use] / core.calValue(item.money)) : core.itemCount(item.id),
				type == 0 && item.number != null ? item.number : Number.MAX_SAFE_INTEGER)
		);
	}

	var _confirm = function (item) {
		if (item == null || selectCount == 0) return;
		if (type == 0) {
			core.status.hero[use] -= totalMoney;
			core.getItem(item.id, selectCount);
			core.stopSound();
			core.playSound('确定');
			if (item.number != null) item.number -= selectCount;
			item.money_count = (item.money_count || 0) + selectCount;
		} else {
			core.status.hero[use] += totalMoney;
			core.removeItem(item.id, selectCount);
			core.playSound('确定');
			core.drawTip("成功卖出" + selectCount + "个" + core.material.items[item.id].name, item.id);
			if (item.number != null) item.number += selectCount;
			item.sell_count = (item.sell_count || 0) + selectCount;
		}
		selectCount = 0;
	}

	this._performItemShopKeyBoard = function (keycode) {
		var item = list[selectItem] || null;
		// 键盘操作
		switch (keycode) {
		case 38: // up
			if (selectItem == null) break;
			if (selectItem == 0) selectItem = null;
			else selectItem--;
			selectCount = 0;
			break;
		case 37: // left
			if (selectItem == null) {
				if (type > 0) type--;
				break;
			}
			_add(item, -1);
			break;
		case 39: // right
			if (selectItem == null) {
				if (type < 2) type++;
				break;
			}
			_add(item, 1);
			break;
		case 40: // down
			if (selectItem == null) {
				if (list.length > 0) selectItem = 0;
				break;
			}
			if (list.length == 0) break;
			selectItem = Math.min(selectItem + 1, list.length - 1);
			selectCount = 0;
			break;
		case 13:
		case 32: // Enter/Space
			if (selectItem == null) {
				if (type == 2)
					core.insertAction({ "type": "break" });
				else if (list.length > 0)
					selectItem = 0;
				break;
			}
			_confirm(item);
			break;
		case 27: // ESC
			if (selectItem == null) {
				core.insertAction({ "type": "break" });
				break;
			}
			selectItem = null;
			break;
		}
	}

	this._performItemShopClick = function (px, py) {
		var item = list[selectItem] || null;
		// 鼠标操作
		if (px >= 22 && px <= 82 && py >= 71 && py <= 102) {
			// 买
			if (type != 0) {
				type = 0;
				selectItem = null;
				selectCount = 0;
			}
			return;
		}
		if (px >= 122 && px <= 182 && py >= 71 && py <= 102) {
			// 卖
			if (type != 1) {
				type = 1;
				selectItem = null;
				selectCount = 0;
			}
			return;
		}
		if (px >= 222 && px <= 282 && py >= 71 && py <= 102) // 离开
			return core.insertAction({ "type": "break" });
		// < >
		if (px >= 318 && px <= 341 && py >= 348 && py <= 376)
			return _add(item, -1);
		if (px >= 388 && px <= 416 && py >= 348 && py <= 376)
			return _add(item, 1);
		// 确定
		if (px >= 341 && px <= 387 && py >= 380 && py <= 407)
			return _confirm(item);

		// 上一页/下一页
		if (px >= 45 && px <= 105 && py >= 388) {
			if (page > 1) {
				selectItem -= 6;
				selectCount = 0;
			}
			return;
		}
		if (px >= 208 && px <= 268 && py >= 388) {
			if (page < totalPage) {
				selectItem = Math.min(selectItem + 6, list.length - 1);
				selectCount = 0;
			}
			return;
		}

		// 实际区域
		if (px >= 9 && px <= 300 && py >= 120 && py < 360) {
			if (list.length == 0) return;
			var index = parseInt((py - 120) / 40);
			var newItem = 6 * (page - 1) + index;
			if (newItem >= list.length) newItem = list.length - 1;
			if (newItem != selectItem) {
				selectItem = newItem;
				selectCount = 0;
			}
			return;
		}
	}

	this._performItemShopAction = function () {
		if (flags.type == 0) return this._performItemShopKeyBoard(flags.keycode);
		else return this._performItemShopClick(flags.px, flags.py);
	}

	this.openItemShop = function (itemShopId) {
		shopId = itemShopId;
		type = 0;
		page = 0;
		selectItem = null;
		selectCount = 0;
		core.isShopVisited(itemShopId);
		shopInfo = flags.__shops__[shopId];
		if (shopInfo.choices == null) shopInfo.choices = core.clone(core.status.shops[shopId].choices);
		choices = shopInfo.choices;
		use = core.status.shops[shopId].use;
		if (use != 'exp') use = 'money';
		useText = use == 'money' ? '金币' : '经验';

		core.insertAction([{
				"type": "while",
				"condition": "true",
				"data": [
					{ "type": "function", "function": "function () { core.plugin._drawItemShop(); }" },
					{ "type": "wait" },
					{ "type": "function", "function": "function() { core.plugin._performItemShopAction(); }" }
				]
			},
			{
				"type": "function",
				"function": "function () { core.deleteCanvas('uievent'); core.ui.clearUIEventSelector(); }"
			}
		]);
	}
},
    "multiHeros": function () {
	// 在此增加新插件
	// 多角色插件
	// Step 1: 启用本插件
	// Step 2: 定义每个新的角色各项初始数据（参见下方注释）
	// Step 3: 在游戏中的任何地方都可以调用 `core.changeHero()` 进行切换；也可以 `core.changeHero(1)` 来切换到某个具体的角色上

	// 是否开启本插件，默认禁用；将此改成 true 将启用本插件。
	var __enable = false;
	if (!__enable) return;

	// 在这里定义全部的新角色属性
	// 请注意，在这里定义的内容不会多角色共用，在切换时会进行恢复。
	// 你也可以自行新增或删除，比如不共用金币则可以加上"money"的初始化，不共用道具则可以加上"items"的初始化，
	// 多角色共用hp的话则删除hp，等等。总之，不共用的属性都在这里进行定义就好。
	var hero1 = {
		"floorId": "MT0", // 该角色初始楼层ID；如果共用楼层可以注释此项
		"image": "brave.png", // 角色的行走图名称；此项必填不然会报错
		"name": "1号角色",
		"lv": 1,
		"hp": 10000, // 如果HP共用可注释此项
		"atk": 1000,
		"def": 1000,
		"mdef": 0,
		// "money": 0, // 如果要不共用金币则取消此项注释
		// "exp": 0, // 如果要不共用经验则取消此项注释
		"loc": { "x": 0, "y": 0, "direction": "up" }, // 该角色初始位置；如果共用位置可注释此项
		"items": {
			"tools": {}, // 如果共用消耗道具（含钥匙）则可注释此项
			// "constants": {}, // 如果不共用永久道具（如手册）可取消注释此项
			"equips": {}, // 如果共用在背包的装备可注释此项
		},
		"equipment": [], // 如果共用装备可注释此项；此项和上面的「共用在背包的装备」需要拥有相同状态，不然可能出现问题
	};
	// 也可以类似新增其他角色
	// 新增的角色，各项属性共用与不共用的选择必须和上面完全相同，否则可能出现问题。
	// var hero2 = { ...

	var heroCount = 2; // 包含默认角色在内总共多少个角色，该值需手动修改。

	this.initHeros = function () {
		core.setFlag("hero1", core.clone(hero1)); // 将属性值存到变量中
		// core.setFlag("hero2", core.clone(hero2)); // 更多的角色也存入变量中；每个定义的角色都需要新增一行

		// 检测是否存在装备
		if (hero1.equipment) {
			if (!hero1.items || !hero1.items.equips) {
				alert('多角色插件的equipment和道具中的equips必须拥有相同状态！');
			}
			// 存99号套装为全空
			var saveEquips = core.getFlag("saveEquips", []);
			saveEquips[99] = [];
			core.setFlag("saveEquips", saveEquips);
		} else {
			if (hero1.items && hero1.items.equips) {
				alert('多角色插件的equipment和道具中的equips必须拥有相同状态！');
			}
		}
	}

	// 在游戏开始注入initHeros
	var _startGame_setHard = core.events._startGame_setHard;
	core.events._startGame_setHard = function () {
		_startGame_setHard.call(core.events);
		core.initHeros();
	}

	// 切换角色
	// 可以使用 core.changeHero() 来切换到下一个角色
	// 也可以 core.changeHero(1) 来切换到某个角色（默认角色为0）
	this.changeHero = function (toHeroId) {
		var currHeroId = core.getFlag("heroId", 0); // 获得当前角色ID
		if (toHeroId == null) {
			toHeroId = (currHeroId + 1) % heroCount;
		}
		if (currHeroId == toHeroId) return;

		var saveList = Object.keys(hero1);

		// 保存当前内容
		var toSave = {};
		// 暂时干掉 drawTip 和 音效，避免切装时的提示
		var _drawTip = core.ui.drawTip;
		core.ui.drawTip = function () {};
		var _playSound = core.control.playSound;
		core.control.playSound = function () {}
		// 记录当前录像，因为可能存在换装问题
		core.clearRouteFolding();
		var routeLength = core.status.route.length;
		// 优先判定装备
		if (hero1.equipment) {
			core.items.quickSaveEquip(100 + currHeroId);
			core.items.quickLoadEquip(99);
		}

		saveList.forEach(function (name) {
			if (name == 'floorId') toSave[name] = core.status.floorId; // 楼层单独设置
			else if (name == 'items') {
				toSave.items = core.clone(core.status.hero.items);
				Object.keys(toSave.items).forEach(function (one) {
					if (!hero1.items[one]) delete toSave.items[one];
				});
			} else toSave[name] = core.clone(core.status.hero[name]); // 使用core.clone()来创建新对象
		});

		core.setFlag("hero" + currHeroId, toSave); // 将当前角色信息进行保存
		var data = core.getFlag("hero" + toHeroId); // 获得要切换的角色保存内容

		// 设置角色的属性值
		saveList.forEach(function (name) {
			if (name == "floorId");
			else if (name == "items") {
				Object.keys(core.status.hero.items).forEach(function (one) {
					if (data.items[one]) core.status.hero.items[one] = core.clone(data.items[one]);
				});
			} else {
				core.status.hero[name] = core.clone(data[name]);
			}
		});
		// 最后装上装备
		if (hero1.equipment) {
			core.items.quickLoadEquip(100 + toHeroId);
		}

		core.ui.drawTip = _drawTip;
		core.control.playSound = _playSound;
		core.status.route = core.status.route.slice(0, routeLength);
		core.control._bindRoutePush();

		// 插入事件：改变角色行走图并进行楼层切换
		var toFloorId = data.floorId || core.status.floorId;
		var toLoc = data.loc || core.status.hero.loc;
		core.insertAction([
			{ "type": "setHeroIcon", "name": data.image || "hero.png" }, // 改变行走图
			// 同层则用changePos，不同层则用changeFloor；这是为了避免共用楼层造成触发eachArrive
			toFloorId != core.status.floorId ? {
				"type": "changeFloor",
				"floorId": toFloorId,
				"loc": [toLoc.x, toLoc.y],
				"direction": toLoc.direction,
				"time": 0 // 可以在这里设置切换时间
			} : { "type": "changePos", "loc": [toLoc.x, toLoc.y], "direction": toLoc.direction }
			// 你还可以在这里执行其他事件，比如增加或取消跟随效果
		]);
		core.setFlag("heroId", toHeroId); // 保存切换到的角色ID
	}
},
    "技能树": function () {

	// 现在支持录像了。


	// 本技能树使用了flag:usable，是一个数组，表示各技能页目前能否使用。
	// 本技能树还使用了flag:skill_level，也是一个数组，表示各技能的当前等级。
	// 请首先将它们初始化。

	// 请设置好所有的变量。可能会用到的函数如下：
	// openSkillTree(): 打开技能树
	// revealLevel(pos): 查看第pos个技能的等级
	// tryUpdate(pos): 尝试升级第pos个技能（若不满足升级条件，则不会升级）
	// 如果想强制提升技能等级，请自行重设skill_level。


	// [图片名称, x坐标, y坐标, 页码, 技能名]
	var images = [
		["sanity_cure.png", 78, 15, 0, "理智治疗"],
		["courier_level_up.png", 172, 15, 0, "讯使-升级"],
		["courier_skill.png", 172, 80, 0, "讯使-技能升级"],
		["exusiai_level_up.png", 44, 15, 1, "能天使-升级"],
		["exusiai_skill.png", 44, 80, 1, "能天使-技能升级"],
		["sanity_recovery.png", 44, 80, 0, "理智回复"],
		["sanity_deduction.png", 121, 80, 0, "理智推演"],
	];
	// 技能之间的边
	var edges = [
		[1, 2],
		[3, 4],
		[0, 5],
		[0, 6],
	];

	// 技能的初始等级
	// 重要：现在请用core.setFlag("skill_level", [1, 0, 0])初始化

	// 技能的满级
	var full_level = [3, 3, 3, 3, 3, 3, 3];
	// 技能的前置技能：必须将前置技能升级至满级，才能升级该技能
	// 若无，则填-1
	var fathers = [-1, -1, -1, -1, -1, 0, 0];
	// 不必修改
	var sons = [];
	// 当前页码（从0开始）
	var page = 0;
	// 总共的页码
	var maxpage = 2;
	// 翻到某一页时，默认选中的技能
	var defau = [0, 3];
	// 升级条件（将会展示在技能树界面中）
	var needed = {
		"sanity_cure.png": [
			"\r[#00FF00]需要 \r[#FFFF00]${core.getFlag('sc_usage_count', 0)}\r[#00FF00]/0 次使用",
			"\r[#00FF00]需要 \r[#FFFF00]${core.getFlag('sc_usage_count', 0)}\r[#00FF00]/25 次使用",
			"\r[#00FF00]需要 \r[#FFFF00]${core.getFlag('sc_usage_count', 0)}\r[#00FF00]/50 次使用",
			"\r[#00FF00]已满级"
		],
		"courier_level_up.png": [
			"\r[#00FF00]需要讯使达到精英阶段0， \r[#FFFF00]${core.getFlag('hero_basic')[0].level}\r[#00FF00]/50 级",
			"\r[#00FF00]需要讯使达到精英阶段1， \r[#FFFF00]${core.getFlag('hero_basic')[0].level}\r[#00FF00]/80 级",
			"\r[#00FF00]需要讯使达到精英阶段2， \r[#FFFF00]${core.getFlag('hero_basic')[0].level}\r[#00FF00]/60 级",
			"\r[#00FF00]已满级"
		],
		"courier_skill.png": [
			"\r[#00FF00]需要讯使达到精英阶段1，消耗\r[#FFFF00]${core.getFlag('skillbook', 0)}\r[#00FF00]/3 技巧概要",
			"\r[#00FF00]需要讯使达到精英阶段2，消耗\r[#FFFF00]${core.getFlag('skillbook', 0)}\r[#00FF00]/8 技巧概要",
			"\r[#00FF00]消耗 \r[#FFFF00]${core.getFlag('skillbook', 0)}\r[#00FF00]/15 技巧概要",
			"\r[#00FF00]已满级"
		],
		"exusiai_level_up.png": [
			"\r[#00FF00]需要能天使达到精英阶段0， \r[#FFFF00]${core.getFlag('hero_basic')[1].level}\r[#00FF00]/50 级",
			"\r[#00FF00]需要能天使达到精英阶段1， \r[#FFFF00]${core.getFlag('hero_basic')[1].level}\r[#00FF00]/80 级",
			"\r[#00FF00]需要能天使达到精英阶段2， \r[#FFFF00]${core.getFlag('hero_basic')[1].level}\r[#00FF00]/60 级",
			"\r[#00FF00]已满级"
		],
		"exusiai_skill.png": [
			"\r[#00FF00]需要能天使达到精英阶段1，消耗\r[#FFFF00]${core.getFlag('skillbook', 1)}\r[#00FF00]/3 技巧概要",
			"\r[#00FF00]需要能天使达到精英阶段2，消耗\r[#FFFF00]${core.getFlag('skillbook', 1)}\r[#00FF00]/8 技巧概要",
			"\r[#00FF00]消耗 \r[#FFFF00]${core.getFlag('skillbook', 0)}\r[#00FF00]/15 技巧概要",
			"\r[#00FF00]已满级"
		],
		"sanity_recovery.png": [
			"\r[#00FF00]需要理智治疗升级完成，并付出 \r[#FFFF00]${core.status.hero.mana}\r[#00FF00]/30 理智",
			"\r[#00FF00]需要理智治疗升级完成，并付出 \r[#FFFF00]${core.status.hero.mana}\r[#00FF00]/40 理智",
			"\r[#00FF00]需要理智治疗升级完成，并付出 \r[#FFFF00]${core.status.hero.mana}\r[#00FF00]/50 理智",
			"\r[#00FF00]已满级"
		],
		"sanity_deduction.png": [
			"\r[#00FF00]需要理智治疗升级完成，并付出 \r[#FFFF00]${core.status.hero.mana}\r[#00FF00]/20 理智",
			"\r[#00FF00]需要理智治疗升级完成，并付出 \r[#FFFF00]${core.status.hero.mana}\r[#00FF00]/40 理智",
			"\r[#00FF00]需要理智治疗升级完成，并付出 \r[#FFFF00]${core.status.hero.mana}\r[#00FF00]/60 理智",
			"\r[#00FF00]已满级"
		],
	};

	// 技能描述
	var description = {
		"sanity_cure.png": [ // 0
			"消耗10理智，使全队回复200/400/600体力。",
			"消耗10理智，使全队回复\r[#FFFF00]200\r[#FFFFFF]/400/600体力。",
			"消耗10理智，使全队回复200/\r[#FFFF00]400\r[#FFFFFF]/600体力。",
			"消耗10理智，使全队回复200/400/\r[#FFFF00]600\r[#FFFFFF]体力。",
		],
		"courier_level_up.png": [ // 1
			"将讯使提升到精英阶段1，等级变为1，属性不变，解锁天赋[雪境巡逻员]：受到的伤害-\r[#98F5FF]11\r[#FFFFFF]%",
			"将讯使提升到精英阶段2，等级变为1，属性不变，增强天赋[雪境巡逻员]：受到的伤害-\r[#98F5FF]19\r[#FFFFFF]%",
			"讯使获得战斗模组，进一步增强天赋[雪境巡逻员]：防御力+\r[#FFFF00]50\r[#FFFFFF]，受到的伤害-\r[#98F5FF]28\r[#FFFFFF]%",
			"讯使的作战升级已经完成。当前天赋[雪境巡逻员]：防御力+\r[#FFFF00]50\r[#FFFFFF]，受到的伤害-\r[#98F5FF]28\r[#FFFFFF]%",
		],
		"courier_skill.png": [ // 2
			"讯使的技能\r[green]冲锋号令-防御\r[white]消耗变为\r[#FFFF00]27\r[#FFFFFF]，效果增强：攻击速度+\r[#98F5FF]50\r[white]，防御力+\r[#98F5FF]50\r[white]%",
			"讯使的技能\r[green]冲锋号令-防御\r[white]消耗变为\r[#FFFF00]24\r[#FFFFFF]，效果增强：攻击速度+\r[#98F5FF]50\r[white]，防御力+\r[#98F5FF]80\r[white]%",
			"讯使的技能\r[green]冲锋号令-防御\r[white]消耗变为\r[#FFFF00]20\r[#FFFFFF]，效果增强：全员攻击速度+\r[#98F5FF]50\r[white]，防御力+\r[#98F5FF]120\r[white]%",
			"讯使的技能升级已经完成。当前技能[\r[green]冲锋号令-防御\r[white]]（消耗\r[#FFFF00]20\r[#FFFFFF]）：全员攻击速度+\r[#98F5FF]50\r[white]，防御力+\r[#98F5FF]120\r[white]%",
		],
		"exusiai_level_up.png": [ // 3
			"将能天使提升到精英阶段1，等级变为1，属性不变，解锁天赋[天使的祝福]：攻击速度+\r[#98F5FF]6\r[#FFFFFF]，自身与另一名干员攻击+\r[#98F5FF]6\r[#FFFFFF]%，且战后回复\r[#98F5FF]1\r[#FFFFFF]%战前生命值",
			"将能天使提升到精英阶段2，等级变为1，属性不变，增强天赋[天使的祝福]：攻击速度+\r[#98F5FF]15\r[#FFFFFF]，自身与另一名干员攻击+\r[#98F5FF]8\r[#FFFFFF]%，且战后回复\r[#98F5FF]3\r[#FFFFFF]%战前生命值",
			"能天使获得战斗模组，进一步增强天赋[天使的祝福]：攻击速度+\r[#98F5FF]21\r[#FFFFFF]，自身与另一名干员攻击+\r[#98F5FF]14\r[#FFFFFF]%，且战后回复\r[#98F5FF]5\r[#FFFFFF]%战前生命值",
			"能天使的作战升级已经完成。当前天赋[天使的祝福]：攻击速度+\r[#98F5FF]21\r[#FFFFFF]，自身与另一名干员攻击+\r[#98F5FF]14\r[#FFFFFF]%，且战后回复\r[#98F5FF]5\r[#FFFFFF]%战前生命值",
		],
		"exusiai_skill.png": [ // 4
			"能天使的技能\r[red]过载模式\r[white]效果增强：攻击变为\r[#98F5FF]五连击\r[white]，攻击速度+\r[#98F5FF]12\r[white]，攻击力提升至\r[#98F5FF]108\r[white]%。",
			"能天使的技能\r[red]过载模式\r[white]效果增强：攻击变为\r[#98F5FF]五连击\r[white]，攻击速度+\r[#98F5FF]24\r[white]，攻击力提升至\r[#98F5FF]112\r[white]%。",
			"能天使的技能\r[red]过载模式\r[white]效果增强：攻击变为\r[#98F5FF]五连击\r[white]，攻击速度+\r[#98F5FF]36\r[white]，攻击力提升至\r[#98F5FF]120\r[white]%，技能期间每击使敌人防御力-10",
			"能天使的技能升级已经完成。当前技能[\r[red]过载模式\r[white]]（消耗\r[#FFFF00]30\r[#FFFFFF]）：攻击变为\r[#98F5FF]五连击\r[white]，攻击速度+\r[#98F5FF]36\r[white]，攻击力提升至\r[#98F5FF]120\r[white]%，技能期间每击使敌人防御力-10",
		],
		"sanity_recovery.png": [ // 5
			"在每场战斗后回复1/2/3理智。",
			"在每场战斗后回复\r[#FFFF00]1\r[#FFFFFF]/2/3理智。",
			"在每场战斗后回复1/\r[#FFFF00]2\r[#FFFFFF]/3理智。",
			"在每场战斗后回复1/2/\r[#FFFF00]3\r[#FFFFFF]理智。",
		],
		"sanity_deduction.png": [ // 6
			"能够将10理智转化为1000/2000/3000经验。",
			"能够将10理智转化为\r[#FFFF00]1000\r[#FFFFFF]/2000/3000经验。",
			"能够将10理智转化为1000/\r[#FFFF00]2000\r[#FFFFFF]/3000经验。",
			"能够将10理智转化为1000/2000/\r[#FFFF00]3000\r[#FFFFFF]经验。",
		],
	};

	var cost_1 = function () {
		return core.getFlag('skillpoint', 0) >= 1;
	};

	var remove_1 = function () {
		core.addFlag('skillpoint', -1);
	};

	// predicate: 升级的条件。
	// 假设当前想要升级的技能是第pos个，升级前等级为level。
	// 若要升级它，除了其前置技能(fathers[pos])满级之外，还需要使得此技能的predicate[pos][level]()是true。
	var predicates = [
		[() => core.getFlag("sc_usage_count", 0) >= 0, () => core.getFlag("sc_usage_count", 0) >= 25, () => core.getFlag("sc_usage_count", 0) >= 50],
		[() => core.getFlag("hero_basic")[0].level >= 50, () => core.getFlag("hero_basic")[0].level >= 80, () => core.getFlag("hero_basic")[0].level >= 40],
		[() => core.getFlag("skillbook", 0) >= 3 && core.getFlag("hero_basic")[0].elite >= 1,
			() => core.getFlag("skillbook", 0) >= 8 && core.getFlag("hero_basic")[0].elite >= 2,
			() => core.getFlag("skillbook", 0) >= 15
		],
		[() => core.getFlag("hero_basic")[1].level >= 50, () => core.getFlag("hero_basic")[1].level >= 80, () => core.getFlag("hero_basic")[1].level >= 40],
		[() => core.getFlag("skillbook", 0) >= 3 && core.getFlag("hero_basic")[1].elite >= 1,
			() => core.getFlag("skillbook", 0) >= 8 && core.getFlag("hero_basic")[1].elite >= 2,
			() => core.getFlag("skillbook", 0) >= 15
		],
		[() => core.status.hero.mana >= 30, () => core.status.hero.mana >= 40, () => core.status.hero.mana >= 50],
		[() => core.status.hero.mana >= 20, () => core.status.hero.mana >= 40, () => core.status.hero.mana >= 60],
	];

	// 升级后的操作。
	// 假设当前想要升级的技能是第pos个，升级前等级为level。（注意是升级前）
	// 那么如果成功升级，将会执行removal[pos][level]()。
	var removal = [
		[() => {}, () => {}, () => {}],
		[() => {
				var heroes = core.getFlag("hero_basic");
				heroes[0].elite++;
				heroes[0].level = 1;
				core.setFlag("hero_basic", heroes);
				core.plugin.redispatch();
			},
			() => {
				var heroes = core.getFlag("hero_basic");
				heroes[0].elite++;
				heroes[0].level = 1;
				core.setFlag("hero_basic", heroes);
				core.plugin.redispatch();
			},
			() => {}
		],
		[() => { core.setFlag("skillbook", core.getFlag("skillbook") - 3); }, function () { core.setFlag("skillbook", core.getFlag("skillbook") - 8); }, function () { core.setFlag("skillbook", core.getFlag("skillbook") - 15); }],
		[() => {
				var heroes = core.getFlag("hero_basic");
				heroes[1].elite++;
				heroes[1].level = 1;
				core.setFlag("hero_basic", heroes);
				core.plugin.redispatch();
			},
			() => {
				var heroes = core.getFlag("hero_basic");
				heroes[1].elite++;
				heroes[1].level = 1;
				core.setFlag("hero_basic", heroes);
				core.plugin.redispatch();
			},
			() => {}
		],
		[() => { core.setFlag("skillbook", core.getFlag("skillbook") - 3); }, function () { core.setFlag("skillbook", core.getFlag("skillbook") - 8); }, function () { core.setFlag("skillbook", core.getFlag("skillbook") - 15); }],
		[() => { core.status.hero.mana -= 30; }, () => { core.status.hero.mana -= 40; }, () => { core.status.hero.mana -= 50; }],
		[() => {
			core.getItem("I334");
			core.status.hero.mana -= 20;
		}, () => { core.status.hero.mana -= 40; }, () => { core.status.hero.mana -= 60; }],
	];

	// 不必修改
	var current, old_current;
	var content = "",
		update_content = "";

	// 默认图片大小为32*32。如果不是，请自行修改下面的数据（包括绘图部分）
	this.inRect = function (px, py, rx, ry) {
		return (px >= rx) && (px <= rx + 32) && (py >= ry) && (py <= ry + 32);
	};

	this._drawEdge = function (x1, y1, x2, y2) {
		if (y1 < y2)
			core.drawLine("uievent", x1, y1 + 32, x2, y2, 'white');
		else
			core.drawLine("uievent", x1, y1, x2, y2 + 32, 'white');
	};

	this.get_predicates = () => predicates;
	this.get_removal = () => removal;

	this.tryUpdate = function (pos) {
		var level = core.getFlag("skill_level");
		var father = fathers[pos];
		if (father != -1 && level[father] != full_level[father])
			return;
		if (level[pos] == full_level[pos])
			return;
		if (!predicates[pos][level[pos]]())
			return;
		core.status.route.push("skilltree:" + pos);
		removal[pos][level[pos]]();
		level[pos] += 1;
		core.setFlag("skill_level", level);
		update_content = needed[images[pos][0]][level[pos]];
		content = description[images[pos][0]][level[pos]];
	};

	this.revealLevel = function (pos) {
		return core.getFlag("skill_level")[pos];
	};

	this.drawSkillTree = function () {
		core.ui._createUIEvent();
		core.clearMap('uievent');
		core.getContextByName('uievent').mozImageSmoothingEnabled = false;
		core.getContextByName('uievent').webkitImageSmoothingEnabled = false;
		core.getContextByName('uievent').msImageSmoothingEnabled = false;
		core.getContextByName('uievent').imageSmoothingEnabled = false;
		core.ui._uievent_drawBackground({ background: [0, 0, 0, 0.85], x: 0, y: 0, width: 416, height: 416 });
		for (var i in images) {
			if (images[i][3] != page)
				continue;
			core.drawImage("uievent", images[i][0], images[i][1], images[i][2], 32, 32);
			if (i == current) {
				var x = images[i][1],
					y = images[i][2];
				core.drawLine("uievent", x, y, x + 32, y, 'red');
				core.drawLine("uievent", x, y, x, y + 32, 'red');
				core.drawLine("uievent", x + 32, y, x + 32, y + 32, 'red');
				core.drawLine("uievent", x, y + 32, x + 32, y + 32, 'red');
			}
		}
		for (var i in edges) {
			if (images[edges[i][0]][3] != page)
				continue;
			this._drawEdge(images[edges[i][0]][1] + 16, images[edges[i][0]][2], images[edges[i][1]][1] + 16, images[edges[i][1]][2]);
		}
		core.ui._uievent_drawTextContent({ text: "离开", left: 5, top: 5 });
		core.ui._uievent_drawTextContent({ text: "上一页", left: 5, top: 265 });
		core.ui._uievent_drawTextContent({ text: "下一页", left: 361, top: 265 });
		core.ui._uievent_drawTextContent({ text: update_content, top: 285, maxWidth: 416, fontSize: 16, align: "center" });
		core.ui._uievent_drawTextContent({ text: "\r[#8A2BE2]" + images[current][4] + "：", left: 10, top: 305, fontSize: 16 });
		core.ui._uievent_drawTextContent({ text: content, left: 35, top: 325, maxWidth: 416 - 65, fontSize: 16 });

	};

	this._clickedSkill = function (px, py) {
		var level = core.getFlag("skill_level");
		if (px <= 55 && py <= 22) {
			core.insertAction({ "type": "break" });
			return;
		}
		if (px <= 55 && py <= 287 && py >= 265) {
			var old_page = page;
			for (var p = page - 1; p >= 0; p--)
				if (core.getFlag("usable", [])[p]) {
					page = p;
					break;
				}
			if (old_page != page) {
				current = defau[page];
				content = description[images[current][0]][level[current]];
				update_content = needed[images[current][0]][level[current]];
			}
		}
		if (px >= 360 && py <= 287 && py >= 265) {
			var old_page = page;
			for (var p = page + 1; p < maxpage; p++)
				if (core.getFlag("usable", [])[p]) {
					page = p;
					break;
				}
			if (old_page != page) {
				current = defau[page];
				content = description[images[current][0]][level[current]];
				update_content = needed[images[current][0]][level[current]];
			}
		}
		for (var i in images)
			if (this.inRect(px, py, images[i][1], images[i][2]) && images[i][3] == page) {
				old_current = current;
				current = i;
				content = description[images[i][0]][level[i]];
				update_content = needed[images[current][0]][level[current]];
				if (old_current == current)
					this.tryUpdate(i, false);
			}
		core.setFlag("skill_level", level);
	};

	this._keySkill = function (key) {
		var level = core.getFlag("skill_level");
		var fa = fathers[current];
		var i = 0;
		switch (key) {
		case 75:
		case 27: //esc/k
			core.insertAction({ "type": "break" });
			break;
		case 38: //up
			if (fathers[current] != -1) {
				old_current = current;
				current = fathers[current];
			}
			content = description[images[current][0]][level[current]];
			update_content = needed[images[current][0]][level[current]];
			break;
		case 40: //down
			if (sons[current] && sons[current][0] != -1) {
				old_current = current;
				current = sons[current][0];
			}
			content = description[images[current][0]][level[current]];
			update_content = needed[images[current][0]][level[current]];
			break;
		case 37: //left
			for (; i < sons[fa].length; i++)
				if (sons[fa][i] == current)
					break;
			var j = 1;
			for (; j < i; j++)
				if (images[sons[fa][i - j]][3] == images[current][3])
					break;
			if (sons[fa][i - j] && images[sons[fa][i - j]][3] == images[current][3]) {
				old_current = current;
				current = sons[fa][i - j];
				content = description[images[current][0]][level[current]];
				update_content = needed[images[current][0]][level[current]];
			}
			break;
		case 39: // right
			for (; i < sons[fa].length; i++)
				if (sons[fa][i] == current)
					break;
			var j = 1;
			for (; j < sons[fa].length - i + 1; j++)
				if (images[sons[fa][i + j]][3] == images[current][3])
					break;
			if (sons[fa][i + j] && images[sons[fa][i + j]][3] == images[current][3]) {
				old_current = current;
				current = sons[fa][i + j];
				content = description[images[current][0]][level[current]];
				update_content = needed[images[current][0]][level[current]];
			}
			break;
		case 65: // A
			var old_page = page;
			for (var p = page - 1; p >= 0; p--)
				if (core.getFlag("usable", [])[p]) {
					page = p;
					break;
				}
			if (old_page != page) {
				current = defau[page];
				content = description[images[current][0]][level[current]];
				update_content = needed[images[current][0]][level[current]];
			}
			break;
		case 68: // D
			var old_page = page;
			for (var p = page + 1; p < maxpage; p++)
				if (core.getFlag("usable", [])[p]) {
					page = p;
					break;
				}
			if (old_page != page) {
				current = defau[page];
				content = description[images[current][0]][level[current]];
				update_content = needed[images[current][0]][level[current]];
			}
			break;
		case 13:
		case 32: //enter/space
			this.tryUpdate(current);
			break;
		}
	};

	this.activateSkill = function () {
		if (flags.type == 0)
			this._keySkill(flags.keycode);
		else
			this._clickedSkill(flags.px, flags.py);
	};

	this.openSkillTree = function () {
		var level = core.getFlag("skill_level");
		for (var i in fathers) {
			var fa = fathers[i];
			if (!sons[fa])
				sons[fa] = [];
			sons[fa].push(i);
		}
		page = 0;
		current = defau[page];
		old_current = -1;
		update_content = needed[images[current][0]][level[current]];
		content = description[images[current][0]][level[current]];
		core.insertAction([{
				"type": "while",
				"condition": "true",
				"data": [
					{ "type": "function", "function": "function () { core.drawSkillTree(); }" },
					{ "type": "wait" },
					{ "type": "function", "function": "function() { core.activateSkill(); }" }
				]
			},
			{
				"type": "function",
				"function": "function () { " +
					"core.deleteCanvas('uievent');" +
					"core.clearUI(); " +
					"core.clearMap('uievent');" +
					"core.ui._uievent_drawSelector({ \"code\": 1 }); " +
					"core.ui._uievent_drawSelector({ \"code\": 2 }); " +
					"}"
			}
		]);
	};

},
    "1防减伤计算": function () {
	// 在此增加新插件
	ui.prototype._drawBook_drawRow3 = function (index, enemy, top, left, width, position) {
		// 绘制第三行
		core.setTextAlign('ui', 'left');
		var b13 = this._buildFont(13, true),
			f13 = this._buildFont(13, false);
		var col1 = left,
			col2 = left + width * 9 / 25,
			col3 = left + width * 17 / 25;
		core.fillText('ui', '临界', col1, position, '#DDDDDD', f13);
		core.fillText('ui', core.formatBigNumber(enemy.critical || 0), col1 + 30, position, null, b13);
		core.fillText('ui', '减伤', col2, position, null, f13);
		core.fillText('ui', core.formatBigNumber(enemy.criticalDamage || 0), col2 + 30, position, null, b13);
		//core.fillText('ui', '加防', col3, position, null, f13);
		core.fillText('ui', '1防', col3, position, null, f13);
		core.fillText('ui', core.formatBigNumber(enemy.defDamage || 0), col3 + 30, position, null, b13);
	}
	////// 1防减伤计算 //////
	enemys.prototype.getDefDamage = function (enemy, k, x, y, floorId) {
		if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
		k = k || 1;
		var nowDamage = this._getDamage(enemy, null, x, y, floorId);
		var nextDamage = this._getDamage(enemy, { "def": core.status.hero.def + k }, x, y, floorId);
		if (nowDamage == null || nextDamage == null) return "???";
		return nowDamage - nextDamage;
	}
	//防御倍数
	enemys.prototype._getCurrentEnemys_addEnemy_defDamage = function (enemy, x, y, floorId) {
		var ratio = core.status.maps[floorId || core.status.floorId].ratio || 1;
		//第一行为按照ratio值计算减防，第二行为1防减伤
		//return this.getDefDamage(enemy, ratio, x, y, floorId);
		return this.getDefDamage(enemy, null, x, y, floorId);
	}

},
    "通用函数": function () {// Utility.js
		// 通用函數插件
		// 本插件與古祠發佈的《功能插件 --- 实用功能函数集》不同，函數是定義在全域的。
		// 自訂常見事件模板插件(editorBlocklyconfigPlus.js)的前置插件
		
		/**
		 * 使js暫停指定時間
		 * async環境下await Sleep(500)
		 * @param {number} millisecond 暫停毫秒數
		 */
		self.Sleep = async function (millisecond) {
			return new Promise(resolve => setTimeout(resolve, millisecond));
		}
		
		/**
		 * 使js暫停一幀
		 * async環境下await SleepFrame()
		 */
		self.SleepFrame = async function () {
			return new Promise(resolve => requestAnimationFrame(resolve));
		}
		
		/**
		 * editor_file的isset函數
		 */
		self.isset = function (val) {
			if (val == undefined || val == null) {
				return false;
			}
			return true
		}
		
		/**
		 * editor_file的checkCallback函數
		 */
		self.checkCallback = function (callback) {
			if (!isset(callback)) {
				printe('未设置callback');
				throw ('未设置callback')
			}
		}},
    "自定义事件": function () {
		// editorBlocklyconfigPlus.js
// 自訂常見事件模板插件
// 本插件引用了通用函數插件(Utility.js)
// 適用樣板：2.10.3
// 請注意：
// 此插件對事件編輯器(editor_blocklyconfig)進行複寫，若還有其它針對事件編輯器做複寫的插件，請謹慎使用！
// 此插件對表格操作行為(editor_mode.doActionList)進行複寫，若還有其它對表格操作行為做複寫的插件，請謹慎使用！
// 使用方法：
// 現在在主頁下拉選單多了個常用事件模版，在那邊可以自由設定常用事件模板。
// 設定完後按F5刷新，再到事件編輯器看就有你設定好的常用事件模板了。

if (main.mode == "editor") {
    //#region 配置表格初始化
    let TableFileName = "project/table/CommonEventTemplate_comment.js";
    let TableRow = `
				var CommonEventTemplate_comment = {"_type": "object",
				"_data": {
					"CommonEventTemplate": {
						"_type": "object",
						"_data": function (key) {
							var obj = {
								"检测音乐如果没有开启则系统提示开启": {
									"_leaf": true,
									"_type": "event",
									"_event": "commonEvent",
									"_data": "检测音乐如果没有开启则系统提示开启"
								},
								"仿新新魔塔一次性商人": {
									"_leaf": true,
									"_type": "event",
									"_event": "commonEvent",
									"_data": "仿新新魔塔一次性商人"
								},
								"全地图选中一个点": {
									"_leaf": true,
									"_type": "event",
									"_event": "commonEvent",
									"_data": "全地图选中一个点"
								},
								"多阶段Boss战斗": {
									"_leaf": true,
									"_type": "event",
									"_event": "commonEvent",
									"_data": "多阶段Boss战斗"
								},
							}
							if (obj[key]) return obj[key];
							return {
								"_leaf": true,
								"_type": "event",
								"_event": "commonEvent",
								"_data": "常見事件模板"
							}
						}
					}
				}}
			`;
    if (!events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate) {
        /**
         * @type {{[EvnetName:actionParserJson]}}
         */
        events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate = {
            "检测音乐如果没有开启则系统提示开启": [
                {
                    "type": "if", "condition": "!core.musicStatus.bgmStatus",
                    "true": [
                        "\t[系统提示]你当前音乐处于关闭状态，本塔开音乐游戏效果更佳"
                    ],
                    "false": []
                }
            ],
            "仿新新魔塔一次性商人": [
                {
                    "type": "if",
                    "condition": "switch:A",
                    "true": [
                        "\t[行商,trader]\b[this]这是购买我的道具后我给玩家的提示。",
                        {
                            "type": "comment",
                            "text": "下一条指令可视情况使用或不使用"
                        },
                        {
                            "type": "hide",
                            "remove": true,
                            "time": 250
                        }
                    ],
                    "false": [
                        {
                            "type": "confirm",
                            "text": "我有3把黄钥匙，\n你出50金币就卖给你。",
                            "yes": [
                                {
                                    "type": "if",
                                    "condition": "status:money>=50",
                                    "true": [
                                        {
                                            "type": "setValue",
                                            "name": "status:money",
                                            "operator": "-=",
                                            "value": "50"
                                        },
                                        {
                                            "type": "setValue",
                                            "name": "item:yellowKey",
                                            "operator": "+=",
                                            "value": "3"
                                        },
                                        {
                                            "type": "playSound",
                                            "name": "确定",
                                            "stop": true
                                        },
                                        {
                                            "type": "setValue",
                                            "name": "switch:A",
                                            "value": "true"
                                        }
                                    ],
                                    "false": [
                                        {
                                            "type": "playSound",
                                            "name": "操作失败"
                                        },
                                        "\t[行商,trader]\b[this]你的金币不足！"
                                    ]
                                }
                            ],
                            "no": []
                        }
                    ]
                }
            ],
            "全地图选中一个点": [
                {
                    "type": "comment",
                    "text": "全地图选中一个点，需要用鼠标或触屏操作"
                },
                {
                    "type": "setValue",
                    "name": "temp:X",
                    "value": "status:x"
                },
                {
                    "type": "setValue",
                    "name": "temp:Y",
                    "value": "status:y"
                },
                {
                    "type": "tip",
                    "text": "再次点击闪烁位置确认"
                },
                {
                    "type": "while",
                    "condition": "true",
                    "data": [
                        {
                            "type": "drawSelector",
                            "image": "winskin.png",
                            "code": 1,
                            "x": "32*temp:X",
                            "y": "32*temp:Y",
                            "width": 32,
                            "height": 32
                        },
                        {
                            "type": "wait"
                        },
                        {
                            "type": "if",
                            "condition": "(flag:type === 1)",
                            "true": [
                                {
                                    "type": "if",
                                    "condition": "((temp:X===flag:x)&&(temp:Y===flag:y))",
                                    "true": [
                                        {
                                            "type": "break",
                                            "n": 1
                                        }
                                    ]
                                },
                                {
                                    "type": "setValue",
                                    "name": "temp:X",
                                    "value": "flag:x"
                                },
                                {
                                    "type": "setValue",
                                    "name": "temp:Y",
                                    "value": "flag:y"
                                }
                            ]
                        }
                    ]
                },
                {
                    "type": "drawSelector",
                    "code": 1
                },
                {
                    "type": "comment",
                    "text": "流程进行到这里可以对[X,Y]点进行处理，比如"
                },
                {
                    "type": "closeDoor",
                    "id": "yellowDoor",
                    "loc": [
                        "temp:X",
                        "temp:Y"
                    ]
                }
            ],
            "多阶段Boss战斗": [
                {
                    "type": "comment",
                    "text": "多阶段boss，请直接作为战后事件使用"
                },
                {
                    "type": "setValue",
                    "name": "switch:A",
                    "operator": "+=",
                    "value": "1"
                },
                {
                    "type": "switch",
                    "condition": "switch:A",
                    "caseList": [
                        {
                            "case": "1",
                            "action": [
                                {
                                    "type": "setBlock",
                                    "number": "redSlime"
                                },
                                "\t[2阶段boss,redSlime]\b[this]你以为你已经打败我了吗？没听说过史莱姆有九条命吗？"
                            ]
                        },
                        {
                            "case": "2",
                            "action": [
                                {
                                    "type": "setBlock",
                                    "number": "blackSlime"
                                },
                                "\t[3阶段boss,blackSlime]\b[this]不能消灭我的，只会让我更强大！"
                            ]
                        },
                        {
                            "case": "3",
                            "action": [
                                {
                                    "type": "setBlock",
                                    "number": "slimelord"
                                },
                                "\t[4阶段boss,slimelord]\b[this]我还能打！"
                            ]
                        },
                        {
                            "case": "4",
                            "action": [
                                "\t[4阶段boss,slimelord]我一定会回来的！"
                            ]
                        }
                    ]
                }
            ],
        };
    }
    //#endregion

    // 新增模板選項
    let editModeSelect = document.getElementById("editModeSelect");
    let newEditModeOption = document.createElement("option");
    newEditModeOption.value = "CommonEventTemplate";
    newEditModeOption.text = "常見事件模板";
    editModeSelect.add(newEditModeOption);

    //檢查可用的編輯模板ID
    let leftIDNumber = 11 - 1;
    let ExistLeftElement = document.querySelector(".main");
    while (ExistLeftElement) {
        leftIDNumber++;
        ExistLeftElement = document.getElementById(`left${leftIDNumber}`);
    }

    //新增編輯模板
    let MainDiv = document.querySelector('.main');

    let CommonEventTemplateMainDiv = document.createElement("div");
    CommonEventTemplateMainDiv.id = `left${leftIDNumber}`;
    CommonEventTemplateMainDiv.className = 'leftTab';
    CommonEventTemplateMainDiv.style.zIndex = '-1';
    CommonEventTemplateMainDiv.style.opacity = '0';

    CommonEventTemplateMainDiv.innerHTML = `
				<!-- CommonEventTemplate -->
				<h3 class="leftTabHeader">
				常見事件模板&nbsp;&nbsp;
				<button onclick="editor.mode.onmode('save')">保存</button>&nbsp;&nbsp;
				<button onclick="editor.table.CommonEventTemplateAddFunc()">添加</button>&nbsp;&nbsp;
				<button onclick="editor.mode.changeDoubleClickModeByButton('delete')">删除</button>&nbsp;&nbsp;
				<button onclick="editor_multi.CommonEventTemplateEditCommentJs('CommonEventTemplate')">配置表格</button>
				</h3>
				<div class="leftTabContent">
					<div class='etable'>
						<table>
							<tbody id='table_298572d8-93dd-4c6e-a278-6a7d49831e3a'>
							<tr>
								<td>条目</td>
								<td>注释</td>
								<td>值</td>
							</tr>
							</tbody>
						</table>
					</div>
				</div>
			`;
    MainDiv.appendChild(CommonEventTemplateMainDiv);

    (async function () {
        //等待編輯器初始化
        while (!editor_mode.ids) {
            await Sleep(100);
        }
        //新增編輯模板ID
        editor_mode.ids["CommonEventTemplate"] = `left${leftIDNumber}`;
        editor_mode.init_dom_ids();
        //切換至常見事件模板
        editor_mode.CommonEventTemplate = function (callback) {
            var objs = [];
            editor.file.editCommonEventTemplate([], function (objs_) {
                objs = objs_;
                //console.log(objs_)
            });
            //只查询不修改时,内部实现不是异步的,所以可以这么写
            var tableinfo = editor.table.objToTable(objs[0], objs[1]);
            document.getElementById('table_298572d8-93dd-4c6e-a278-6a7d49831e3a').innerHTML = tableinfo.HTML;
            tableinfo.listen(tableinfo.guids);
            if (Boolean(callback)) callback();
        }

        //檢查配置表格存在
        let TableRowExist = null;
        fs.readFile(TableFileName, 'base64', function (err, data) {
            if (err) {
                console.log(`察覺常見事件模板配置表格不存在，原因：${err}`);
                console.log("新建一個常見事件模板配置表格。");
                TableRowExist = false;
            }
            else {
                TableRowExist = true;
            }
        })
        //等待配置表格載入完畢(最多0.3秒，超過則視為失敗)
        for (let i = 0; i < 3; i++) {
            if (TableRowExist == null) {
                await Sleep(100);
            }
        }
        //配置表格初始化
        if (TableRowExist != true) {
            fs.mkdir("project/table", function (err, data) {
                if (err) throw `常見事件模板配置表格目錄初始化失敗，原因：${err}`;
            });
            fs.writeFile(TableFileName, editor.util.encode64(TableRow || ''), 'base64', function (err, data) {
                if (err) throw `常見事件模板配置表格文件初始化失敗，原因：${err}`;
            });
        }
        //載入配置表格
        //editor.file.loadCommentjs(callback);
        (function () {
            var key = "CommonEventTemplate_comment";
            var script = document.createElement('script');
            script.src = 'project/table/' + key + '.js';
            document.body.appendChild(script);
            script.onload = function () {
                editor.file[key] = eval(key.replace('.', '_'));
                var loaded = Boolean(editor.file[key]);
            }
        })();
        //按下配置表格
        editor_multi.CommonEventTemplateEditCommentJs = function (mod) {
            editor_multi.lintAutocomplete = true;
            editor_multi.setLint();
            editor_multi.importFile(TableFileName)
        }

        //定義表格操作行為
        editor_mode.OriginDoActionList = editor_mode.doActionList;
        editor_mode.doActionList = function (mode, actionList, callback) {
            if (editor_mode.mode == "CommonEventTemplate") {
                if (actionList.length == 0) return;
                printf('修改中...');
                var cb = function (objs_) {
                    if (objs_.slice(-1)[0] != null) {
                        printe(objs_.slice(-1)[0]);
                        throw (objs_.slice(-1)[0])
                    }
                    ;
                    var str = '修改成功！';
                    if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.name == 'template')
                        str += '<br/>请注意：全塔属性的name尚未修改，请及时予以设置。';
                    printf(str);
                    if (callback) callback();
                }
                editor.file.editCommonEventTemplate(actionList, cb);
            }
            else {
                editor_mode.OriginDoActionList(mode, actionList, callback);
            }
        }
        //添加表格列
        editor.table.CommonEventTemplateAddFunc = function () {
            let obj = events_c12a15a8_c380_4b28_8144_256cba95f760;

            // 1.输入id
            let newid = prompt('请输入新项的ID（支持中文）');
            if (newid == null || newid.length == 0) {
                return;
            }

            // 2.检查id是否符合规范或与已有id重复
            var conflict = true;
            var basefield = ("").replace(/\[[^\[]*\]$/, '');

            try {
                var baseobj = eval('obj' + basefield);
                conflict = newid in baseobj;
            } catch (ee) {
                // 理论上这里不会发生错误
                printe(ee);
                throw ee;
            }

            if (conflict) {
                printe('id已存在, 请直接修改该项的值');
                return;
            }

            // 3.添加 
            editor_mode.addAction(['add', basefield + "['" + newid + "']", null]);
            editor_mode.onmode('save', function () {
                printf('添加成功，刷新后生效；也可以继续新增其他项目。')
            });//自动保存 删掉此行的话点保存按钮才会保存
        }
        //對表格的存讀
        editor.file.editCommonEventTemplate = function (actionList, callback) {
            /*actionList:[
              ["change","['test']",['123']],
            ]
            为[]时只查询不修改
            */
            var data_obj = events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate;
            checkCallback(callback);
            if (isset(actionList) && actionList.length > 0) {
                actionList.forEach(function (value) {
                    value[1] = "['CommonEventTemplate']" + value[1];
                });
                editor.file.saveSetting('events', actionList, function (err) {
                    callback([err]);
                });
            } else {
                callback([
                    Object.assign({}, data_obj),
                    editor.file.CommonEventTemplate_comment._data.CommonEventTemplate,
                    null]);
            }
        }
    })();

    //複寫事件編輯器(editor_blocklyconfig)
    editor_blocklyconfig = (function () {
        // start mark sfergsvae


        (function () {
            var getCategory = function (name, custom) {
                for (var node of document.getElementById('toolbox').children) {
                    if (node.getAttribute('name') == name) return node;
                }
                var node = document.createElement('category');
                node.setAttribute('name', name);
                if (custom) node.setAttribute('custom', custom);
                document.getElementById('toolbox').appendChild(node);
                return node;
            }

            var toolboxObj = {
                '入口方块': [
                    '<label text="入口方块会根据当前类型在此数组中筛选,具体控制在editor_blockly.entranceCategoryCallback中"></label>',
                    MotaActionFunctions.actionParser.parse([
                        "欢迎使用事件编辑器",
                        "本事件触发一次后会消失",
                        { "type": "hide", "time": 500 },
                    ], 'event'),
                    MotaActionFunctions.actionParser.parse({
                        "condition": "flag:__door__===2",
                        "currentFloor": true,
                        "priority": 0,
                        "delayExecute": false,
                        "multiExecute": false,
                        "data": [
                            { "type": "openDoor", "loc": [10, 5] }
                        ],
                    }, 'autoEvent'),
                    MotaActionBlocks['changeFloor_m'].xmlText(),
                    MotaActionFunctions.actionParser.parse([{
                        "id": "shop1",
                        "text": "\t[贪婪之神,moneyShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以：",
                        "textInList": "1F金币商店",
                        "choices": [
                            {
                                "text": "生命+800", "need": "status:money>=20+2*flag:shop1", "action": [
                                    { "type": "comment", "text": "新版商店中需要手动扣减金币和增加访问次数" },
                                    { "type": "setValue", "name": "status:money", "operator": "-=", "value": "20+2*flag:shop1" },
                                    { "type": "setValue", "name": "flag:shop1", "operator": "+=", "value": "1" },
                                    { "type": "setValue", "name": "status:hp", "operator": "+=", "value": "800" }
                                ]
                            }
                        ]
                    }, {
                        "id": "itemShop",
                        "item": true,
                        "textInList": "道具商店",
                        "choices": [
                            { "id": "yellowKey", "number": 10, "money": 10 }
                        ]
                    }, {
                        "id": "keyShop1",
                        "textInList": "回收钥匙商店",
                        "commonEvent": "回收钥匙商店",
                        "args": ""
                    }], 'shop'),
                    MotaActionBlocks['common_m'].xmlText(),
                    MotaActionBlocks['beforeBattle_m'].xmlText(),
                    MotaActionBlocks['afterBattle_m'].xmlText(),
                    MotaActionBlocks['afterGetItem_m'].xmlText(),
                    MotaActionBlocks['afterOpenDoor_m'].xmlText(),
                    MotaActionBlocks['firstArrive_m'].xmlText(),
                    MotaActionBlocks['eachArrive_m'].xmlText(),
                    MotaActionBlocks['level_m'].xmlText(),
                    MotaActionFunctions.actionParser.parse([
                        ['MTx', '']
                    ], 'floorPartition'),
                    MotaActionBlocks['commonEvent_m'].xmlText(),
                    MotaActionBlocks['item_m'].xmlText(),
                    MotaActionFunctions.actionParser.parse([
                        {
                            "title": "简单", "name": "Easy", "hard": 1, "action": [
                                { "type": "comment", "text": "在这里写该难度需执行的事件" }
                            ]
                        }
                    ], 'levelChoose'),
                    MotaActionFunctions.actionParser.parse({
                        "type": 0, "value": { "atk": 10 }, "percentage": { "speed": 10 },
                    }, 'equip'),
                    MotaActionFunctions.actionParser.parse([{
                        "name": "bg.jpg", "x": 0, "y": 0, "canvas": "bg"
                    }], 'floorImage'),
                    MotaActionFunctions.actionParser.parse({
                        "time": 160, "openSound": "door.mp3", "closeSound": "door.mp3", "keys": { "yellowKey": 1, "orangeKey": 1 }
                    }, 'doorInfo'),
                    MotaActionBlocks['faceIds_m'].xmlText(),
                    MotaActionBlocks['mainStyle_m'].xmlText(),
                    MotaActionFunctions.actionParser.parse({
                        "背景音乐": "bgm.mp3", "确定": "confirm.mp3", "攻击": "attack.mp3", "背景图": "bg.jpg", "领域": "zone", "文件名": "file.jpg"
                    }, 'nameMap'),
                    MotaActionFunctions.actionParser.parse([
                        { "name": "hero.png", "width": 32, "height": 32, "prefix": "hero_" },
                    ], 'splitImages'),
                ],
                '显示文字': [
                    MotaActionBlocks['text_0_s'].xmlText(),
                    MotaActionBlocks['text_1_s'].xmlText(),
                    MotaActionFunctions.actionParser.parseList("\t[小妖精,fairy]\f[fairy.png,0,0]欢迎使用事件编辑器(双击方块可直接预览)"),
                    MotaActionBlocks['moveTextBox_s'].xmlText(),
                    MotaActionBlocks['clearTextBox_s'].xmlText(),
                    MotaActionBlocks['comment_s'].xmlText(),
                    MotaActionBlocks['autoText_s'].xmlText(),
                    MotaActionBlocks['scrollText_s'].xmlText(),
                    MotaActionBlocks['setText_s'].xmlText(),
                    MotaActionBlocks['tip_s'].xmlText(),
                    MotaActionBlocks['confirm_s'].xmlText(),
                    MotaActionBlocks['choices_s'].xmlText([
                        '选择剑或者盾', '流浪者', 'man', 0, '', MotaActionBlocks['choicesContext'].xmlText([
                            '剑', '', '', null, '', '', MotaActionFunctions.actionParser.parseList([{ "type": "openDoor", "loc": [3, 3] }]),
                        ])
                    ]),
                    MotaActionBlocks['win_s'].xmlText(),
                    MotaActionBlocks['lose_s'].xmlText(),
                    MotaActionBlocks['restart_s'].xmlText(),
                ],
                '数据相关': [
                    MotaActionBlocks['setValue_s'].xmlText([
                        MotaActionBlocks['idIdList_e'].xmlText(['status', '生命']), '=', '', false
                    ]),
                    MotaActionBlocks['setEnemy_s'].xmlText(),
                    MotaActionBlocks['setEnemyOnPoint_s'].xmlText(),
                    MotaActionBlocks['resetEnemyOnPoint_s'].xmlText(),
                    MotaActionBlocks['moveEnemyOnPoint_s'].xmlText(),
                    MotaActionBlocks['moveEnemyOnPoint_1_s'].xmlText(),
                    MotaActionBlocks['setEquip_s'].xmlText(),
                    MotaActionBlocks['setFloor_s'].xmlText(),
                    MotaActionBlocks['setGlobalAttribute_s'].xmlText(),
                    MotaActionBlocks['setGlobalValue_s'].xmlText(),
                    MotaActionBlocks['setGlobalFlag_s'].xmlText(),
                    MotaActionBlocks['setNameMap_s'].xmlText(),
                    MotaActionBlocks['input_s'].xmlText(),
                    MotaActionBlocks['input2_s'].xmlText(),
                    MotaActionBlocks['update_s'].xmlText(),
                    MotaActionBlocks['moveAction_s'].xmlText(),
                    MotaActionBlocks['changeFloor_s'].xmlText(),
                    MotaActionBlocks['changePos_s'].xmlText(),
                    MotaActionBlocks['battle_s'].xmlText(),
                    MotaActionBlocks['useItem_s'].xmlText(),
                    MotaActionBlocks['loadEquip_s'].xmlText(),
                    MotaActionBlocks['unloadEquip_s'].xmlText(),
                    MotaActionBlocks['openShop_s'].xmlText(),
                    MotaActionBlocks['disableShop_s'].xmlText(),
                    MotaActionBlocks['setHeroIcon_s'].xmlText(),
                    MotaActionBlocks['follow_s'].xmlText(),
                    MotaActionBlocks['unfollow_s'].xmlText(),
                ],
                '地图处理': [
                    MotaActionBlocks['battle_1_s'].xmlText(),
                    MotaActionBlocks['openDoor_s'].xmlText(),
                    MotaActionBlocks['closeDoor_s'].xmlText(),
                    MotaActionBlocks['show_s'].xmlText(),
                    MotaActionBlocks['hide_s'].xmlText(),
                    MotaActionBlocks['setBlock_s'].xmlText(),
                    MotaActionBlocks['setBlockOpacity_s'].xmlText(),
                    MotaActionBlocks['setBlockFilter_s'].xmlText(),
                    MotaActionBlocks['turnBlock_s'].xmlText(),
                    MotaActionBlocks['moveHero_s'].xmlText(),
                    MotaActionBlocks['move_s'].xmlText(),
                    MotaActionBlocks['jumpHero_s'].xmlText(),
                    MotaActionBlocks['jumpHero_1_s'].xmlText(),
                    MotaActionBlocks['jump_s'].xmlText(),
                    MotaActionBlocks['jump_1_s'].xmlText(),
                    MotaActionBlocks['showBgFgMap_s'].xmlText(),
                    MotaActionBlocks['hideBgFgMap_s'].xmlText(),
                    MotaActionBlocks['setBgFgBlock_s'].xmlText(),
                    MotaActionBlocks['showFloorImg_s'].xmlText(),
                    MotaActionBlocks['hideFloorImg_s'].xmlText(),
                ],
                '事件控制': [
                    MotaActionBlocks['if_1_s'].xmlText(),
                    MotaActionBlocks['if_s'].xmlText(),
                    MotaActionFunctions.actionParser.parseList({
                        "type": "switch", "condition": "判别值", "caseList": [
                            { "action": [{ "type": "comment", "text": "当判别值是值的场合执行此事件" }] },
                            { "case": "default", "action": [{ "type": "comment", "text": "当没有符合的值的场合执行default事件" }] },
                        ]
                    }),
                    MotaActionFunctions.actionParser.parseList({ "type": "for", "name": "temp:A", "from": "0", "to": "12", "step": "1", "data": [] }),
                    MotaActionFunctions.actionParser.parseList({ "type": "forEach", "name": "temp:A", "list": ["status:atk", "status:def"], "data": [] }),
                    MotaActionBlocks['while_s'].xmlText(),
                    MotaActionBlocks['dowhile_s'].xmlText(),
                    MotaActionBlocks['break_s'].xmlText(),
                    MotaActionBlocks['continue_s'].xmlText(),
                    MotaActionBlocks['exit_s'].xmlText(),
                    MotaActionBlocks['trigger_s'].xmlText(),
                    MotaActionBlocks['insert_1_s'].xmlText(),
                    MotaActionBlocks['insert_2_s'].xmlText(),
                ],
                '特效表现': [
                    MotaActionBlocks['sleep_s'].xmlText(),
                    MotaActionFunctions.actionParser.parseList({
                        "type": "wait", "timeout": 0, "data": [
                            { "case": "keyboard", "keycode": "13,32", "action": [{ "type": "comment", "text": "当按下回车(keycode=13)或空格(keycode=32)时执行此事件\n超时剩余时间会写入flag:timeout" }] },
                            { "case": "mouse", "px": [0, 32], "py": [0, 32], "action": [{ "type": "comment", "text": "当点击地图左上角时执行此事件\n超时剩余时间会写入flag:timeout" }] },
                            { "case": "condition", "condition": "flag:type==0\n&&flag:keycode==13", "action": [{ "type": "comment", "text": "当满足自定义条件时会执行此事件\n超时剩余时间会写入flag:timeout" }] },
                            { "case": "timeout", "action": [{ "type": "comment", "text": "当超时未操作时执行此事件" }] },
                        ]
                    }),
                    MotaActionBlocks['waitAsync_s'].xmlText(),
                    MotaActionBlocks['stopAsync_s'].xmlText(),
                    MotaActionBlocks['vibrate_s'].xmlText(),
                    MotaActionBlocks['animate_s'].xmlText(),
                    MotaActionBlocks['animate_1_s'].xmlText(),
                    MotaActionBlocks['stopAnimate_s'].xmlText(),
                    MotaActionBlocks['setViewport_s'].xmlText(),
                    MotaActionBlocks['setViewport_1_s'].xmlText(),
                    MotaActionBlocks['lockViewport_s'].xmlText(),
                    MotaActionBlocks['showStatusBar_s'].xmlText(),
                    MotaActionBlocks['hideStatusBar_s'].xmlText(),
                    MotaActionBlocks['setHeroOpacity_s'].xmlText(),
                    MotaActionBlocks['setCurtain_0_s'].xmlText(),
                    MotaActionBlocks['setCurtain_1_s'].xmlText(),
                    MotaActionBlocks['screenFlash_s'].xmlText(),
                    MotaActionBlocks['setWeather_s'].xmlText(),
                    MotaActionBlocks['callBook_s'].xmlText(),
                    MotaActionBlocks['callSave_s'].xmlText(),
                    MotaActionBlocks['autoSave_s'].xmlText(),
                    MotaActionBlocks['forbidSave_s'].xmlText(),
                    MotaActionBlocks['callLoad_s'].xmlText(),
                ],
                '音像处理': [
                    MotaActionBlocks['showImage_s'].xmlText(),
                    MotaActionBlocks['showImage_1_s'].xmlText(),
                    MotaActionBlocks['hideImage_s'].xmlText(),
                    MotaActionBlocks['showTextImage_s'].xmlText(),
                    MotaActionBlocks['moveImage_s'].xmlText(),
                    MotaActionBlocks['rotateImage_s'].xmlText(),
                    MotaActionBlocks['scaleImage_s'].xmlText(),
                    MotaActionBlocks['showGif_s'].xmlText(),
                    MotaActionBlocks['playBgm_s'].xmlText(),
                    MotaActionBlocks['pauseBgm_s'].xmlText(),
                    MotaActionBlocks['resumeBgm_s'].xmlText(),
                    MotaActionBlocks['loadBgm_s'].xmlText(),
                    MotaActionBlocks['freeBgm_s'].xmlText(),
                    MotaActionBlocks['playSound_s'].xmlText(),
                    MotaActionBlocks['playSound_1_s'].xmlText(),
                    MotaActionBlocks['stopSound_s'].xmlText(),
                    MotaActionBlocks['setVolume_s'].xmlText(),
                    MotaActionBlocks['setBgmSpeed_s'].xmlText(),
                ],
                'UI绘制': [
                    MotaActionBlocks['previewUI_s'].xmlText(),
                    MotaActionBlocks['clearMap_s'].xmlText(),
                    MotaActionBlocks['setAttribute_s'].xmlText(),
                    MotaActionBlocks['setFilter_s'].xmlText(),
                    MotaActionBlocks['fillText_s'].xmlText(),
                    MotaActionBlocks['fillBoldText_s'].xmlText(),
                    MotaActionBlocks['drawTextContent_s'].xmlText(),
                    MotaActionBlocks['fillRect_s'].xmlText(),
                    MotaActionBlocks['strokeRect_s'].xmlText(),
                    MotaActionBlocks['drawLine_s'].xmlText(),
                    MotaActionBlocks['drawArrow_s'].xmlText(),
                    MotaActionBlocks['fillPolygon_s'].xmlText(),
                    MotaActionBlocks['strokePolygon_s'].xmlText(),
                    MotaActionBlocks['fillEllipse_s'].xmlText(),
                    MotaActionBlocks['strokeEllipse_s'].xmlText(),
                    MotaActionBlocks['fillArc_s'].xmlText(),
                    MotaActionBlocks['strokeArc_s'].xmlText(),
                    MotaActionBlocks['drawImage_s'].xmlText(),
                    MotaActionBlocks['drawImage_1_s'].xmlText(),
                    MotaActionBlocks['drawIcon_s'].xmlText(),
                    MotaActionBlocks['drawBackground_s'].xmlText(),
                    MotaActionBlocks['drawSelector_s'].xmlText(),
                    MotaActionBlocks['drawSelector_1_s'].xmlText(),
                ],
                '原生脚本': [
                    MotaActionBlocks['function_s'].xmlText(),
                    MotaActionBlocks['unknown_s'].xmlText(),
                ],
                '值块': [
                    MotaActionBlocks['setValue_s'].xmlText([
                        MotaActionBlocks['idIdList_e'].xmlText(['status', '生命']), '=', '', false
                    ]),
                    MotaActionBlocks['expression_arithmetic_0'].xmlText(),
                    MotaActionBlocks['idFlag_e'].xmlText(),
                    MotaActionBlocks['idTemp_e'].xmlText(),
                    MotaActionBlocks['negate_e'].xmlText(),
                    MotaActionBlocks['unaryOperation_e'].xmlText(),
                    MotaActionBlocks['bool_e'].xmlText(),
                    MotaActionBlocks['idString_e'].xmlText(),
                    MotaActionBlocks['idIdList_e'].xmlText(),
                    MotaActionBlocks['idFixedList_e'].xmlText(),
                    MotaActionBlocks['enemyattr_e'].xmlText(),
                    MotaActionBlocks['blockId_e'].xmlText(),
                    MotaActionBlocks['blockNumber_e'].xmlText(),
                    MotaActionBlocks['blockCls_e'].xmlText(),
                    MotaActionBlocks['hasEquip_e'].xmlText(),
                    MotaActionBlocks['equip_e'].xmlText(),
                    MotaActionBlocks['nextXY_e'].xmlText(),
                    MotaActionBlocks['isReplaying_e'].xmlText(),
                    MotaActionBlocks['hasVisitedFloor_e'].xmlText(),
                    MotaActionBlocks['isShopVisited_e'].xmlText(),
                    MotaActionBlocks['canBattle_e'].xmlText(),
                    MotaActionBlocks['damage_e'].xmlText(),
                    MotaActionBlocks['damage_1_e'].xmlText(),
                    MotaActionBlocks['rand_e'].xmlText(),
                    MotaActionBlocks['evalString_e'].xmlText(),
                ],
                '常见事件模板': [
                    '<label text="此处只是占位符,实际定义在#region 動態常見事件模板"></label>',
                ],
                '最近使用事件': [
                    '<label text="此处只是占位符,实际定义在editor_blockly.searchBlockCategoryCallback中"></label>',
                ]
            }
            var toolboxgap = '<sep gap="5"></sep>'
            //xml_text = MotaActionFunctions.actionParser.parse(obj,type||'event')
            //MotaActionBlocks['idString_e'].xmlText()

            //#region 動態常見事件模板
            let CommonEventTemplateHTML = [];

            for (let commonEventName in events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate) {
                if (events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate.hasOwnProperty(commonEventName)) {
                    let actionParserJson = Array.from(events_c12a15a8_c380_4b28_8144_256cba95f760.CommonEventTemplate[commonEventName] ?? []);

                    let labelHTML = "";
                    let blockHTML = "";

                    labelHTML = `<label text="${commonEventName}"></label>`

                    if (actionParserJson.length > 1) {
                        actionParserJson = {
                            "type": "if", "condition": "true",
                            "true": actionParserJson
                        }
                    }
                    else if (actionParserJson.length < 1) {
                        actionParserJson = [
                            "空的常用事件模板。\n請在主頁下拉菜單中，選擇常用事件模板，進行編輯。\n編輯後需按F5刷新事件編輯器。",
                        ]
                    }
                    blockHTML = MotaActionFunctions.actionParser.parseList(actionParserJson);

                    CommonEventTemplateHTML.push(labelHTML);
                    CommonEventTemplateHTML.push(blockHTML);
                }
            }

            toolboxObj["常见事件模板"] = CommonEventTemplateHTML;
            //#endregion

            for (var name in toolboxObj) {
                var custom = null;
                if (name == '最近使用事件') custom = 'searchBlockCategory';
                if (name == '入口方块') custom = 'entranceCategory';
                getCategory(name, custom).innerHTML = toolboxObj[name].join(toolboxgap);
            }

            var blocklyArea = document.getElementById('blocklyArea');
            var blocklyDiv = document.getElementById('blocklyDiv');
            var workspace = Blockly.inject(blocklyDiv, {
                media: '_server/blockly/media/',
                toolbox: document.getElementById('toolbox'),
                zoom: {
                    controls: true,
                    wheel: false,//滚轮改为上下(shift:左右)翻滚
                    startScale: 1.0,
                    maxScale: 3,
                    minScale: 0.3,
                    scaleSpeed: 1.08
                },
                trashcan: false,
            });

            editor_blockly.isCommonEntry = function () {
                var commonEntries = ['beforeBattle', 'afterBattle', 'afterOpenDoor', 'firstArrive', 'eachArrive', 'commonEvent', 'item'];
                return commonEntries.indexOf(editor_blockly.entryType) >= 0;
            }

            editor_blockly.entranceCategoryCallback = function (workspace) {
                var list = toolboxObj['入口方块']
                var xmlList = [];
                var eventType = (editor_blockly.isCommonEntry() ? 'common' : editor_blockly.entryType) + '_m';
                for (var ii = 0, blockText; blockText = list[ii]; ii++) {
                    if (new RegExp('<block type="' + eventType + '">').exec(blockText)) {
                        var block = Blockly.Xml.textToDom('<xml>' + blockText + '</xml>').firstChild;
                        block.setAttribute("gap", 5);
                        xmlList.push(block);
                    }
                }
                return xmlList;
            }

            workspace.registerToolboxCategoryCallback(
                'entranceCategory', editor_blockly.entranceCategoryCallback);

            editor_blockly.searchBlockCategoryCallback = function (workspace) {
                var xmlList = [];
                var labels = editor_blockly.searchBlock();
                for (var i = 0; i < labels.length; i++) {
                    var blockText = '<xml>' +
                        MotaActionBlocks[labels[i]].xmlText() +
                        '</xml>';
                    var block = Blockly.Xml.textToDom(blockText).firstChild;
                    block.setAttribute("gap", 5);
                    xmlList.push(block);
                }
                return xmlList;
            };

            workspace.registerToolboxCategoryCallback(
                'searchBlockCategory', editor_blockly.searchBlockCategoryCallback);

            var onresize = function (e) {
                blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
                blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
                Blockly.svgResize(workspace);
            };
            if (typeof editor !== "undefined" && !editor.isMobile) window.addEventListener('resize', onresize, false);
            onresize();
            //Blockly.svgResize(workspace);

            //Blockly.bindEventWithChecks_(workspace.svgGroup_,"wheel",workspace,function(e){});
            document.getElementById('blocklyDiv').onmousewheel = function (e) {
                //console.log(e);
                e.preventDefault();
                var hvScroll = e.shiftKey ? 'hScroll' : 'vScroll';
                var mousewheelOffsetValue = 20 / 380 * workspace.scrollbar[hvScroll].handleLength_ * 3;
                workspace.scrollbar[hvScroll].handlePosition_ += (((e.deltaY || 0) + (e.detail || 0)) > 0 ? mousewheelOffsetValue : -mousewheelOffsetValue);
                workspace.scrollbar[hvScroll].onScroll_();
                // workspace.setScale(workspace.scale);
            }

            var doubleClickCheck = [[0, 'abc']];
            function omitedcheckUpdateFunction(event) {
                if (event.type === 'create') {
                    editor_blockly.addIntoLastUsedType(event.blockId);
                }
                if (event.type === 'ui' && event.element == 'click') {
                    var newClick = [new Date().getTime(), event.blockId];
                    var lastClick = doubleClickCheck.shift();
                    doubleClickCheck.push(newClick);
                    if (newClick[0] - lastClick[0] < 500) {
                        if (newClick[1] === lastClick[1]) {
                            editor_blockly.doubleClickBlock(newClick[1]);
                        }
                    }
                }
                // Only handle these events
                if (["create", "move", "change", "delete"].indexOf(event.type) < 0) return;
                if (editor_blockly.workspace.topBlocks_.length >= 2) {
                    editor_blockly.setValue('入口方块只能有一个');
                    return;
                }
                var eventType = editor_blockly.entryType;
                if (editor_blockly.workspace.topBlocks_.length == 1) {
                    var blockType = editor_blockly.workspace.topBlocks_[0].type;
                    if (blockType !== eventType + '_m' && !(editor_blockly.isCommonEntry() && blockType == 'common_m')) {
                        editor_blockly.setValue('入口方块类型错误');
                        return;
                    }
                }
                try {
                    var code = Blockly.JavaScript.workspaceToCode(workspace).replace(/\\(i|c|d|e|g|z)/g, '\\\\$1');
                    editor_blockly.setValue(code);
                } catch (error) {
                    editor_blockly.setValue(String(error));
                    if (error instanceof OmitedError) {
                        var blockName = error.blockName;
                        var varName = error.varName;
                        var block = error.block;
                    }
                    // console.log(error);
                }
            }

            workspace.addChangeListener(omitedcheckUpdateFunction);

            workspace.addChangeListener(Blockly.Events.disableOrphans);

            editor_blockly.workspace = workspace;

            MotaActionFunctions.workspace = function () {
                return editor_blockly.workspace;
            }

            // 因为在editor_blockly.parse里已经HTML转义过一次了,所以这里要覆盖掉以避免在注释中出现&lt;等
            MotaActionFunctions.xmlText = function (ruleName, inputs, isShadow, comment, collapsed, disabled) {
                var rule = MotaActionBlocks[ruleName];
                var blocktext = isShadow ? 'shadow' : 'block';
                var xmlText = [];
                xmlText.push('<' + blocktext + ' type="' + ruleName + '"' + (collapsed ? ' collapsed="true"' : '') + (disabled ? ' disabled="true"' : '') + '>');
                if (!inputs) inputs = [];
                for (var ii = 0, inputType; inputType = rule.argsType[ii]; ii++) {
                    var input = inputs[ii];
                    var _input = '';
                    var noinput = (input === null || input === undefined);
                    if (noinput && inputType === 'field' && MotaActionBlocks[rule.argsGrammarName[ii]].type !== 'field_dropdown') continue;
                    if (noinput && inputType === 'field') {
                        noinput = false;
                        input = rule.fieldDefault(rule.args[ii])
                    }
                    if (noinput) input = '';
                    if (inputType === 'field' && MotaActionBlocks[rule.argsGrammarName[ii]].type === 'field_checkbox') input = input ? 'TRUE' : 'FALSE';
                    if (inputType !== 'field') {
                        var subList = false;
                        var subrulename = rule.argsGrammarName[ii];
                        var subrule = MotaActionBlocks[subrulename];
                        if (subrule instanceof Array) {
                            subrulename = subrule[subrule.length - 1];
                            subrule = MotaActionBlocks[subrulename];
                            subList = true;
                        }
                        _input = subrule.xmlText([], true);
                        if (noinput && !subList && !isShadow) {
                            //无输入的默认行为是: 如果语句块的备选方块只有一个,直接代入方块
                            input = subrule.xmlText();
                        }
                    }
                    xmlText.push('<' + inputType + ' name="' + rule.args[ii] + '">');
                    xmlText.push(_input + input);
                    xmlText.push('</' + inputType + '>');
                }
                if (comment) {
                    xmlText.push('<comment>');
                    xmlText.push(comment);
                    xmlText.push('</comment>');
                }
                var next = inputs[rule.args.length];
                if (next) {//next
                    xmlText.push('<next>');
                    xmlText.push(next);
                    xmlText.push('</next>');
                }
                xmlText.push('</' + blocktext + '>');
                return xmlText.join('');
            }
        })();

        // end mark sfergsvae
    }).toString().split('// start mark sfergsvae')[1].split('// end mark sfergsvae')[0]
}
	},
    "sprites": function () {
	// 基于canvas的sprite化，摘编整理自万宁魔塔
	// 
	// ---------------------------------------- 第一部分 js代码 （必装） --------------------------------------- //

	/* ---------------- 用法说明 ---------------- *
	 * 1. 创建sprite: var sprite = new Sprite(x, y, w, h, z, reference, name);
	 *   其中x y w h为画布的横纵坐标及长宽，reference为参考系，只能填game（相对于游戏画面）和window（相对于窗口）
	 *   且当为相对游戏画面时，长宽与坐标将会乘以放缩比例（相当于用createCanvas创建）
	 *   z为纵深，表示不同元素之间的覆盖关系，大的覆盖小的
	 *   name为自定义名称，可以不填
	 * 2. 删除: sprite.destroy();
	 * 3. 设置css特效: sprite.setCss(css);
	 *   其中css直接填 box-shadow: 0px 0px 10px black;的形式即可，与style标签与css文件内写法相同
	 *   对于已设置的特效，如果之后不需要再次设置，可以不填
	 * 4. 添加事件监听器: sprite.addEventListener(); 用法与html元素的addEventListener完全一致
	 * 5. 移除事件监听器: sprite.removeEventListener(); 用法与html元素的removeEventListener完全一致
	 * 6. 属性列表
	 *   (1) sprite.x | sprite.y | sprite.width | sprite.height | sprite.zIndex | sprite.reference 顾名思义
	 *   (2) sprite.canvas 该sprite的画布
	 *   (3) sprite.context 该画布的CanvasRenderingContext2d对象，即样板中常见的ctx
	 *   (4) sprite.count 不要改这个玩意
	 * 7. 使用样板api进行绘制
	 *   示例：
	 *   var ctx = sprite.context;
	 *   core.fillText(ctx, 'xxx', 100, 100);
	 *   core.fillRect(ctx, 0, 0, 50, 50);
	 *   当然也可以使用原生js
	 *   ctx.moveTo(0, 0);
	 *   ctx.bezierCurveTo(50, 50, 100, 0, 100, 50);
	 *   ctx.stroke();
	 * ---------------- 用法说明 ---------------- */

	var count = 0;

	/** 创建一个sprite画布
	 * @param {number} x
	 * @param {number} y
	 * @param {number} w
	 * @param {number} h
	 * @param {number} z
	 * @param {'game' | 'window'} reference 参考系，游戏画面或者窗口
	 * @param {string} name 可选，sprite的名称，方便通过core.dymCanvas获取
	 */
	function Sprite(x, y, w, h, z, reference, name) {
		this.x = x;
		this.y = y;
		this.width = w;
		this.height = h;
		this.zIndex = z;
		this.reference = reference;
		this.canvas = null;
		this.context = null;
		this.count = 0;
		this.name = name || '_sprite_' + count;
		this.style = null;
		/** 初始化 */
		this.init = function () {
			if (reference === 'window') {
				var canvas = document.createElement('canvas');
				this.canvas = canvas;
				this.context = canvas.getContext('2d');
				canvas.width = w;
				canvas.height = h;
				canvas.style.width = w + 'px';
				canvas.style.height = h + 'px';
				canvas.style.position = 'absolute';
				canvas.style.top = y + 'px';
				canvas.style.left = x + 'px';
				canvas.style.zIndex = z.toString();
				document.body.appendChild(canvas);
				this.style = canvas.style;
			} else {
				this.context = core.createCanvas(this.name || '_sprite_' + count, x, y, w, h, z);
				this.canvas = this.context.canvas;
				this.canvas.style.pointerEvents = 'auto';
				this.style = this.canvas.style;
			}
			this.count = count;
			count++;
		}
		this.init();

		/** 设置css特效
		 * @param {string} css
		 */
		this.setCss = function (css) {
			css = css.replace('\n', ';').replace(';;', ';');
			var effects = css.split(';');
			var self = this;
			effects.forEach(function (v) {
				var content = v.split(':');
				var name = content[0];
				var value = content[1];
				name = name.trim().split('-').reduce(function (pre, curr, i, a) {
					if (i === 0 && curr !== '') return curr;
					if (a[0] === '' && i === 1) return curr;
					return pre + curr.toUpperCase()[0] + curr.slice(1);
				}, '');
				var canvas = self.canvas;
				if (name in canvas.style) canvas.style[name] = value;
			});
			return this;
		}

		/** 
		 * 移动sprite
		 * @param {boolean} isDelta 是否是相对位置，如果是，那么sprite会相对于原先的位置进行移动
		 */
		this.move = function (x, y, isDelta) {
			if (x !== undefined && x !== null) this.x = x;
			if (y !== undefined && y !== null) this.y = y;
			if (this.reference === 'window') {
				var ele = this.canvas;
				ele.style.left = x + (isDelta ? parseFloat(ele.style.left) : 0) + 'px';
				ele.style.top = y + (isDelta ? parseFloat(ele.style.top) : 0) + 'px';
			} else core.relocateCanvas(this.context, x, y, isDelta);
			return this;
		}

		/** 
		 * 重新设置sprite的大小
		 * @param {boolean} styleOnly 是否只修改css效果，如果是，那么将会不高清，如果不是，那么会清空画布
		 */
		this.resize = function (w, h, styleOnly) {
			if (w !== undefined && w !== null) this.w = w;
			if (h !== undefined && h !== null) this.h = h;
			if (reference === 'window') {
				var ele = this.canvas;
				ele.style.width = w + 'px';
				ele.style.height = h + 'px';
				if (!styleOnly) {
					ele.width = w;
					ele.height = h;
				}
			} else core.resizeCanvas(this.context, w, h, styleOnly);
			return this;
		}

		/**
		 * 旋转画布
		 */
		this.rotate = function (angle, cx, cy) {
			if (this.reference === 'window') {
				var left = this.x;
				var top = this.y;
				this.canvas.style.transformOrigin = (cx - left) + 'px ' + (cy - top) + 'px';
				if (angle === 0) {
					canvas.style.transform = '';
				} else {
					canvas.style.transform = 'rotate(' + angle + 'deg)';
				}
			} else {
				core.rotateCanvas(this.context, angle, cx, cy);
			}
			return this;
		}

		/**
		 * 清除sprite
		 */
		this.clear = function (x, y, w, h) {
			if (this.reference === 'window') {
				this.context.clearRect(x, y, w, h);
			} else {
				core.clearMap(this.context, x, y, w, h);
			}
			return this;
		}

		/** 删除 */
		this.destroy = function () {
			if (this.reference === 'window') {
				if (this.canvas) document.body.removeChild(this.canvas);
			} else {
				core.deleteCanvas(this.name || '_sprite_' + this.count);
			}
		}

		/** 添加事件监听器 */
		this.addEventListener = function () {
			this.canvas.addEventListener.apply(this.canvas, arguments);
		}

		/** 移除事件监听器 */
		this.removeEventListener = function () {
			this.canvas.removeEventListener.apply(this.canvas, arguments);
		}
	}

	window.Sprite = Sprite;
},
    "平面楼传": function () {
		// 在此增加新插件
		// 该插件可自定义空间很大，自定义内容请看注释
	
		// ------------------------- 安装说明 ------------------------- //
		// 先安装基于canvas的sprite化插件（2.10.0以上自带）
		// 确保自己的编辑器已安装造塔群内的编辑器升级压缩包（在HTML5魔塔样板文件夹内，2.10.1以上样板自带）
		// 再将以下代码复制进插件中
		// 提供的api请看以this.xxx = function开头的函数，函数前会有函数说明及参数说明，调用时只需core.plugin.xxx(参数)即可
	
		// ------------------------- 使用说明 ------------------------- //
		/*
		 * 直接复制进插件中，然后添加一个快捷键或道具效果为core.plugin.drawFlyMap()即可使用，不需额外设置
		 * 楼层id中不要出现下划线
		 * 该插件具体功能有：
		 * 1.绘制区域内的地图
		 * 2.可以拖动地图
		 * 3.点击地图可直接传送至目标地图，同时降低背景的不透明度，方便观察
		 * 4.滚轮或双指可以放缩绘制内容
		 * 5.放缩较大时，绘制地图的缩略图，可能会比较卡，但移动不会卡
		 * 6.整合漏怪检测，如果想忽略怪物，请在下方改动或用脚本修改core.plugin.ignoreEnemies，类型为数组
		 * 7.整合区域显示，所有单独或连在一起的地图会被视为一个区域
		 * 8.键盘操作，上下左右移动
		 */
	
		// ------------------------- 插件说明 ------------------------- //
		/*
		 * 该插件注释极其详细，可以帮助那些想要提升代码力，但实力有不足的作者
		 * 注意！！！该插件难度极大，没有代码底力的不建议研究
		 * 该插件涉及部分较为高级的算法，如bfs
		 */
		if (core.flags.flyRecordPosition&&core.getFlag("平面楼传",true)) {
			// 录像验证直接干掉这个插件
			if (main.replayChecking || main.mode === 'editor') return;
			if (!core.flags.flyRecordPosition) return;
			// ----- 不可自定义 杂七杂八的变量
			/** @type {{[x: string]: BFSResult}} */
			let mapCache = {}; // 地图缓存
			let drawCache = {}; // 绘制信息缓存
			let status = 'none'; // 当前的绘制状态
			/** @type {{[x: string]: Sprite}} */
			let sprites = {}; // 当前所有的sprite
			/** @type {{[x: string]: Sprite}} */
			let canDrag = {}; // 可以拖拽的sprite
			/** @type {{[x: string]: Button}} */
			let areaSprite = {}; // 区域列表对应的sprite
			let clicking = false; // 是否正在点击，用于拖拽判定
			let drawingMap = ''; // 正在绘制的中心楼层
			let nowScale = 1; // 当前绘制的放缩比例
			let lastTouch = {}; // 上一次的单点点击信息
			let lastLength = 0; // 手机端缩放时上一次的两指间距离
			let nowDepth = 0; // 当前的遍历深度
			let drawedThumbnail = {}; // 已经绘制过的缩略图
			let moved = false; // 鼠标按下后是否移动了
			let noBorder = true; // 是否是无边框拼接模式
			let lastScale = 0; // 上一次缩放，用于优化缩略图绘制
			let showEnemy = true; // 是否显示漏怪
			let areaPage = 0; // 区域显示的当前页数
			let nowArea = 0; // 当前区域index
			let selecting = ''; // 选择时当前正在选择的地图
	
			// ---- 不可自定义，常量
			/** @type {Area} */
			let areas = []; // 区域信息
			const perPage = Math.floor((core._PY_ - 60) / 30); // 区域的每页显示数量
	
			// ---- 可自定义，默认的切换地图的图块id
			const defaultChange = {
				left: 'leftPortal', // 左箭头
				up: 'upPortal', // 上箭头
				right: 'rightPortal', // 右箭头
				down: 'downPortal', // 下箭头
				upFloor: 'upFloor', // 上楼
				downFloor: 'downFloor' // 下楼
			};
			// ---- 可自定义，默认数值
			const defaultValue = {
				font: 'beeB', // 默认字体
				scale: 9, // 默认地图缩放比例
				depth: Infinity, // 默认的遍历深度
			};
	
			// ---- 不可自定义，计算数据
			const dirData = {
				up: [1, 0],
				down: [-1, 0],
				left: [0, 1],
				right: [0, -1],
				upFloor: [0, 0],
				downFloor: [0, 0]
			}
	
			let ignoreEnemies = this.ignoreEnemies = [];
	
			let allChangeEntries = Object.entries(defaultChange);
	
			const reset = core.events.resetGame;
			core.events.resetGame = function () {
				reset.apply(core.events, arguments);
				areas = [];
				// 获取所有分区，使用异步函数，保证不会卡顿
				// 原理是用bfs扫，将所有连在一起的地图合并成一个区域
				(async function () {
					let all = core.floorIds.slice();
					const scanned = {
						[all[0]]: true
					};
					while (all.length > 0) {
						let now = all.shift();
						if(now==="MT0"||now==="Final2"||now==="Final3"||now==="MTGuard"||now==="MT18")continue;//不扫描隐藏层
						if(core.getFlag("6区")===true){if(core.status.maps[now].ratio!==6)continue;}else{if(core.status.maps[now].ratio===6)continue;}
						if (core.status.maps[now].deleted) continue;
						if (!now) return;
						await new Promise(res => {
							const result = bfsSearch(now, Infinity, true);
							mapCache[`${now}_Infinity_false`] = result;
							areas.push({ name: core.floors[now].title, maps: result.order });
							for (const map of result.order) {
								scanned[map] = true;
								all = all.filter(v => !result.order.includes(v));
							}
							res('success');
						});
					}
				})();
			}
	
			/** 工具按钮 */
			class Button extends Sprite {
				constructor(name, x, y, w, h, text, fontSize = '20px', transition = true) {
					const btn = super(x, y, w, h, 1050, 'game', name);
					this.css(transition);
					setTimeout(() => btn.setCss(`opacity: 1;`), 50);
					const ctx = btn.context;
					core.setFont('beeB');
					ctx.textAlign = 'center';
					ctx.textBaseline = 'middle';
					//core.fillText(ctx, text, w / 2, h / 2, '#fff', `${fontSize} normal`, w - 10);
					core.fillText(ctx, text, w / 2, h / 2, '#fff', "15px beeB", w - 10);
					sprites[name] = btn;
				}
	
				css(transition) {
					this.setCss(
						'transition: opacity 0.6s linear, transform 0.2s linear;' +
						'font_family: "beeB";' +
						'background-color: #111111;' +
						'box-shadow: 0px 0px 0px black;' +
						(transition ? 'opacity: 0;' : '') +
						'filter: drop-shadow(1px 1px 2px black);' +
						'box-shadow: 0px 0px 4px black;' +
						'cursor: pointer;'
					);
				}
			}
	
			/** 背景 */
			class Back extends Sprite {
				constructor(name, x, y, w, h, z, color) {
					const sprite = super(x, y, w, h, z, 'game', name);
					sprites[name] = sprite;
					this.setCss(`transition: all 0.6s linear;`);
					setTimeout(() => {
						this.setCss(`background-color: ${color};`);
					}, 50);
				}
			}
	
			/** 
			 * 获取绘制信息
			 * @param {string?} center 中心地图id
			 * @param {number?} depth 搜索深度
			 * @param {boolean?} noCache 是否不使用缓存
			 * @returns {MapDrawInfo}
			 */
			this.getMapDrawInfo = function (center = core.status.floorId, depth = defaultValue.depth, noCache = false) {
				nowDepth = depth;
				drawingMap = center;
				const id = `${center}_${depth}_${noBorder}`;
				// 检查缓存
				if (drawCache[id] && !noCache) return drawCache[id];
				const map = bfsSearch(center, depth, noCache);
				mapCache[id] = map;
				const res = getDrawInfo(map.res, center, map.order);
				res.upOrDown = map.upOrDown;
				drawCache[id] = res;
				return res;
			}
	
			/** 
			 * 绘制大地图，可拖动、滚轮缩放、点击对应位置可以楼传等
			 * @param {string} floorId 中心地图的id
			 * @param {number} depth 遍历深度
			 * @param {boolean} noCache 是否不使用缓存
			 * @param {number} scale 绘制的缩放比例
			 */
			this.drawFlyMap = function (floorId = core.status.floorId,
				depth = defaultValue.depth, noCache = false, scale = defaultValue.scale) {
	
				if (core.isReplaying()) return;
	
				// 把区域页码归零
				nowArea = areas.findIndex(v => v.maps.includes(core.status.floorId));
				areaPage = 0;
				nowScale = scale;
				selecting = floorId;
				const info = this.getMapDrawInfo(floorId, depth, noCache);
				if (status !== 'scale' && status !== 'border') {
					drawBack();
					drawTools();
				}
				drawMap(info, scale);
				status = 'flyMap';
				core.lockControl();
				core.canvas.data.canvas.style.zIndex = '990';
			}
	
			/**
			 * 获得某个区域的剩余怪物
			 * @param {string} floorId 区域包含的地图或要扫描的地图
			 * @param {boolean} area 是否扫描整个区域
			 * @returns {RemainEnemy} 怪物总数、所在地图、位置
			 * 返回值格式：{
			 *  rough: 每种怪物的数量及所有怪物的总数，为字符串，每个怪物独占一行
			 *  detail: 每个怪物的所在位置，每个怪物独占一行，以每20个整合成字符串，为字符串数组形式
			 *  data: 怪物数量的原始信息，格式为{ 楼层id: { 'x,y': 怪物id } }
			 * }
			 */
			this.getRemainEnemy = function (floorId = core.status.floorId, area = false) {
				const res = bfsSearch(floorId, Infinity, true);
				// 整合怪物总数
				/** @type {{[x: string]: number}} */
				const category = {};
				const toShow = area ? res.order : [floorId];
				const strArr = [];
				const add = (...num) => num.reduce((pre, cur) => pre + cur, 0);
				const name = (id) => core.material.enemys[id].name;
				const title = (id) => core.status.maps[id].title;
				for (const id of toShow) {
					const enemies = res.enemies[id];
					Object.values(enemies).forEach(v => {
						// 编辑器不支持 ??=，悲
						category[v] = category[v] ?? 0;
						category[v]++;
					})
					// 每个怪物的信息
					strArr.push(...Object.entries(enemies)
						.map(v => `${name(v[1])}    楼层:${title(id)},楼层id:${id},坐标:${v[0]}`));
				}
				// 输出字符串
				const all = `当前${area ? '区域' : '地图'}中剩余怪物数量：${add(...Object.values(category))}`;
				const classified = Object.entries(category).map(v => `${name(v[0])} × ${v[1]}`).join`\n`;
				const detail = [];
				while (strArr.length > 0) {
					detail.push(strArr.splice(0, 20).join`\n`);
				}
				return { rough: `${all}\n${classified}`, detail, data: res.enemies };
			}
	
			/** 
			 * 广度优先搜索搜索地图路径
			 * @param {string} center 中心地图的id
			 * @param {number} depth 搜索深度
			 * @param {boolean} noCache 是否不使用缓存
			 * @returns {BFSResult} 格式：floorId_x_y_dir: floorId_x_y
			 */
			function bfsSearch(center, depth, noCache) {
				// 检查缓存
				const id = `${center}_${depth}_${noBorder}`;
				if (mapCache[id] && !noCache) return mapCache[id];
				const used = {
					[center]: true
				}; // 搜索过的楼层
				let queue = [];
				let stack = [center]; // 当前栈
				let nowDepth = -1;
				const mapOrder = [center]; // 遍历顺序，顺便还能记录遍历了哪些楼层
	
				const res = {}; // 输出结果，格式：floorId_x_y_dir: floorId_x_y
				const enemies = {};
				const upOrDown = {};
	
				// 开始循环搜索
				while (nowDepth < depth && stack.length > 0) {
					const now = stack.shift(); // 当前id
					if (core.status.maps[now].deleted) continue;
					const blocks = core.getMapBlocksObj(now); // 获取当前地图的每点的事件
					enemies[now] = {};
					// 遍历，获取可以传送的点，只检测绿点事件，因此可用红点事件进行传送来实现分区功能
					for (const i in blocks) {
						const block = blocks[i];
						// 整合漏怪检测，所以要检测怪物
						if (block.event.trigger === 'battle') {
							const id = block.event.id;
							if (ignoreEnemies.includes(id)) continue;
							else enemies[now][i] = block.event.id;
							continue;
						}
						// 检测触发器是否为切换楼层，不是则直接跳过
						if (block.event.trigger !== 'changeFloor') continue;
						const dirEntries = allChangeEntries.find(v => v[1] === block.event.id);
						// 如果不是那六种传送门，直接忽略
						if (!dirEntries) continue;
						const data = block.event.data;
						const dir = dirEntries[0];
						const route = `${now}_${i.replace(',', '_')}_${dir}`;
						const target = `${data.floorId}_${data.loc.join('_')}`;
						if (!used[data.floorId]) {
							if (dir === 'upFloor' || dir === 'downFloor') {
								upOrDown[now] = upOrDown[id] ?? [];
								upOrDown[now].push(dir);
							}
							queue.push(data.floorId); // 没有搜索过，则加入栈中
							mapOrder.push(data.floorId);
							used[data.floorId] = true;
						}
						res[route] = target;
					}
					if (stack.length === 0) {
						stack = queue;
						queue = [];
						nowDepth++;
					}
					if (stack.length === 0 && queue.length === 0) break;
				}
				return { res, order: mapOrder, enemies, upOrDown };
			}
	
			/**
			 * 提供地图的绘制信息
			 * @param {{[x: string]: string}} map 要绘制的地图，格式：floorId_x_y_dir: floorId_x_y
			 * @param {string} center 中心地图的id
			 * @param {string[]} order 遍历顺序
			 * @returns {MapDrawInfo} 地图的绘制信息
			 */
			function getDrawInfo(map, center, order) {
				// 先根据地图id分类，从而确定每个地图连接哪些地图，同时方便处理
				const links = {};
				for (const i in map) {
					const splitted = i.split('_');
					const id = splitted[0]
					if (!links[id]) links[id] = {};
					links[id][i] = map[i];
				}
				// 分类完毕，然后根据连接点先计算出各个地图的坐标，然后再进行判断
				const centerFloor = core.status.maps[center];
				const visitedCenter = core.hasVisitedFloor(center);
				const locs = { // 格式：[中心x, 中心y, 宽, 高, 是否到达过]
					[center]: [0, 0, centerFloor.width, centerFloor.height, visitedCenter]
				};
				const lines = {}; // 地图间的连线
				// 可以上楼下楼的地图
				const upOrDown = {};
				for (const id of order) {
					const now = links[id];
					// 遍历每一个地图的连接情况
					for (const from in now) {
						const to = now[from];
						// 先根据from to计算物理位置
						const fromData = from.split('_'),
							toData = to.split('_');
						const dir = fromData[3];
						if (dir === 'upFloor' || dir === 'downFloor') continue;
						if (!defaultChange[dir]) continue;
						const v = dirData[dir][1], // 竖直数值
							h = dirData[dir][0], // 水平数值
							ha = Math.abs(h),
							va = Math.abs(v);
						const fx = parseInt(fromData[1]), // fromX
							fy = parseInt(fromData[2]), // fromY
							tx = parseInt(toData[1]), // toX
							ty = parseInt(toData[2]), // toY
							ff = id, // fromFloorId
							tf = toData[0]; // toFloorId
						const fromFloor = core.status.maps[ff],
							toFloor = core.status.maps[tf];
						const fhw = Math.floor(fromFloor.width / 2), // fromFloorHalfWidth
							fhh = Math.floor(fromFloor.height / 2),
							thw = Math.floor(toFloor.width / 2),
							thh = Math.floor(toFloor.height / 2);
						const fLoc = locs[id] ?? [0, 0];
						if (!locs[ff]) continue;
						let x, y;
						const dis = noBorder ? 1 : 5;
						if (locs && locs[tf]) {
							x = locs[tf][0];
							y = locs[tf][1];
						} else {
							// 计算坐标，公式可以通过画图推断出
							x = fLoc[0] - ha * (fhw - fx + tx - thw) - v * (fhw + thw + dis);
							y = fLoc[1] - va * (fhh - fy + ty - thh) - h * (fhh + thh + dis);
						};
						locs[tf] = locs[tf] ?? [x, y, toFloor.width, toFloor.height, core.hasVisitedFloor(tf)];
						// 添加连线
						lines[`${from}_${to}`] = [
							[
								fx - fhw + locs[ff][0],
								fy - fhh + locs[ff][1],
								x + tx - thw, y + ty - thh
							]
						];
					}
				}
				// 获取地图绘制需要的长宽
				let width = 0,
					height = 0;
				let left, right, up, down;
				for (const id in locs) {
					const [x, y, w, h] = locs[id];
					if (left === void 0) {
						left = right = x;
						up = down = y;
					}
					left = Math.min(x - w / 2 - 1, left);
					right = Math.max(x + w / 2 + 1, right);
					up = Math.min(y - h / 2 - 1, up);
					down = Math.max(y + h / 2 + 1, down);
				}
				width = right - left;
				height = down - up;
				// 所有地图和连线向右下移动，避免绘制出现问题
				for (const id in locs) {
					const loc = locs[id];
					loc[0] -= left; // 这时候left和up是负值，所以要减
					loc[1] -= up;
				}
				for (const route in lines) {
					const line = lines[route];
					for (const node of line) {
						node[0] -= left;
						node[1] -= up;
						node[2] -= left;
						node[3] -= up;
					}
				}
	
				return { locs, lines, width, height, layer: upOrDown };
			}
	
			/** 绘制背景 */
			function drawBack() {
				if (status !== 'none') return;
				new Back('__map_back__', 0, 0, core._PX_, core._PY_, 175, 'rgba(0, 0, 0, 0.9)');
				const listen = new Sprite(0, 0, core._PX_, core._PY_, 1000, 'game', '__map_listen__');
				addDrag(listen);
				const exit = new Button('__map_exit__', core._PX_ - 64, core._PY_ - 26, 60, 22, '退出');
				exit.addEventListener('click', close);
				sprites.listen = listen;
			}
	
			/** 绘制工具栏 */
			function drawTools() {
				new Back('__map_toolback__', 0, core._PY_ - 30, core._PX_, 30, 600, 'rgba(9, 22, 30, 0.75)');
				// 无边框
				const border = new Button('__map_border__', core._PX_ - 150, core._PY_ - 26, 60, 22, '边框');
				border.addEventListener('click', changeBorder);
				// 怪物数量
				const enemy = new Button('__map_enemy__', core._PX_ - 240, core._PY_ - 26, 60, 22, '怪物');
				enemy.addEventListener('click', triggerEnemy);
				// 区域显示
				//const area = new Back('__map_areasback__', core._PX_ - 80, 0, 80, core._PY_ - 30, 550, 'rgba(9, 22, 30, 0.75)');
				//drawAreaList();
				//core.drawLine(area.context, 0, core._PY_ - 30, 80, core._PY_ - 30, '#222', 2);
			}
	
			/** 绘制区域列表 */
			function drawAreaList(transition = true) {
				const start = perPage * areaPage;
				Object.values(areaSprite).forEach(v => v.destroy());
				areaSprite = {};
				for (let i = start; i < start + perPage && areas[i]; i++) {
					const n = i % perPage;
					const { name, maps } = areas[i];
					const btn = new Button(`_area_${maps[0]}`, core._PX_ - 75, 4 + 30 * n, 70, 22, name, '16px', transition);
					areaSprite[maps[0]] = btn;
					if (i === nowArea) btn.setCss(`border: 2px solid gold;`);
					btn.addEventListener('click', e => {
						if (i === nowArea) return;
						changeArea(i);
					})
				}
				// 上一页下一页
				if (areaPage > 0) {
					const last = new Button('_area_last_', core._PX_ - 75, core._PY_ - 50, 30, 16, '上一页', '14px', transition);
					areaSprite._area_last_ = last;
					last.addEventListener('click', e => {
						areaPage--;
						drawAreaList(false);
					});
				}
				if (areaPage < Math.floor(areas.length / perPage)) {
					const next = new Button('_area_next_', core._PX_ - 35, core._PY_ - 50, 30, 16, '下一页', '14px', transition);
					areaSprite._area_next_ = next;
					next.addEventListener('click', e => {
						areaPage++;
						drawAreaList(false);
					});
				}
			}
	
			/** 
			 * 绘制大地图
			 * @param {MapDrawInfo} info 地图绘制信息
			 * @param {number} scale 地图的绘制比例
			 */
			function drawMap(info, scale = defaultValue.scale) {
				if (status === 'flyMap') return;

				const center_loc = info.locs[selecting]
				const [center_x, center_y, center_w, center_h] = center_loc.map(v => typeof v === 'number' && v * scale);

				const PX = core._PX_,
					PY = core._PY_;
				const w = info.width * scale,
					h = info.height * scale;
				const id = `__flyMap__`;
				const cx = PX / 2 - center_x,
					cy = PY / 2 - center_y;
				const map = new Sprite(cx, cy, w, h, 500, 'game', id);
				sprites[id] = map;
				canDrag[id] = map;
				map.canvas.className = 'fly-map';
				const ctx = map.context;
				core.clearMap(ctx);
				if (!noBorder) {
					const drawed = {}; // 绘制过的线
					// 先绘制连线
					const lines = info.lines;
					for (const route in lines) {
						const line = lines[route];
						for (const node of line) {
							const from = `${node[0]},${node[1]}`,
								to = `${node[2]},${node[3]}`;
							if (drawed[`${from}-${to}`] || drawed[`${to}-${from}`]) continue;
							drawed[`${from}-${to}`] = true;
							let lineWidth = scale / 2;
							core.drawLine(ctx, node[0] * scale, node[1] * scale, node[2] * scale, node[3] * scale, '#fff', lineWidth);
						}
					}
					// 再绘制楼层
					const locs = info.locs;
					for (const id in locs) {
						const loc = locs[id];
						let color = '#000';
						if (!loc[4]) color = '#0c1c25';
						const [x, y, w, h] = loc.map(v => typeof v === 'number' && v * scale);
						let dx = 0,
							dy = 0; // 避免绘图误差
						if (loc[2] % 2 === 0) dx = 0.5 * scale;
						if (loc[3] % 2 === 0) dy = 0.5 * scale;
						const fx = x - w / 2 - dx,
							fy = y - h / 2 - dy;
						core.fillRect(ctx, fx, fy, w, h, color);
						if (id === selecting) core.strokeRect(ctx, fx, fy, w, h, '#77FFDC', scale / 2);
						else core.strokeRect(ctx, fx, fy, w, h, '#fff', scale / 2);
						const layer = info.upOrDown[id];
						const min = Math.min(w, h);
						if (layer?.includes('upFloor'))
							core.drawIcon(ctx, defaultChange.upFloor, fx, fy, min / 3, min / 3);
						if (layer?.includes('downFloor'))
							core.drawIcon(ctx, defaultChange.downFloor, fx + w - min / 3, fy + h - min / 3, min / 3, min / 3);
						// 显示漏怪数量
						if (showEnemy) {
							ctx.textAlign = 'center';
							ctx.textBaseline = 'middle';
							const c = `${drawingMap}_${nowDepth}_${noBorder}`;
							const n = Object.keys(mapCache[c].enemies[id]).length;
							color = '#3f3';
							if (n > 0) color = '#eee';
							if (n > 10) color = '#bdf';
							if (n > 20) color = '#6ac';
							ctx.shadowBlur = 0.6 * nowScale;
							ctx.shadowColor = '#000';
							core.fillText(ctx, `怪物剩余：${n}`, x, y, color, `${2 * nowScale}px beeB`);
							ctx.shadowBlur = 0;
						}
					}
				}
				checkThumbnail();
			}
	
			/** 
			 * 重新绘制缩略图
			 * @param {Sprite} sprite
			 * @param {string} floor
			 */
			function drawThumbnail(sprite, floor, x, y, w, h) {
				const ctx = sprite.context;
				const scale = nowScale;
				core.plugin.drawThumbnail(floor, void 0, {
					ctx: ctx,
					x: x - w / 2,
					y: y - h / 2,
					damage: true,
					all: true,
					size: Math.max(w, h) / Math.max(core._PX_, core._PY_),
					fromMap: true
				});
				const color = floor === core.status.floorId ? '#77FFDC' : '#eee'
				if (!noBorder)
					core.strokeRect(ctx, x - w / 2, y - h / 2, w, h, color, scale / 2);
			}
	
			/** 检查是否需要绘制缩略图 */
			function checkThumbnail() {
				const id = `${drawingMap}_${nowDepth}_${noBorder}`;
				const locs = drawCache[id].locs;
				const map = canDrag[`__flyMap__`];
				for (const id in locs) {
					const loc = locs[id];
					const scale = nowScale;
					const [x, y, w, h] = loc.map(v => typeof v === 'number' && v * scale);
					let dx = 0,
						dy = 0; // 避免绘图误差
					if (loc[2] % 2 === 0) dx = 0.5 * scale;
					if (loc[3] % 2 === 0) dy = 0.5 * scale;
					if (!drawedThumbnail[id] && x + map.x > 0 && x + map.x < core._PX_ &&
						y + map.y > 0 && y + map.y < core._PY_) {
						if (!noBorder && core.hasVisitedFloor(id) && scale > 5) {
							drawThumbnail(map, id, x - dx, y - dy, w, h);
							drawedThumbnail[id] = true;
						}
						if (noBorder) {
							drawThumbnail(map, id, x - dx, y - dy, w, h)
							drawedThumbnail[id] = true;
							if (!core.hasVisitedFloor(id))
								core.fillRect(map.context, x - dx - w / 2, y - dy - h / 2, w, h, 'rgba(45, 110, 150,0.2)');
						}
					}
				}
				// 如果是无边框模式，那就只绘制当前地图的边框
				if (noBorder) {
					const loc = locs[selecting];
					const scale = nowScale;
					if (loc) {
						const [x, y, w, h] = loc.map(v => typeof v === 'number' && v * scale);
						core.strokeRect(map.context, x - w / 2, y - h / 2, w, h, '#77FFDC', scale / 2);
					}
				}
			}
	
			/** 检查点击点是否在以x,y为中心的某一矩形中 */
			function inRect(x, y, w, h, px, py) {
				x -= w / 2;
				y -= h / 2;
				return px > x && px < x + w && py > y && py < y + h;
			}
	
			/** 测试画布是否超过上限，摘自https://github.com/jhildenbiddle/canvas-size */
			function canvasTest(size) {
				const width = Math.max(Math.ceil(size[0]), 1);
				const height = Math.max(Math.ceil(size[1]), 1);
				if (width === 0 || height === 0) return true;
				const fill = [width - 1, height - 1, 1, 1];
				let cropCvs, testCvs;
				cropCvs = document.createElement("canvas");
				cropCvs.width = 1;
				cropCvs.height = 1;
				testCvs = document.createElement("canvas");
				testCvs.width = width;
				testCvs.height = height;
				const cropCtx = cropCvs.getContext("2d");
				const testCtx = testCvs.getContext("2d");
				if (testCtx) {
					testCtx.fillRect.apply(testCtx, fill);
					cropCtx.drawImage(testCvs, width - 1, height - 1, 1, 1, 0, 0, 1, 1);
				}
				const isTestPass = cropCtx && cropCtx.getImageData(0, 0, 1, 1).data[3] !== 0;
				return isTestPass;
			}
	
			/** 检查浏览器限制 */
			function checkMaximum(before, scale) {
				for (const id in canDrag) {
					const sprite = canDrag[id];
					const rate = scale / before;
					const w = sprite.width * rate * core.domStyle.scale,
						h = sprite.height * rate * core.domStyle.scale;
					const valid = canvasTest([w, h]);
					if (!valid) {
						core.drawTip('画布大小将超过浏览器限制！请勿继续放大！');
						return true;
					}
				}
				return false;
			}
	
			/** 关闭事件 */
			function close() {
				document.body.removeEventListener('keyup', keyboard);
				Object.values(sprites).forEach((v) => {
					v.setCss('transition: opacity 0.6s linear;');
				});
				setTimeout(() => {
					Object.values(sprites).forEach((v) => {
						v.setCss('opacity: 0;');
					});
				}, 50);
				setTimeout(() => {
					core.unlockControl();
					Object.values(sprites).forEach((v) => {
						v.destroy();
					});
					drawedThumbnail = {};
					sprites = {};
					canDrag = {};
					status = 'none';
					core.canvas.data.canvas.style.zIndex = '170';
				}, 650);
			}
	
			/** 
			 * 点击地图事件，尝试楼层传送
			 * @param {MouseEvent} e
			 */
			function clickMap(e) {
				if (moved) return moved = false;
				const { x, y } = core.actions._getClickLoc(e.clientX, e.clientY);
				let px = x / core.domStyle.scale,
					py = y / core.domStyle.scale;
				const scale = nowScale;
				const id = `${drawingMap}_${nowDepth}_${noBorder}`;
				const locs = drawCache[id].locs;
				const sprite = canDrag.__flyMap__;
				px -= sprite.x;
				py -= sprite.y;
				for (const id in locs) {
					const loc = locs[id];
					const [x, y, w, h] = loc.map(v => typeof v === 'number' && v * scale);
					if (inRect(x, y, w, h, px, py)) {
						return flyTo(id);
					}
				}
			}
	
			/** 飞向某个楼层 */
			function flyTo(id) {
				if (!core.hasItem('fly')) return core.drawTip('你没有楼层传送器');
				sprites.__map_back__.setCss('opacity: 0.2;');
close();
				return core.flyTo(id, () => setTimeout(() => {
					if (sprites.__map_back__) core.lockControl();
				}, 100));
			}
	
			/** 
			 * 拖拽事件
			 * @param {MouseEvent} e
			 */
			function drag(e) {
				if (!clicking) return;
				const scale = core.domStyle.scale
				moveEle(e.movementX / scale, e.movementY / scale);
			}
	
			/**
			 * 手机端点击拖动事件
			 * @param {TouchEvent} e
			 * @this {HTMLCanvasElement}
			 */
			function touchDrag(e) {
				moved = true;
				const scale = core.domStyle.scale;
				if (e.touches.length === 1) { // 拖拽
					const info = e.touches[0];
					if (!lastTouch[this.id]) {
						lastTouch[this.id] = [info.clientX, info.clientY];
						return;
					}
					const { clientX: x, clientY: y } = info;
					const dx = x - lastTouch[this.id][0],
						dy = y - lastTouch[this.id][1];
					moveEle(dx / scale, dy / scale);
					lastTouch[this.id] = [info.clientX, info.clientY];
				} else if (e.touches.length >= 2) { // 双指放缩
					const first = e.touches[0],
						second = e.touches[1];
					const dx = first.clientX - second.clientX,
						dy = first.clientY - second.clientY;
					if (lastLength === 0) {
						lastLength = Math.sqrt(dx * dx + dy * dy);
						return;
					}
					let cx = (first.clientX + second.clientX) / 2,
						cy = (first.clientY + second.clientY) / 2;
					const { x, y } = core.actions._getClickLoc(cx, cy);
					const mx = x / scale;
					const my = y / scale;
					const length = Math.sqrt(dx * dx + dy * dy);
					const delta = 1/((lastLength / length) ** (1/ 32));
					const info = {};
					for (const id in canDrag) {
						const sprite = canDrag[id];
						const sx = sprite.x + sprite.width / 2,
							sy = sprite.y + sprite.height / 2;
						const dx = sx - mx,
							dy = sy - my;
						info[id] = [mx + dx * delta, my + dy * delta];
					}
					scaleMap(delta * nowScale, info);
				}
			}
	
			/** 
			 * 滚轮缩放
			 * @param {WheelEvent} e
			 */
			function wheel(e) {
				const delta = 1 - Math.sign(e.deltaY) / 10;
				const { x, y } = core.actions._getClickLoc(e.clientX, e.clientY);
				const scale = core.domStyle.scale;
				const mx = x / scale,
					my = y / scale;
				const info = {};
				for (const id in canDrag) {
					const sprite = canDrag[id];
					const cx = sprite.x + sprite.width / 2,
						cy = sprite.y + sprite.height / 2;
					const dx = cx - mx,
						dy = cy - my;
					info[id] = [mx + dx * delta, my + dy * delta];
				}
				scaleMap(delta * nowScale, info);
			}
	
			/** 切换边框 */
			function changeBorder() {
				noBorder = !noBorder;
				redraw('border');
			}
	
			/** 切换是否显示漏怪数量 */
			function triggerEnemy() {
				showEnemy = !showEnemy;
				redraw('enemy');
			}
	
			/** 改变区域 */
			function changeArea(index) {
				nowArea = index;
				drawAreaList(false);
				drawedThumbnail = {};
				status = 'area';
				nowScale = defaultValue.scale;
				drawMap(core.plugin.getMapDrawInfo(areas[index].maps[0]));
			}
	
			/** 重绘 */
			function redraw(id, px, py, move = true) {
				const { x, y } = canDrag.__flyMap__;
				status = id;
				drawedThumbnail = {};
				drawMap(core.plugin.getMapDrawInfo(drawingMap, nowDepth, true), nowScale);
				if (move) canDrag.__flyMap__.move(px ?? x, py ?? y);
				checkThumbnail();
			}
	
			/** 
			 * 拖拽时移动需要元素
			 * @param {string} dx
			 * @param {string} dy
			 */
			function moveEle(dx, dy) {
				moved = true;
				for (const id in canDrag) {
					const sprite = canDrag[id];
					const ctx = sprite.context;
					sprite.x += dx;
					sprite.y += dy;
					core.relocateCanvas(ctx, dx, dy, true);
				}
				checkThumbnail();
			}
	
			/** 
			 * 缩放绘制地图
			 * @param {number} target 目标缩放比例
			 * @param {{[x: string]: [number, number]}} info 缩放后的sprite位置数据
			 */
			function scaleMap(target, info) {
				// 检查浏览器限制
				if (checkMaximum(nowScale, target)) return;
				clearTimeout(lastScale);
				const [x, y] = info.__flyMap__;
				// 先直接修改style，延迟200ms再绘制，进行性能优化
				const sprite = canDrag.__flyMap__;
				const rate = target / nowScale;
				nowScale = target;
				sprite.resize(sprite.width * rate, sprite.height * rate, true);
				sprite.move(x - sprite.width / 2, y - sprite.height / 2);
				lastScale = setTimeout(() => {
					redraw('scale', x - sprite.width / 2, y - sprite.height / 2);
				}, 200)
			}
	
			/** 键盘操作
			 * @param {KeyboardEvent} e
			 */
			function keyboard(e) {
				if (e.key === 'Enter' || e.key === 'C' || e.key === 'c' || e.key === ' ') {
					return flyTo(selecting);
				} else if (e.key === 'Escape' || e.key === 'x' || e.key === 'X') {
					return close();
				} else if (e.key.startsWith('Arrow')) {
					const dir = e.key.slice(5).toLowerCase();
					// 获取目标楼层
					const res = mapCache[`${drawingMap}_${nowDepth}_${noBorder}`].res;
					const key = Object.keys(res)
						.find(v => {
							const [floorId, x, y, d] = v.split('_');
							return floorId === selecting && d === dir;
						});
					if (!key) return;
					const target = res[key].split('_')[0];
					selecting = target;
					redraw('key');
				}
			}
	
			/**
			 * 给需要的元素添加拖拽等事件
			 * @param {HTMLCanvasElement} ele
			 */
			function addDrag(ele) {
				ele.addEventListener('wheel', wheel);
				ele.addEventListener('mousemove', drag);
				ele.addEventListener('touchmove', touchDrag);
				ele.addEventListener('click', clickMap);
				ele.addEventListener('mousedown', () => { clicking = true; });
				ele.addEventListener('mouseup', () => { clicking = false; });
				ele.addEventListener('touchend', () => {
					lastTouch = {};
					lastLength = 0;
				});
				document.body.addEventListener('keyup', keyboard);
			}
	core.plugin.drawThumbnail = function (floorId, blocks, options) {
    floorId = floorId || core.status.floorId;
    if (!floorId) return;
    options = options || {};
    if (typeof options == 'string' || options.canvas) options = {ctx: options};
    var ctx = options.ctx;
    // Step1：绘制到tempCanvas上
    core.plugin._drawThumbnail_drawTempCanvas(floorId, blocks, options);
    options.ctx = ctx;
    // Step2：从tempCanvas绘制到对应的画布上
    core.plugin._drawThumbnail_drawToTarget(floorId, options);
}
core.plugin._drawThumbnail_drawTempCanvas = function (floorId, blocks, options) {
    var width = core.floors[floorId].width;
    var height = core.floors[floorId].height;
    // 绘制到tempCanvas上面
    var tempCanvas = core.bigmap.tempCanvas;

    // 如果是大地图模式？
    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;
    }

    core.maps._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);
}

			this._drawThumbnail_drawToTarget = function (floorId, options) {
				const ctx = core.getContextByName(options.ctx);
				if (ctx == null) return;
				const x = options.x || 0,
					y = options.y || 0,
					size = options.size || 1;
				// size的含义改为(0,1]范围的系数以适配长方形，默认为1，楼传为3/4，SL界面为0.3
				let w = Math.ceil(size * core._PX_),
					h = Math.ceil(size * core._PY_);
				// 特判是否为编辑器，编辑器中长宽均采用core.js的遗留正方形像素边长，以保证下面的绘制正常
				if (main.mode == 'editor') w = h = size * core.__PIXELS__;
				const width = core.floors[floorId].width,
					height = core.floors[floorId].height;
				let centerX = options.centerX,
					centerY = options.centerY;
				if (centerX == null) centerX = Math.floor(width / 2);
				if (centerY == null) centerY = Math.floor(height / 2);
				const tempCanvas = core.bigmap.tempCanvas;
	
				if (options.all) {
					const tempWidth = tempCanvas.canvas.width,
						tempHeight = tempCanvas.canvas.height;
					// 绘制全景图
					if (tempWidth <= tempHeight) {
						const realHeight = h,
							realWidth = realHeight * tempWidth / tempHeight;
						const side = (w - realWidth) / 2;
						if (options.fromMap) {
							return core.drawImage(ctx, tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x, y, realWidth, realHeight);
						}
						core.fillRect(ctx, x, y, side, realHeight, '#000000');
						core.fillRect(ctx, x + w - side, y, side, realHeight);
						core.drawImage(ctx, tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x + side, y, realWidth, realHeight);
					} else {
						const realWidth = w,
							realHeight = realWidth * tempHeight / tempWidth;
						const side = (h - realHeight) / 2;
						if (options.fromMap) {
							return core.drawImage(ctx, tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x, y, realWidth, realHeight);
						}
						core.fillRect(ctx, x, y, realWidth, side, '#000000');
						core.fillRect(ctx, x, y + h - side, realWidth, side);
						core.drawImage(ctx, tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x, y + side, realWidth, realHeight);
					}
				} else {
					// 只绘制可见窗口
					let pw = core._PX_,
						ph = core._PY_,
						hw = core._HALF_WIDTH_,
						hh = core._HALF_HEIGHT_,
						W = core._WIDTH_,
						H = core._HEIGHT_;
					const ratio = core.domStyle.isVertical ? core.domStyle.ratio : core.domStyle.scale;
					if (main.mode == 'editor') {
						pw = ph = core.__PIXELS__;
						hw = hh = core.__HALF_SIZE__;
						W = H = core.__SIZE__;
					}
					if (options.v2) {
						core.drawImage(ctx, tempCanvas.canvas, 0, 0, pw * ratio, ph * ratio, x, y, w, h);
					} else {
						const offsetX = core.clamp(centerX - hw, 0, width - W),
							offsetY = core.clamp(centerY - hh, 0, height - H);
						if (options.noHD) {
							core.drawImage(ctx, tempCanvas.canvas, offsetX * 32, offsetY * 32, pw, ph, x, y, w, h);
							return;
						}
						core.drawImage(ctx, tempCanvas.canvas, offsetX * 32 * ratio, offsetY * 32 * ratio, pw * ratio, ph * ratio, x, y, w, h);
					}
				}
			}
		}
	},
    "动态血量": function () {
	// 此插件允许人物血量动态进行变化
	// 原作：Fux2（老黄鸡）

	// 是否开启本插件，默认禁用；将此改成 true 将启用本插件。
	var __enable = true;
	if (!__enable) return;

	var speed = 0.05; // 动态血量变化速度，越大越快。

	var _currentHp = null;
	var _lastStatus = null;
	var _check = function () {
		if (_lastStatus != core.status.hero) {
			_lastStatus = core.status.hero;
			_currentHp = core.getRealStatus('hp');
		}
	}

	core.registerAnimationFrame('dynamicHp', true, function () {
		_check();
		if (core.status.hero.hp != _currentHp) {
			var dis = (_currentHp - core.status.hero.hp) * speed;
			if (Math.abs(dis) < 2) {
				_currentHp = core.getRealStatus('hp');
			} else {
				_currentHp -= dis;
			}
			core.setFlag('hp', _currentHp);
			core.updateStatusBar();
		}
	});
},
    "怪显": function () {
	// 在此增加新插件
	/* 插件名：移动鼠标显示怪物信息
	 * 插件作者：霸道的老鼠
	 * 使用案例：作者的2个塔
	 * 感谢 @古祠 和 @艾之葵 的帮助。
	 * 
	 * 插件作用：在电脑上，把鼠标移动到怪物处，直接查看怪物信息。
	 * 
	 * 适配下列情况：
	 * 手机端不会出错；不会炸录像，但是手机播放录像过快会黑屏。
	 * 适配2.7.3及以上的样板；
	 * 适用于大地图、32*48图块、没有怪物手册、大额金币经验、楼层转换、
	 * 怪物多属性、战斗后怪物死亡、其他游戏属性这些情况。
	 * 
	 * 使用指南：
	 * 第一步：
	 * 在本插件标注"......"的2个地方填上注释提示的信息，
	 * 并根据实际情况和注释comment和uncomment语句。
	 * 第二步：
	 * 在游戏初始化或者你想加入的地方把useEnemyInfoDisplay设置为true。
	 * 
	 * 就是这么多啦，祝你造塔顺利哦！o(*￣▽￣*)ブ
	 * 如果发现有bug的话请在大群或造塔群@霸道的老鼠，或者联系作者qq:1622587044。
	 */


	//如果检测到怪物，这个子程序给绘制初始化
	this.startEnemyInfoDisplay = function (enemy_x, enemy_y) {
		//我们要提前获得特殊属性以判断窗口高度
		var mon_id = core.getBlockId(enemy_x, enemy_y);

		var special = core.getSpecialText(core.getEnemyInfo(mon_id, null, enemy_x, enemy_y, core.status.floorId), enemy_x, enemy_y, core.status.floorId);
		var specialColor = core.getSpecialColor(core.getEnemyInfo(mon_id, null, enemy_x, enemy_y, core.status.floorId), enemy_x, enemy_y, core.status.floorId);
		var width = 120;
		//bigEnemy变量的作用是检测48像素高的怪物并进行特定绘制
		if (core.getBlockCls(enemy_x, enemy_y) == 'enemy48')
			var bigEnemy = 16;
		else
			var bigEnemy = 0;
		//extra_line检测过长的怪物属性
		var extra_line = 0;
		for (let i = 0; i < special.length - 1; i += 2)
			if (special[i].length > 4 || (i + 1 < special.length && special[i + 1].length > 4)) extra_line += 1;
		//高度控制。这里只给了正常情况。如果需要绘制境界等其他属性，把下面一行
		//加//注释掉，然后删除下面的/*和*/启用注释的内容，填写"......"，每个属性20像素。
		var height = 140 + Math.ceil(special.length / 2 + extra_line) * 18;
		//var height = 168 + Math.ceil(special.length / 2 + extra_line) * 20 + 20 * "......";  //你需要绘制的行数
		//下面是计算窗口绘制起始点，本程序考虑了大地图和怪物在地图边缘的情况。
		var cell_x = (enemy_x + 0.5) * 32 - core.bigmap.offsetX;
		var cell_y = (enemy_y + 0.5) * 32 - core.bigmap.offsetY;
		if (cell_x > 32 * core.__SIZE__ - width) cell_x -= width;
		if (cell_y > 32 * core.__SIZE__ - height) cell_y -= height + bigEnemy;
		//获得怪物属性
		var info = core.getEnemyInfo(mon_id, null, enemy_x, enemy_y, core.status.floorId);
		//在下面设置你的字体和字体大小
		var font = "Arial"; //这里"......"填写你的字体。如果不修改的话，默认Arial字体。
		var fontSize = 12;
		//绘制窗口
		core.plugin.addBasics(width, height, cell_x, cell_y, info, special, fontSize, mon_id, font, bigEnemy);
		var i = core.plugin.addSpecial(width, cell_x, cell_y + bigEnemy, special, specialColor, mon_id, fontSize, font);
		core.plugin.addEnemyInfo(i, cell_x, cell_y + bigEnemy, mon_id, fontSize, width, font);
	}

	//这个子程序用来绘制怪物的基本信息
	this.addBasics = function (width, height, cell_x, cell_y, info, special, fontSize, mon_id, font, bigEnemy) {
		//绘制窗口外观
		core.fillRoundRect("enemyInfo", cell_x, cell_y, width, height + bigEnemy, 5, [0, 0, 0, 0.7]);
		core.strokeRoundRect("enemyInfo", cell_x, cell_y, width, height + bigEnemy, 5, [255, 255, 255, 1], 2);
		//绘制抬头、怪物图标和怪物名称
		core.fillText("enemyInfo", "敌方信息", cell_x + width / 2 - 4 * fontSize / 2, cell_y + 17,
			[255, 255, 255, 1], "Bold " + fontSize + "px " + font);
		core.drawIcon('enemyInfo', mon_id, cell_x + width / 2 - 16, cell_y + 25);
		core.fillText("enemyInfo", core.getEnemyValue(mon_id, "name", cell_x, cell_y, core.status.floorId), cell_x + width / 2 - core.getEnemyValue(mon_id, "name", cell_x, cell_y, core.status.floorId).length * fontSize / 8 -
			core.getEnemyValue(mon_id, "name", cell_x, cell_y, core.status.floorId).replace(/[()]/g, '').length * fontSize / 8 - core.getEnemyValue(mon_id, "name", cell_x, cell_y, core.status.floorId).replace(/[-_ ()0-9a-zA-Z]/g, '').length *
			fontSize / 4, cell_y + 68 + bigEnemy, [255, 255, 255, 1], fontSize + "px " + font);
	}

	//这个子程序调整字体的亮度
	this.changeColor = function (color, Increment) {
		var r = parseInt(color.slice(1, 3), 16);
		var g = parseInt(color.slice(3, 5), 16);
		var b = parseInt(color.slice(5, 7), 16);
		//I代表灰度
		var I = (r + g + b) / 3 + 0.001;
		//PS亮度算法
		var I_new = I + Increment - 128.0;
		var r_new = core.clamp(Math.round(r + (256.0 - r) * I_new / 128.0), 0, 255);
		var g_new = core.clamp(Math.round(g + (256.0 - g) * I_new / 128.0), 0, 255);
		var b_new = core.clamp(Math.round(b + (256.0 - b) * I_new / 128.0), 0, 255);
		r = r_new.toString(16);
		g = g_new.toString(16);
		b = b_new.toString(16);
		color = "#" + r + g + b;
		return color;
	}

	//这个子程序用来绘制怪物的特殊属性
	this.addSpecial = function (width, cell_x, cell_y, special, specialColor, mon_id, fontSize, font) {
		var i = 0,
			jump = 0;
		//特殊属性2个一行，可以绘制很多属性。i控制特殊属性个数以判断绘制行数,jump控制过长属性
		while (i - jump < special.length) {
			//这里的代码计算极其复杂，因为特殊属性的绘制位置需要考虑到方方面面。
			if (special[i - jump].length <= 4 && i + 1 - jump < special.length && special[i + 1 - jump].length <= 4) {
				core.fillText("enemyInfo", special[i - jump], cell_x + width / 4 - special[i - jump].length * fontSize /
					4 - special[i - jump].replace(/[-_ 0-9a-zA-Z]/g, '').length * fontSize / 4, cell_y + 83 +
					10 * i, core.arrayToRGBA(specialColor[i - jump]), fontSize + "px " + font);
				if (i + 1 - jump < special.length)
					core.fillText("enemyInfo", special[i + 1 - jump], cell_x + width * 3 / 4 - special[i - jump].length *
						fontSize / 4 - special[i - jump].replace(/[-_ 0-9a-zA-Z]/g, '').length * fontSize / 4, cell_y +
						83 + 10 * i, core.arrayToRGBA(specialColor[i + 1 - jump]), fontSize + "px " + font);

				//调整亮度，需要请uncomment并且，如果属性颜色过亮或者过暗可选 "......"里填上调整值
				/*
				if (special[i - jump].length <= 4 && i + 1 - jump < special.length && special[i + 1 - jump].length <= 4) {
				core.fillText("enemyInfo", special[i - jump], cell_x + width / 4 - special[i - jump].length * fontSize /
					4 - special[i - jump].replace(/[-_ 0-9a-zA-Z]/g, '').length * fontSize / 4, cell_y + 98 + 10 * i,
					core.arrayToRGBA(core.plugin.changeColor(core.getSpecialColor(mon_id)[i - jump], "......")), fontSize + "px " + font);
				if (i + 1 - jump < special.length)
					core.fillText("enemyInfo", special[i + 1 - jump], cell_x + width * 3 / 4 - special[i - jump].length *
						fontSize / 4 - special[i - jump].replace(/[-_ 0-9a-zA-Z]/g, '').length * fontSize / 4, cell_y + 98 + 10 * i, 
						core.arrayToRGBA(core.plugin.changeColor(core.getSpecialColor(mon_id)[i + 1 - jump], "......")), fontSize + "px " + font);
				*/
			} else {
				core.fillText("enemyInfo", special[i - jump], cell_x + width / 2 - special[i - jump].length * fontSize / 4 -
					special[i - jump].replace(/[-_ 0-9a-zA-Z]/g, '').length * fontSize / 4,
					cell_y + 83 + 10 * i, core.arrayToRGBA(specialColor[i - jump]), fontSize + "px " + font);

				//调整亮度，需要请uncomment并且，如果属性颜色过亮或者过暗可选 "......"里填上调整值
				/*
				core.fillText("enemyInfo", special[i - jump], cell_x + width / 2 - special[i - jump].length * fontSize / 4 -
					special[i - jump].replace(/[-_ 0-9a-zA-Z]/g, '').length * fontSize / 4, cell_y + 98 + 10 * i,
					core.arrayToRGBA(core.plugin.changeColor(core.getSpecialColor(mon_id)[i - jump], "......")), fontSize + "px " + font);
				*/

				jump += 1;
			}
			i += 2;
		}
		i -= 2;
		return i;
	}

	//这个子程序用来绘制怪物的攻防等信息，由于代码理解简单便不怎么单独注释。
	//前一半绘制"生命"等等提示，后一半绘制怪物信息。
	this.addEnemyInfo = function (i, cell_x, cell_y, mon_id, fontSize, width, font) {
		//下面这两行用来放怪物境界，如果需要就uncomment
		/*
		i += 2;
		core.fillText("enemyInfo", info[mon_id].level, cell_x + width / 2 -
			info[mon_id].level.length * fontSize / 2, cell_y + 96 + 10 * i, [255, 255, 255, 1], fontSize + "px heiti");
		*/
		var info = core.getEnemyInfo(mon_id, null, cell_x, cell_y, core.status.floorId);
		core.setTextAlign('enemyInfo', 'left');
		core.fillText("enemyInfo", "生命", cell_x + 8,
			cell_y + 100 + 10 * i, [192, 224, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", "攻击", cell_x + 8,
			cell_y + 115 + 10 * i, [192, 224, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", "防御", cell_x + 8,
			cell_y + 130 + 10 * i, [192, 224, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", "金币", cell_x + 8,
			cell_y + 145 + 10 * i, [192, 224, 255, 1], fontSize + "px " + font);
		core.setTextAlign('enemyInfo', 'right');
		core.fillText("enemyInfo", core.formatBigNumber(info.hp, true), cell_x + width - 8,
			cell_y + 100 + 10 * i, [255, 255, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", core.formatBigNumber(info.atk, true), cell_x + width - 8,
			cell_y + 115 + 10 * i, [255, 255, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", core.formatBigNumber(info.def, true), cell_x + width - 8,
			cell_y + 130 + 10 * i, [255, 255, 255, 1], fontSize + "px " + font);
		//这里单独注释一下，如果金币经验>1000且<100w文字会覆盖，处理了一下
		[displayMoney] = this.ThousandTransform(cell_x, cell_y, mon_id);
		core.fillText("enemyInfo", displayMoney, cell_x + width - 8,
			cell_y + 145 + 10 * i, [255, 255, 255, 1], fontSize + "px " + font);

		//最后这两行用来加其他信息，在"......"里填上属性名，如果需要就uncomment
		/*
		i += 2;
		core.setTextAlign('enemyInfo', 'left');
		core.fillText("enemyInfo", "......", cell_x + 8,
			cell_y + 178 + 10 * i, [192, 224, 255, 1], fontSize + "px " + font);
		core.setTextAlign('enemyInfo', 'right');
		core.fillText("enemyInfo", core.formatBigNumber(info[mon_id]."......", true), cell_x + width - 8,
			cell_y + 178 + 10 * i, [255, 255, 255, 1], fontSize + "px " + font);
		*/
	}

	//这个子程序处理了金币和经验在1000 ~ 999999时的情况，不处理的话文字会覆盖。
	this.ThousandTransform = function (cell_x, cell_y, mon_id) {
		var displayMoney = "",
			displayEXP = "";
		var info = core.getEnemyInfo(mon_id, null, cell_x, cell_y, core.status.floorId);
		if (info.money >= 1000 && info.money < 100000) {
			displayMoney = info.money / 1000;
			displayMoney = displayMoney.toString() + "k";
		} else if (info.money >= 100000 && info.money < 1000000) {
			displayMoney = info.money / 10000;
			displayMoney = displayMoney.toString() + "w";
		} else
			displayMoney = core.formatBigNumber(info.money, true);
		if (info.exp >= 1000 && info.exp < 100000) {
			displayEXP = info.exp / 1000;
			displayEXP = displayEXP.toString() + "k";
		} else if (info.exp >= 100000 && info.exp < 1000000) {
			displayEXP = info.exp / 10000;
			displayEXP = displayEXP.toString() + "w";
		} else
			displayEXP = core.formatBigNumber(info.exp, true);
		return [displayMoney, displayEXP];
	}

	//如果鼠标移动，则Register Action
	core.registerAction('onmove', 'enemyInfoDisplay', function (mouse_x, mouse_y) {
		core.plugin.enemyInfoDisplay(mouse_x, mouse_y);
		return false;
	}, 100);

	//主程序，当鼠标移动时进行判断
	this.enemyInfoDisplay = function (mouse_x, mouse_y) {
		//使用需要3个条件：打开开关、拥有怪物手册和玩家在电脑端上游戏
		if (core.getFlag("useEnemyInfoDisplay") == true && core.hasItem("book") && core.platform.isPC == true) {
			core.createCanvas("enemyInfo", 0, 0, 422, 422, 81);
			//这里处理大地图带来的影响
			offsetX = core.bigmap.offsetX;
			offsetY = core.bigmap.offsetY;
			//获得鼠标对应的图块坐标位置
			var enemy_x = core.clamp(Math.floor((mouse_x + offsetX / (32 * core.domStyle.scale))), core.bigmap.offsetX /
				(32 * core.domStyle.scale) - 1, core.__SIZE__ - 1 + core.bigmap.offsetX / (32 * core.domStyle.scale)) + 1;
			var enemy_y = core.clamp(Math.floor((mouse_y + offsetY / (32 * core.domStyle.scale))), core.bigmap.offsetY /
				(32 * core.domStyle.scale) - 1, core.__SIZE__ - 1 + core.bigmap.offsetY / (32 * core.domStyle.scale)) + 1;
			//如果该图块有怪物，则绘制窗口
			if (core.enemyExists(enemy_x, enemy_y)) core.plugin.startEnemyInfoDisplay(enemy_x, enemy_y);
		}
	}

	//备用程序，获得鼠标的位置，出错时使用
	main.dom.data.onmousemove2 = function (e) {
		e.stopPropagation();
		var loc = main.core.actions._getClickLoc(e.clientX, e.clientY);
		if (loc == null) return;
		main.core.onmove(loc);
		return [e.clientX, e.clientY];
	}

	//楼层转换后，怪物消失。但是如果鼠标不移动，窗口会继续显示降低玩家体验
	//复写一下楼层转换，使得楼层转换后窗口不再继续显示
	events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback) {
		var info = this._changeFloor_getInfo(floorId, stair, heroLoc, time);
		if (info == null) {
			if (callback) callback();
			return;
		}
		floorId = info.floorId;
		info.locked = core.status.lockControl;

		core.dom.floorNameLabel.innerText = core.status.maps[floorId].title;
		core.lockControl();
		core.stopAutomaticRoute();
		core.clearContinueAutomaticRoute();
		core.status.replay.animate = true;
		clearInterval(core.interval.onDownInterval);
		core.interval.onDownInterval = 'tmp';
		core.clearMap("enemyInfo");
		this._changeFloor_beforeChange(info, callback);
	}

	//战斗后怪物消失，同理需要复写一下
	events.prototype.battle = function (id, x, y, force, callback) {
		core.saveAndStopAutomaticRoute();
		id = id || core.getBlockId(x, y);
		if (!id) return core.clearContinueAutomaticRoute(callback);
		// 非强制战斗
		if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) {
			core.drawTip("你打不过此怪物！");
			return core.clearContinueAutomaticRoute(callback);
		}
		// 自动存档
		if (!core.status.event.id) core.autosave(true);
		// 战前事件
		if (!this.beforeBattle(id, x, y))
			return core.clearContinueAutomaticRoute(callback);
		//战后事件
		this.afterBattle(id, x, y);
		if (callback) callback();
		core.clearMap("enemyInfo");
	}
},
    "NPC悬停": function () {
	//怪物显示复写而成
	//使用方法：启用useNPCDisplay为true，并且在想要显示信息的npc处，events事件最开头添加注释，内容为想要显示的片段，多行用多个注释，每个注释一行
	this.startNPCDisplay = function (enemy_x, enemy_y) {
		var blk = core.getBlock(enemy_x, enemy_y);
		if (!blk || !blk.event || !blk.event.data) return;
		var dt = core.getBlock(enemy_x, enemy_y).event.data;
		if (!dt || !dt[0] || !dt[0].type || dt[0].type != 'comment') return;
		var cls = core.getBlockCls(enemy_x, enemy_y)
		if (cls != 'npcs' && cls != 'npc48' && cls != 'enemys' && cls != 'enemy48') return;
		var text = [dt[0].text];
		var lines = 1;
		while (lines < dt.length && dt[lines].type == 'comment') {
			text.push(dt[lines].text);
			lines++;
		}
		//在下面设置你的字体和字体大小
		var font = "beeB"; //这里"......"填写你的字体。
		var fontSize = 12;
		core.setFont('NPC', "12px beeB");
		var width = core.ui.calWidth('NPC', text, font) - 220;
		var height = 58 + 26 * lines;
		if (cls == 'npc48') height = 73 + 26 * lines
		var cell_x = (enemy_x + 0.5) * 32 - core.bigmap.offsetX;
		var cell_y = (enemy_y + 0.5) * 32 - core.bigmap.offsetY;

		//绘制窗口
		core.plugin.addNPC(width, height, cell_x, cell_y, cls, fontSize, core.getBlockId(enemy_x, enemy_y), font, text);
	}

	//这个子程序用来绘制怪物的基本信息
	this.addNPC = function (width, height, cell_x, cell_y, cls, fontSize, mon_id, font, text) {
		//绘制窗口外观
		width = 0;
		for (let i = 0; i < text.length; i++) {
			let tmp = core.drawTextContent('NPC', core.replaceValue(text[i]), { left: cell_x + 10, top: cell_y + 45 + i * 25, align: 'mid', font: font, fontSize: fontSize });
			if (cls == 'npc48') tmp = core.drawTextContent('NPC', core.replaceValue(text[i]), { left: cell_x + 10, top: cell_y + 60 + i * 25, align: 'mid', font: font, fontSize: fontSize });
			let tt = tmp.blocks[tmp.blocks.length - 1];
			width = Math.max(width, tt.left + tt.width + 20);
		}
		core.clearMap('NPC');
		if (cell_x > 32 * core.__SIZE__ - width) cell_x -= width;
		if (cell_y > 32 * core.__SIZE__ - height) cell_y -= height;

		core.fillRoundRect("NPC", cell_x, cell_y, width, height, 5, [0, 0, 0, 0.85]);
		core.strokeRoundRect("NPC", cell_x, cell_y, width, height, 5, [255, 255, 255, 1], 2);
		//绘制抬头、怪物图标和怪物名称
		core.drawIcon('NPC', mon_id, cell_x + width / 2 - 16, cell_y + 10);
		// 		core.fillText("enemyInfo", info[mon_id].name, cell_x + width / 2 - info[mon_id].name.length * fontSize / 8 -
		// 			info[mon_id].name.replace(/[()]/g, '').length * fontSize / 8 - info[mon_id].name.replace(/[-_ ()0-9a-zA-Z]/g, '').length *
		// 			fontSize / 4, cell_y + 78 + bigEnemy, [255, 255, 255, 1], fontSize + "px " + font);
		if (cls == 'npc48') {
			for (let i = 0; i < text.length; i++) {
				core.drawTextContent('NPC', core.replaceValue(text[i]), { left: cell_x + 10, top: cell_y + 60 + i * 25, align: 'mid', font: font, fontSize: fontSize });
			}
		} else {
			for (let i = 0; i < text.length; i++) {
				core.drawTextContent('NPC', core.replaceValue(text[i]), { left: cell_x + 10, top: cell_y + 45 + i * 25, align: 'mid', font: font, fontSize: fontSize });
			}
		}

	}

	//如果鼠标移动，则Register Action
	core.registerAction('onmove', 'NPCDisplay', function (mouse_x, mouse_y) {
		core.plugin.NPCDisplay(mouse_x, mouse_y);
		return false;
	}, 100);


	//主程序，当鼠标移动时进行判断
	this.NPCDisplay = function (mouse_x, mouse_y) {
		//使用需要3个条件：打开开关、拥有怪物手册和玩家在电脑端上游戏
		if (core.getFlag("useEnemyInfoDisplay") == true && core.hasItem("book") && core.platform.isPC == true) {
			core.createCanvas("NPC", 0, 0, 352, 352, 81);
			//这里处理大地图带来的影响
			offsetX = core.bigmap.offsetX;
			offsetY = core.bigmap.offsetY;
			//获得鼠标对应的图块坐标位置
			var enemy_x = core.clamp(Math.floor((mouse_x + offsetX / (32 * core.domStyle.scale))), core.bigmap.offsetX /
				(32 * core.domStyle.scale) - 1, core.__SIZE__ - 1 + core.bigmap.offsetX / (32 * core.domStyle.scale)) + 1;
			var enemy_y = core.clamp(Math.floor((mouse_y + offsetY / (32 * core.domStyle.scale))), core.bigmap.offsetY /
				(32 * core.domStyle.scale) - 1, core.__SIZE__ - 1 + core.bigmap.offsetY / (32 * core.domStyle.scale)) + 1;
			//如果该图块有怪物，则绘制窗口
			core.plugin.startNPCDisplay(enemy_x, enemy_y);
		}
	}




},
    "穿越楼梯": function () {
	// 在此增加新插件
	core.maps._canMoveDirectly_checkStartPoint = function (sx, sy) {
		if (core.status.checkBlock.damage[sx + "," + sy]) return false;
		var block = core.getBlock(sx, sy);
		if (block != null) {
			if ((block.event.id == "upFloor") || (block.event.id == "downFloor")) return true;
			// 只有起点是传送点才是能无视
			return block.event.trigger == 'changeFloor';
		}
		return true;
	}

	core.maps._canMoveDirectly_bfs = function (sx, sy, locs, number, ans, canMoveArray) {
		canMoveArray = canMoveArray || core.maps.generateMovableArray();
		var blocksObj = core.maps.getMapBlocksObj();
		// 滑冰
		var bgMap = core.maps.getBgMapArray();

		var visited = [],
			queue = [];
		visited[sx + "," + sy] = 0;
		queue.push(sx + "," + sy);

		while (queue.length > 0) {
			var now = queue.shift().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,
					ny = y + core.utils.scan[direction].y,
					nindex = nx + "," + ny;
				if (visited[nindex]) continue;
				if (core.onSki(bgMap[ny][nx])) continue;
				if (!core.maps._canMoveDirectly_checkNextPoint(blocksObj, nx, ny)) continue;
				visited[nindex] = visited[now] + 1;
				// if (nx == ex && ny == ey) return visited[nindex];
				for (var i in ans) {
					if (locs[i][0] == nx && locs[i][1] == ny && ans[i] == null) {
						// 不可以绿点为终点
						var block = blocksObj[nx + "," + ny];
						if (block && !block.disable && block.event.trigger) {
							ans[i] = -1;
						} else {
							if (block && !block.disable && ((block.event.id == "upFloor") || (block.event.id == "downFloor"))) {
								ans[i] = -1;
							} else {
								ans[i] = visited[nindex];
							}
						}
						number--;
						if (number == 0) return ans;
					}
				}
				queue.push(nindex);
			}
		}

		for (var i in ans) {
			if (ans[i] == null) ans[i] = -1;
		}
		return ans;
	}

	core.maps._canMoveDirectly_checkNextPoint = function (blocksObj, x, y) {
		var index = x + "," + y;
		var block = blocksObj[index];
		// 该点是否不可通行或有脚本
		if (block && !block.disable && (block.event.noPass || block.event.script || block.event.event)) {
			if ((block.event.id != "upFloor") && (block.event.id != "downFloor")) {
				return false;
			} else {
				var ignore = core.flags.ignoreChangeFloor;
				if (block.event.data && block.event.data.ignoreChangeFloor != null)
					ignore = block.event.data.ignoreChangeFloor;
				if (!ignore) 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;
	}




	core.events.trigger = function (x, y, callback) {
		var _executeCallback = function () {
			// 因为trigger之后还有可能触发其他同步脚本（比如阻激夹域检测）
			// 所以这里强制callback被异步触发
			if (callback) {
				setTimeout(callback, 1); // +1是为了录像检测系统 
			}
			return;
		}
		if (core.status.gameOver) return _executeCallback();
		if (core.status.event.id == 'action') {
			core.insertAction({ "type": "function", "function": "function () { core.events._trigger_inAction(" + x + "," + y + "); }", "async": true },
				null, null, null, true);
			return _executeCallback();
		}
		if (core.status.event.id) return _executeCallback();

		var block = core.getBlock(x, y);
		if (block == null) return _executeCallback();

		// 执行该点的脚本
		if (block.event.script) {
			var noPass = block.event.noPass;
			if (((block.event.id == "upFloor") || (block.event.id == "downFloor")) && !noPass && core.events._trigger_ignoreChangeFloor(block)) return _executeCallback();
			core.clearRouteFolding();
			try {
				eval(block.event.script);
			} catch (e) { main.log(e); }
		}

		// 碰触事件
		if (block.event.event) {
			core.clearRouteFolding();
			core.insertAction(block.event.event, block.x, block.y);
			// 不再执行该点的系统事件
			return _executeCallback();
		}

		if (block.event.trigger && block.event.trigger != 'null') {
			var noPass = block.event.noPass,
				trigger = block.event.trigger;
			if (noPass) core.clearAutomaticRouteNode(x, y);

			// 转换楼层能否穿透
			if (trigger == 'changeFloor' && !noPass && this._trigger_ignoreChangeFloor(block))
				return _executeCallback();
			core.status.automaticRoute.moveDirectly = false;
			this.doSystemEvent(trigger, block);
		}
		return _executeCallback();
	}

}
}