var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = 
{
    "init": function () {
		this._afterLoadResources = function () {
			// 本函数将在所有资源加载完毕后，游戏开启前被执行
		}
	},
    "drawLight": function () {

		// 绘制灯光/漆黑层效果。调用方式 core.plugin.drawLight(...)
		// 【参数说明】
		// name：必填，要绘制到的画布名；可以是一个系统画布，或者是个自定义画布；如果不存在则创建
		// color：可选，只能是一个0~1之间的数，为不透明度的值。不填则默认为0.9。
		// lights：可选，一个数组，定义了每个独立的灯光。
		//        其中每一项是三元组 [x,y,r] x和y分别为该灯光的横纵坐标，r为该灯光的半径。
		// lightDec：可选，0到1之间，光从多少百分比才开始衰减（在此范围内保持全亮），不设置默认为0。
		//        比如lightDec为0.5代表，每个灯光部分内圈50%的范围全亮，50%以后才开始快速衰减。
		// 【调用样例】
		// core.plugin.drawLight('curtain'); // 在curtain层绘制全图不透明度0.9，等价于更改画面色调为[0,0,0,0.9]。
		// core.plugin.drawLight('ui', 0.95, [[25,11,46]]); // 在ui层绘制全图不透明度0.95，其中在(25,11)点存在一个半径为46的灯光效果。
		// core.plugin.drawLight('test', 0.2, [[25,11,46,0.1]]); // 创建一个test图层，不透明度0.2，其中在(25,11)点存在一个半径为46的灯光效果，灯光中心不透明度0.1。
		// core.plugin.drawLight('test2', 0.9, [[25,11,46],[105,121,88],[301,221,106]]); // 创建test2图层，且存在三个灯光效果，分别是中心(25,11)半径46，中心(105,121)半径88，中心(301,221)半径106。
		// core.plugin.drawLight('xxx', 0.3, [[25,11,46],[105,121,88,0.2]], 0.4); // 存在两个灯光效果，它们在内圈40%范围内保持全亮，40%后才开始衰减。
		this.drawLight = function (name, color, lights, lightDec) {

			// 清空色调层；也可以修改成其它层比如animate/weather层，或者用自己创建的canvas
			var ctx = core.getContextByName(name);
			if (ctx == null) {
				if (typeof name == 'string')
					ctx = core.createCanvas(name, 0, 0, core._PX_ || core.__PIXELS__, core._PY_ || core.__PIXELS__, 98);
				else return;
			}

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

			core.clearMap(name);
			// 绘制色调层，默认不透明度
			if (color == null) color = 0.9;
			ctx.fillStyle = "rgba(0,0,0," + color + ")";
			ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

			lightDec = core.clamp(lightDec, 0, 1);

			// 绘制每个灯光效果
			ctx.globalCompositeOperation = 'destination-out';
			lights.forEach(function (light) {
				// 坐标，半径，中心不透明度
				var x = light[0],
					y = light[1],
					r = light[2];
				// 计算衰减距离
				var decDistance = parseInt(r * lightDec);
				// 正方形区域的直径和左上角坐标
				var grd = ctx.createRadialGradient(x, y, decDistance, x, y, r);
				grd.addColorStop(0, "rgba(0,0,0,1)");
				grd.addColorStop(1, "rgba(0,0,0,0)");
				ctx.beginPath();
				ctx.fillStyle = grd;
				ctx.arc(x, y, r, 0, 2 * Math.PI);
				ctx.fill();
			});
			ctx.globalCompositeOperation = 'source-over';
			// 可以在任何地方（如afterXXX或自定义脚本事件）调用函数，方法为  core.plugin.xxx();
		}
	},
    "shop": function () {
		// 【全局商店】相关的功能
		// 
		// 打开一个全局商店
		// shopId：要打开的商店id；noRoute：是否不计入录像
		this.openShop = function (shopId, noRoute) {
			var shop = core.status.shops[shopId];
			// Step 1: 检查能否打开此商店
			if (!this.canOpenShop(shopId)) {
				core.drawTip("该商店尚未开启");
				return false;
			}

			// Step 2: （如有必要）记录打开商店的脚本事件
			if (!noRoute) {
				core.status.route.push("shop:" + shopId);
			}

			// Step 3: 检查道具商店 or 公共事件
			if (shop.item) {
				if (core.openItemShop) {
					core.openItemShop(shopId);
				} else {
					core.playSound('操作失败');
					core.insertAction("道具商店插件不存在！请检查是否存在该插件！");
				}
				return;
			}
			if (shop.commonEvent) {
				core.insertCommonEvent(shop.commonEvent, shop.args);
				return;
			}

			_shouldProcessKeyUp = true;

			// Step 4: 执行标准公共商店    
			core.insertAction(this._convertShop(shop));
			return true;
		}

		////// 将一个全局商店转变成可预览的公共事件 //////
		this._convertShop = function (shop) {
			return [
				{ "type": "function", "function": "function() {core.addFlag('@temp@shop', 1);}" },
				{
					"type": "while",
					"condition": "true",
					"data": [
						// 检测能否访问该商店
						{
							"type": "if",
							"condition": "core.isShopVisited('" + shop.id + "')",
							"true": [
								// 可以访问，直接插入执行效果
								{ "type": "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._HALF_WIDTH_ || core.__HALF_SIZE__, topIndex + choices.length - 1);
				return true;
			}
			return false;
		}, 60);

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

		// 允许长按屏幕连续执行操作
		core.registerAction('longClick', 'shops', function (x, y, px, py) {
			if (!core.status.lockControl || !core.hasFlag("@temp@shop") || core.status.event.id != 'action') return false;
			if (core.status.event.data.type != 'choices') return false;
			var data = core.status.event.data.current;
			var choices = data.choices;
			var topIndex = core.actions._getChoicesTopIndex(choices.length);
			if (Math.abs(x - (core._HALF_WIDTH_ || core.__HALF_SIZE__)) <= 2 && y >= topIndex && y < topIndex + choices.length) {
				core.actions._clickAction(x, y);
				return true;
			}
			return false;
		}, 60);
	},
    "removeMap": function () {
		// 高层塔砍层插件，删除后不会存入存档，不可浏览地图也不可飞到。
		// 推荐用法：
		// 对于超高层或分区域塔，当在1区时将2区以后的地图删除；1区结束时恢复2区，进二区时删除1区地图，以此类推
		// 这样可以大幅减少存档空间，以及加快存读档速度

		// 删除楼层
		// core.removeMaps("MT1", "MT300") 删除MT1~MT300之间的全部层
		// core.removeMaps("MT10") 只删除MT10层
		this.removeMaps = function (fromId, toId) {
			toId = toId || fromId;
			var fromIndex = core.floorIds.indexOf(fromId),
				toIndex = core.floorIds.indexOf(toId);
			if (toIndex < 0) toIndex = core.floorIds.length - 1;
			flags.__visited__ = flags.__visited__ || {};
			flags.__removed__ = flags.__removed__ || [];
			flags.__disabled__ = flags.__disabled__ || {};
			flags.__leaveLoc__ = flags.__leaveLoc__ || {};
			for (var i = fromIndex; i <= toIndex; ++i) {
				var floorId = core.floorIds[i];
				if (core.status.maps[floorId].deleted) continue;
				delete flags.__visited__[floorId];
				flags.__removed__.push(floorId);
				delete flags.__disabled__[floorId];
				delete flags.__leaveLoc__[floorId];
				(core.status.autoEvents || []).forEach(function (event) {
					if (event.floorId == floorId && event.currentFloor) {
						core.autoEventExecuting(event.symbol, false);
						core.autoEventExecuted(event.symbol, false);
					}
				});
				core.status.maps[floorId].deleted = true;
				core.status.maps[floorId].canFlyTo = false;
				core.status.maps[floorId].canFlyFrom = false;
				core.status.maps[floorId].cannotViewMap = true;
			}
		}

		// 恢复楼层
		// core.resumeMaps("MT1", "MT300") 恢复MT1~MT300之间的全部层
		// core.resumeMaps("MT10") 只恢复MT10层
		this.resumeMaps = function (fromId, toId) {
			toId = toId || fromId;
			var fromIndex = core.floorIds.indexOf(fromId),
				toIndex = core.floorIds.indexOf(toId);
			if (toIndex < 0) toIndex = core.floorIds.length - 1;
			flags.__removed__ = flags.__removed__ || [];
			for (var i = fromIndex; i <= toIndex; ++i) {
				var floorId = core.floorIds[i];
				if (!core.status.maps[floorId].deleted) continue;
				flags.__removed__ = flags.__removed__.filter(function (f) { return f != floorId; });
				core.status.maps[floorId] = core.loadFloor(floorId);
			}
		}

		// 分区砍层相关
		var inAnyPartition = function (floorId) {
			var inPartition = false;
			(core.floorPartitions || []).forEach(function (floor) {
				var fromIndex = core.floorIds.indexOf(floor[0]);
				var toIndex = core.floorIds.indexOf(floor[1]);
				var index = core.floorIds.indexOf(floorId);
				if (fromIndex < 0 || index < 0) return;
				if (toIndex < 0) toIndex = core.floorIds.length - 1;
				if (index >= fromIndex && index <= toIndex) inPartition = true;
			});
			return inPartition;
		}

		// 分区砍层
		this.autoRemoveMaps = function (floorId) {
			if (main.mode != 'play' || !inAnyPartition(floorId)) return;
			// 根据分区信息自动砍层与恢复
			(core.floorPartitions || []).forEach(function (floor) {
				var fromIndex = core.floorIds.indexOf(floor[0]);
				var toIndex = core.floorIds.indexOf(floor[1]);
				var index = core.floorIds.indexOf(floorId);
				if (fromIndex < 0 || index < 0) return;
				if (toIndex < 0) toIndex = core.floorIds.length - 1;
				if (index >= fromIndex && index <= toIndex) {
					core.resumeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]);
				} else {
					core.removeMaps(core.floorIds[fromIndex], core.floorIds[toIndex]);
				}
			});
		}
	},
    "fiveLayers": function () {
		// 是否启用五图层（增加背景2层和前景2层） 将__enable置为true即会启用；启用后请保存后刷新编辑器
		// 背景层2将会覆盖背景层 被事件层覆盖 前景层2将会覆盖前景层
		// 另外 请注意加入两个新图层 会让大地图的性能降低一些
		// 插件作者：ad
		var __enable = false;
		if (!__enable) return;

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

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

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

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

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

		var _loadFloor_doNotCopy = core.maps._loadFloor_doNotCopy;
		core.maps._loadFloor_doNotCopy = function () {
			return ["bg2map", "fg2map"].concat(_loadFloor_doNotCopy());
		}
		////// 绘制背景和前景层 //////
		core.maps._drawBg_draw = function (floorId, toDrawCtx, cacheCtx, config) {
			config.ctx = cacheCtx;
			core.maps._drawBg_drawBackground(floorId, config);
			// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块；后绘制的覆盖先绘制的。
			core.maps._drawFloorImages(floorId, config.ctx, 'bg', null, null, config.onMap);
			core.maps._drawBgFgMap(floorId, 'bg', config);
			if (config.onMap) {
				core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0);
				core.clearMap('bg2');
				core.clearMap(cacheCtx);
			}
			core.maps._drawBgFgMap(floorId, 'bg2', config);
			if (config.onMap) core.drawImage('bg2', cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0);
			config.ctx = toDrawCtx;
		}
		core.maps._drawFg_draw = function (floorId, toDrawCtx, cacheCtx, config) {
			config.ctx = cacheCtx;
			// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块；后绘制的覆盖先绘制的。
			core.maps._drawFloorImages(floorId, config.ctx, 'fg', null, null, config.onMap);
			core.maps._drawBgFgMap(floorId, 'fg', config);
			if (config.onMap) {
				core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0);
				core.clearMap('fg2');
				core.clearMap(cacheCtx);
			}
			core.maps._drawBgFgMap(floorId, 'fg2', config);
			if (config.onMap) core.drawImage('fg2', cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0);
			config.ctx = toDrawCtx;
		}
		////// 移动判定 //////
		core.maps._generateMovableArray_arrays = function (floorId) {
			return {
				bgArray: this.getBgMapArray(floorId),
				fgArray: this.getFgMapArray(floorId),
				eventArray: this.getMapArray(floorId),
				bg2Array: this._getBgFgMapArray('bg2', floorId),
				fg2Array: this._getBgFgMapArray('fg2', floorId)
			};
		}
	},
    "itemShop": function () {
		// 道具商店相关的插件
		// 可在全塔属性-全局商店中使用「道具商店」事件块进行编辑（如果找不到可以在入口方块中找）

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

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

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

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

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

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

			// 绘制每一项
			var start = (page - 1) * per_page;
			for (var i = 0; i < per_page; ++i) {
				var curr = start + i;
				if (curr >= list.length) break;
				var item = list[curr];
				core.drawIcon('uievent', item.id, 10, 125 + i * 40);
				core.setTextAlign('uievent', 'left');
				core.fillText('uievent', core.material.items[item.id].name, 50, 132 + i * 40, null, bigFont);
				core.setTextAlign('uievent', 'right');
				core.fillText('uievent', (type == 0 ? core.calValue(item.money) : core.calValue(item.sell)) + useText + "/个", 300, 133 + i * 40, null, middleFont);
				core.setTextAlign("uievent", "left");
				if (curr == selectItem) {
					// 绘制描述，文字自动放缩
					var text = core.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(); }"
			}
			]);
		}

	},
    "enemyLevel": function () {
		// 此插件将提供怪物手册中的怪物境界显示
		// 使用此插件需要先给每个怪物定义境界，方法如下：
		// 点击怪物的【配置表格】，找到“【怪物】相关的表格配置”，然后在【名称】仿照增加境界定义：
		/*
		 "level": {
			  "_leaf": true,
			  "_type": "textarea",
			  "_string": true,
			  "_data": "境界"
		 },
		 */
		// 然后保存刷新，可以看到怪物的属性定义中出现了【境界】。再开启本插件即可。

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

		// 这里定义每个境界的显示颜色；可以写'red', '#RRGGBB' 或者[r,g,b,a]四元数组
		var levelToColors = {
			"萌新一阶": "red",
			"萌新二阶": "#FF0000",
			"萌新三阶": [255, 0, 0, 1],
		};

		// 复写 _drawBook_drawName
		var originDrawBook = core.ui._drawBook_drawName;
		core.ui._drawBook_drawName = function (index, enemy, top, left, width) {
			// 如果没有境界，则直接调用原始代码绘制
			if (!enemy.level) return originDrawBook.call(core.ui, index, enemy, top, left, width);
			// 存在境界，则额外进行绘制
			core.setTextAlign('ui', 'center');
			if (enemy.specialText.length == 0) {
				core.fillText('ui', enemy.name, left + width / 2,
					top + 27, '#DDDDDD', this._buildFont(17, true));
				core.fillText('ui', enemy.level, left + width / 2,
					top + 51, core.arrayToRGBA(levelToColors[enemy.level] || '#DDDDDD'), this._buildFont(14, true));
			} else {
				core.fillText('ui', enemy.name, left + width / 2,
					top + 20, '#DDDDDD', this._buildFont(17, true), width);
				switch (enemy.specialText.length) {
					case 1:
						core.fillText('ui', enemy.specialText[0], left + width / 2,
							top + 38, core.arrayToRGBA((enemy.specialColor || [])[0] || '#FF6A6A'),
							this._buildFont(14, true), width);
						break;
					case 2:
						// Step 1: 计算字体
						var text = enemy.specialText[0] + "  " + enemy.specialText[1];
						core.setFontForMaxWidth('ui', text, width, this._buildFont(14, true));
						// Step 2: 计算总宽度
						var totalWidth = core.calWidth('ui', text);
						var leftWidth = core.calWidth('ui', enemy.specialText[0]);
						var rightWidth = core.calWidth('ui', enemy.specialText[1]);
						// Step 3: 绘制
						core.fillText('ui', enemy.specialText[0], left + (width + leftWidth - totalWidth) / 2,
							top + 38, core.arrayToRGBA((enemy.specialColor || [])[0] || '#FF6A6A'));
						core.fillText('ui', enemy.specialText[1], left + (width + totalWidth - rightWidth) / 2,
							top + 38, core.arrayToRGBA((enemy.specialColor || [])[1] || '#FF6A6A'));
						break;
					default:
						core.fillText('ui', '多属性...', left + width / 2,
							top + 38, '#FF6A6A', this._buildFont(14, true), width);
				}
				core.fillText('ui', enemy.level, left + width / 2,
					top + 56, core.arrayToRGBA(levelToColors[enemy.level] || '#DDDDDD'), this._buildFont(14, true));
			}
		}

		// 也可以复写其他的属性颜色如怪物攻防等，具体参见下面的例子的注释部分
		core.ui._drawBook_drawRow1 = function (index, enemy, top, left, width, position) {
			// 绘制第一行
			core.setTextAlign('ui', 'left');
			var b13 = this._buildFont(13, true),
				f13 = this._buildFont(13, false);
			var col1 = left,
				col2 = left + width * 9 / 25,
				col3 = left + width * 17 / 25;
			core.fillText('ui', '生命', col1, position, '#DDDDDD', f13);
			core.fillText('ui', core.formatBigNumber(enemy.hp || 0), col1 + 30, position, /*'red' */ null, b13);
			core.fillText('ui', '攻击', col2, position, null, f13);
			core.fillText('ui', core.formatBigNumber(enemy.atk || 0), col2 + 30, position, /* '#FF0000' */ null, b13);
			core.fillText('ui', '防御', col3, position, null, f13);
			core.fillText('ui', core.formatBigNumber(enemy.def || 0), col3 + 30, position, /* [255, 0, 0, 1] */ null, b13);
		}
	},
    "multiHeros": function () {
		// 多角色插件
		// Step 1: 启用本插件
		// Step 2: 定义每个新的角色各项初始数据（参见下方注释）
		// Step 3: 在游戏中的任何地方都可以调用 `core.changeHero()` 进行切换；也可以 `core.changeHero(1)` 来切换到某个具体的角色上

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

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

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

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

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

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

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

			var saveList = Object.keys(hero1);

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

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

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

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

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

			// 插入事件：改变角色行走图并进行楼层切换
			var toFloorId = data.floorId || core.status.floorId;
			var toLoc = data.loc || core.status.hero.loc;
			core.insertAction([
				{ "type": "setHeroIcon", "name": data.image || "hero.png" }, // 改变行走图
				// 同层则用changePos，不同层则用changeFloor；这是为了避免共用楼层造成触发eachArrive
				toFloorId != core.status.floorId ? {
					"type": "changeFloor",
					"floorId": toFloorId,
					"loc": [toLoc.x, toLoc.y],
					"direction": toLoc.direction,
					"time": 0 // 可以在这里设置切换时间
				} : { "type": "changePos", "loc": [toLoc.x, toLoc.y], "direction": toLoc.direction }
				// 你还可以在这里执行其他事件，比如增加或取消跟随效果
			]);
			core.setFlag("heroId", toHeroId); // 保存切换到的角色ID
		}
	},
    "heroFourFrames": function () {
		// 样板的勇士/跟随者移动时只使用2、4两帧，观感较差。本插件可以将四帧全用上。

		// 是否启用本插件
		var __enable = true;
		if (!__enable) return;

		["up", "down", "left", "right"].forEach(function (one) {
			// 指定中间帧动画
			core.material.icons.hero[one].midFoot = 2;
		});

		var heroMoving = function (timestamp) {
			if (core.status.heroMoving <= 0) return;
			if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) {
				core.animateFrame.leftLeg++;
				core.animateFrame.moveTime = timestamp;
			}
			core.drawHero(['stop', 'leftFoot', 'midFoot', 'rightFoot'][core.animateFrame.leftLeg % 4], 4 * core.status.heroMoving);
		}
		core.registerAnimationFrame('heroMoving', true, heroMoving);

		core.events._eventMoveHero_moving = function (step, moveSteps) {
			var curr = moveSteps[0];
			var direction = curr[0], x = core.getHeroLoc('x'), y = core.getHeroLoc('y');
			// ------ 前进/后退
			var o = direction == 'backward' ? -1 : 1;
			if (direction == 'forward' || direction == 'backward') direction = core.getHeroLoc('direction');
			var faceDirection = direction;
			if (direction == 'leftup' || direction == 'leftdown') faceDirection = 'left';
			if (direction == 'rightup' || direction == 'rightdown') faceDirection = 'right';
			core.setHeroLoc('direction', direction);
			if (curr[1] <= 0) {
				core.setHeroLoc('direction', faceDirection);
				moveSteps.shift();
				return true;
			}
			if (step <= 4) core.drawHero('stop', 4 * o * step);
			else if (step <= 8) core.drawHero('leftFoot', 4 * o * step);
			else if (step <= 12) core.drawHero('midFoot', 4 * o * (step - 8));
			else if (step <= 16) core.drawHero('rightFoot', 4 * o * (step - 8)); // if (step == 8) {
			if (step == 8 || step == 16) {
				core.setHeroLoc('x', x + o * core.utils.scan2[direction].x, true);
				core.setHeroLoc('y', y + o * core.utils.scan2[direction].y, true);
				core.updateFollowers();
				curr[1]--;
				if (curr[1] <= 0) moveSteps.shift();
				core.setHeroLoc('direction', faceDirection);
				return step == 16;
			}
			return false;
		}
	},
    "routeFixing": function () {
		// 是否开启本插件，true 表示启用，false 表示禁用。
		var __enable = true;
		if (!__enable) return;
		/*
		 使用说明：启用本插件后，录像回放时您可以用数字键1或6分别切换到原速或24倍速，
		 暂停播放时按数字键7（电脑按N）可以单步播放。（手机端可以点击难度单词切换出数字键）
		 数字键2-5可以进行录像自助精修，具体描述见下（实际弹窗请求您输入时不要带有任何空格）：
		 
		 up down left right 勇士向某个方向「行走一步或撞击」
		 item:ID 使用某件道具，如 item:bomb 表示使用炸弹
		 unEquip:n 卸掉身上第(n+1)件装备（n从0开始），如 unEquip:1 默认表示卸掉盾牌
		 equip:ID 穿上某件装备，如 equip:sword1 表示装上铁剑
		 saveEquip:n 将身上的当前套装保存到第n套快捷套装（n从0开始）
		 loadEquip:n 快捷换上之前保存好的第n套套装
		 fly:ID 使用楼传飞到某一层，如 fly:MT10 表示飞到主塔10层
		 choices:none 确认框/选择项「超时」（作者未设置超时时间则此项视为缺失）
		 choices:n 确认框/选择项选择第(n+1)项（选择项n从0开始，确认框n为0表示「确定」，1表示「取消」）
		 选择项n为负数时表示选择倒数第 -n 项，如 -1 表示最后一项（V2.8.2起标准全局商店的「离开」项）
		 此项缺失的话，确认框将选择作者指定的默认项（初始光标位置），选择项将弹窗请求补选（后台录像验证中选最后一项，可以复写函数来修改）
		 shop:ID 打开某个全局商店，如 shop:itemShop 表示打开道具商店。因此连载塔千万不要中途修改商店ID！
		 turn 单击勇士（Z键）转身，core.turnHero() 会产生此项，因此通过事件等方式强制让勇士转向应该用 core.setHeroLoc()
		 turn:dir 勇士转向某个方向，dir 可以为 up down left right（此项一般是读取自动存档产生的，属于样板的不良特性，请勿滥用）
		 getNext 轻按获得身边道具，优先获得面前的（面前没有则按上下左右顺序依次获得），身边如果没有道具则此项会被跳过
		 input:none “等待用户操作事件”中超时（作者未设置超时时间则此项会导致报错）
		 input:xxx 可能表示“等待用户操作事件”的一个操作（如按键操作将直接记录 input:keycode ），
		 也可能表示一个“接受用户输入数字”的输入，后者的情况下 xxx 为输入的整数。此项缺失的话前者将直接报错，后者将用0代替（后者现在支持负数了）
		 input2:xxx 可能表示“读取全局存储（core.getGlobal）”读取到的值，也可能表示一个“接受用户输入文本”的输入，
		 两种情况下 xxx 都为 base64 编码。此项缺失的话前者将重新现场读取，后者将用空字符串代替
		 no 走到可穿透的楼梯上不触发楼层切换事件，通过本插件可以让勇士停在旁边没有障碍物的楼梯上哦～
		 move:x:y 尝试瞬移到 [x,y] 点（不改变朝向），该点甚至可以和勇士相邻或者位于视野外
		 key:n 松开键值为n的键，如 key:49 表示松开大键盘数字键1，默认会触发使用破墙镐
		 click:n:px:py 点击自绘状态栏，n为0表示横屏1表示竖屏，[px,py] 为点击的像素坐标
		 random:n 生成了随机数n，即 core.rand2(num) 的返回结果，n必须在 [0,num-1] 范围，num必须为正整数。此项缺失将导致现场重新随机生成数值，可能导致回放结果不一致！
		 作者自定义的新项（一般为js对象，可以先JSON.stringify()再core.encodeBase64()得到纯英文数字的内容）需要用(半角圆括弧)括起来。
		 
		 当您使用数字键5将一些项追加到即将播放内容的开头时，请注意要逆序逐项追加，或者每追加一项就按下数字键7或字母键N单步播放一步。
		 但是【input input2 random choices】是被动读取的，单步播放如果触发了相应的事件就会连续读取，这时候只能提前逐项追加好。
		 电脑端熟练以后推荐直接在控制台操作 core.status.route 和 core.status.replay.toReplay（后者录像回放时才有），配合 core.push() 和 core.unshift() 更加灵活自由哦！
		 */
		core.actions.registerAction('onkeyUp', '_sys_onkeyUp_replay', function (e) {
			if (this._checkReplaying()) {
				if (e.keyCode == 27) // ESCAPE
					core.stopReplay();
				else if (e.keyCode == 90) // Z
					core.speedDownReplay();
				else if (e.keyCode == 67) // C
					core.speedUpReplay();
				else if (e.keyCode == 32) // SPACE
					core.triggerReplay();
				else if (e.keyCode == 65) // A
					core.rewindReplay();
				else if (e.keyCode == 83) // S
					core.control._replay_SL();
				else if (e.keyCode == 88) // X
					core.control._replay_book();
				else if (e.keyCode == 33 || e.keyCode == 34) // PgUp/PgDn
					core.control._replay_viewMap();
				else if (e.keyCode == 78) // N
					core.stepReplay();
				else if (e.keyCode == 84) // T
					core.control._replay_toolbox();
				else if (e.keyCode == 81) // Q
					core.control._replay_equipbox();
				else if (e.keyCode == 66) // B
					core.ui._drawStatistics();
				else if (e.keyCode == 49 || e.keyCode == 54) // 1/6，原速/24倍速播放
					core.setReplaySpeed(e.keyCode == 49 ? 1 : 24);
				else if (e.keyCode > 49 && e.keyCode < 54) { // 2-5，录像精修
					switch (e.keyCode - 48) {
						case 2: // pop
							alert("您已移除已录制内容的最后一项：" + core.status.route.pop());
							break;
						case 3: // push
							core.utils.myprompt("请输入您要追加到已录制内容末尾的项：", "", function (value) {
								if (value != null) core.status.route.push(value);
							});
							break;
						case 4: // shift
							alert("您已移除即将播放内容的第一项：" + core.status.replay.toReplay.shift());
							break;
						case 5: // unshift
							core.utils.myprompt("请输入您要追加到即将播放内容开头的项：", "", function (value) {
								if (value != null) core.status.replay.toReplay.unshift(value);
							});
					}
				}
				return true;
			}
		}, 100);
	},
    "numpad": function () {
		// 样板自带的整数输入事件为白屏弹窗且可以误输入任意非法内容但不支持负整数，观感较差。本插件可以将其美化成仿RM样式，使其支持负整数同时带有音效
		// 另一方面，4399等第三方平台不允许使用包括 core.myprompt() 和 core.myconfirm() 在内的弹窗，因此也需要此插件来替代，不然类似生命魔杖的道具就不好实现了
		// 关于负整数输入，V2.8.2原生支持其录像的压缩和解压，只是默认的 core.events._action_input() 函数将负数取了绝对值，可以只复写下面的 core.isReplaying() 部分来取消

		// 是否启用本插件，false表示禁用，true表示启用
		var __enable = true;
		if (!__enable) return;

		core.events._action_input = function (data, x, y, prefix) { // 复写整数输入事件
			if (core.isReplaying()) { // 录像回放时，处理方式不变，但增加负整数支持
				core.events.__action_getInput(core.replaceText(data.text, prefix), false, function (value) {
					value = parseInt(value) || 0; // 去掉了取绝对值的步骤
					core.status.route.push("input:" + value);
					core.setFlag("input", value);
					core.doAction();
				});
			} else {
				// 正常游戏中，采用暂停录制的方式然后用事件流循环“绘制-等待-变量操作”三板斧实现（按照13*13适配的）。
				// 您可以自行修改循环内的内容来适配15*15或其他需求，或干脆作为公共事件编辑。
				core.insertAction([
					// 记录当前录像长度，下面的循环结束后裁剪。达到“暂停录制”的效果
					{ "type": "function", "function": "function(){flags['@temp@length']=core.status.route.length}" },
					{ "type": "setValue", "name": "flag:input", "value": "0" },
					{
						"type": "while",
						"condition": "true",
						"data": [
							{ "type": "drawBackground", "background": "winskin.png", "x": 16, "y": 16, "width": 384, "height": 384 },
							{ "type": "drawIcon", "id": "X10181", "x": 32, "y": 288 },
							{ "type": "drawIcon", "id": "X10185", "x": 64, "y": 288 },
							{ "type": "drawIcon", "id": "X10186", "x": 96, "y": 288 },
							{ "type": "drawIcon", "id": "X10187", "x": 128, "y": 288 },
							{ "type": "drawIcon", "id": "X10188", "x": 160, "y": 288 },
							{ "type": "drawIcon", "id": "X10189", "x": 192, "y": 288 },
							{ "type": "drawIcon", "id": "X10193", "x": 224, "y": 288 },
							{ "type": "drawIcon", "id": "X10194", "x": 256, "y": 288 },
							{ "type": "drawIcon", "id": "X10195", "x": 288, "y": 288 },
							{ "type": "drawIcon", "id": "X10196", "x": 320, "y": 288 },
							{ "type": "drawIcon", "id": "X10197", "x": 352, "y": 288 },
							{ "type": "drawIcon", "id": "X10286", "x": 32, "y": 352 },
							{ "type": "drawIcon", "id": "X10169", "x": 96, "y": 352 },
							{ "type": "drawIcon", "id": "X10232", "x": 128, "y": 352 },
							{ "type": "drawIcon", "id": "X10185", "x": 320, "y": 352 },
							{ "type": "drawIcon", "id": "X10242", "x": 352, "y": 352 },
							{ "type": "fillBoldText", "x": 48, "y": 256, "style": [255, 255, 255, 1], "font": "bold 32px Consolas", "text": "${flag:input}" },
							{ "type": "fillBoldText", "x": 32, "y": 48, "style": [255, 255, 255, 1], "font": "16px Consolas", "text": core.replaceText(data.text, prefix) },
							{
								"type": "wait",
								"forceChild": true,
								"data": [{
									"case": "keyboard",
									"keycode": "48,49,50,51,52,53,54,55,56,57",
									"action": [
										// 按下数字键，追加到已输入内容的末尾，但禁止越界。变量：keycode-48就是末位数字
										{ "type": "playSound", "name": "光标移动" },
										{
											"type": "if",
											"condition": "(flag:input<0)",
											"true": [
												{ "type": "setValue", "name": "flag:input", "value": "10*flag:input-(flag:keycode-48)" },
											],
											"false": [
												{ "type": "setValue", "name": "flag:input", "value": "10*flag:input+(flag:keycode-48)" },
											]
										},
										{ "type": "setValue", "name": "flag:input", "value": "core.clamp(flag:input,-9e15,9e15)" },
									]
								},
								{
									"case": "keyboard",
									"keycode": "189",
									"action": [
										// 按下减号键，变更已输入内容的符号
										{ "type": "playSound", "name": "跳跃" },
										{ "type": "setValue", "name": "flag:input", "value": "-flag:input" },
									]
								},
								{
									"case": "keyboard",
									"keycode": "8",
									"action": [
										// 按下退格键，从已输入内容的末尾删除一位
										{ "type": "playSound", "name": "取消" },
										{ "type": "setValue", "name": "flag:input", "operator": "//=", "value": "10" },
									]
								},
								{
									"case": "keyboard",
									"keycode": "27",
									"action": [
										// 按下ESC键，清空已输入内容
										{ "type": "playSound", "name": "读档" },
										{ "type": "setValue", "name": "flag:input", "value": "0" },
									]
								},
								{
									"case": "keyboard",
									"keycode": "13",
									"action": [
										// 按下回车键，确定
										{ "type": "break", "n": 1 },
									]
								},
								{
									"case": "mouse",
									"px": [32, 63],
									"py": [288, 320],
									"action": [
										// 点击减号，变号。右边界写63防止和下面重叠
										{ "type": "playSound", "name": "跳跃" },
										{ "type": "setValue", "name": "flag:input", "value": "-flag:input" },
									]
								},
								{
									"case": "mouse",
									"px": [64, 384],
									"py": [288, 320],
									"action": [
										// 点击数字，追加到已输入内容的末尾，但禁止越界。变量：x-2就是末位数字
										{ "type": "playSound", "name": "光标移动" },
										{
											"type": "if",
											"condition": "(flag:input<0)",
											"true": [
												{ "type": "setValue", "name": "flag:input", "value": "10*flag:input-(flag:x-2)" },
											],
											"false": [
												{ "type": "setValue", "name": "flag:input", "value": "10*flag:input+(flag:x-2)" },
											]
										},
										{ "type": "setValue", "name": "flag:input", "value": "core.clamp(flag:input,-9e15,9e15)" },
									]
								},
								{
									"case": "mouse",
									"px": [32, 64],
									"py": [352, 384],
									"action": [
										// 点击左箭头，退格
										{ "type": "playSound", "name": "取消" },
										{ "type": "setValue", "name": "flag:input", "operator": "//=", "value": "10" },
									]
								},
								{
									"case": "mouse",
									"px": [96, 160],
									"py": [352, 384],
									"action": [
										// 点击CE，清空
										{ "type": "playSound", "name": "读档" },
										{ "type": "setValue", "name": "flag:input", "value": "0" },
									]
								},
								{
									"case": "mouse",
									"px": [320, 384],
									"py": [352, 384],
									"action": [
										// 点击OK，确定
										{ "type": "break", "n": 1 },
									]
								}
								]
							}
						]
					},
					{ "type": "clearMap" },
					// 裁剪录像，只保留'input:n'，然后继续录制
					{ "type": "function", "function": "function(){core.status.route.splice(flags['@temp@length']);core.status.route.push('input:'+core.getFlag('input',0))}" }
				], x, y);
				core.events.doAction();
			}
		}
	},
    "sprites": function () {
		// 基于canvas的sprite化，摘编整理自万宁魔塔
		// 
		// ---------------------------------------- 第一部分 js代码 （必装） --------------------------------------- //

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

		var count = 0;

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

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

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

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

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

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

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

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

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

		window.Sprite = Sprite;
	},
    "hotReload": function () {
		/* ---------- 功能说明 ---------- *

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

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

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

		*/

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

		/**
		 * 发送请求
		 * @param {string} url
		 * @param {string} type
		 * @param {string} data
		 * @returns {Promise<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 () {
	var origin = core.control.updateStatusBar;
	core.updateStatusBar = core.control.updateStatusBar = function () {
		if (core.getFlag('__statistics__')) return;
		else return origin.apply(core.control, arguments);
	}

	core.bigmap.threshold = 256;

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

		// 没有怪物手册
		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);
		core.getItemDetail(floorId); // 宝石血瓶详细信息
		this.drawDamage(ctx);
	};
	// 绘制地图显示
	control.prototype._drawDamage_draw = function (ctx, onMap) {
		if (!core.hasItem('book')) return;
		// *** 下一句话可以更改你想要的显示字体
		core.setFont(ctx, "bold 11px Arial");
		// ***
		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.__PX__ + 32 || py < -32 || py > core.__PY__ + 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.__PX__ + 32 || py < -32 || py > core.__PY__ + 32)
					return;
			}
			core.fillBoldText(ctx, one.text, px, py, one.color);
		});
	};
	// 获取宝石信息 并绘制
	this.getItemDetail = function (floorId) {
		if (!core.getFlag("itemDetail")) return;
		floorId = floorId || core.status.thisMap.floorId;
		core.status.maps[floorId].blocks.forEach(function (block) {
			if (block.event.cls !== 'items' || block.event.id === 'superPotion') return;
			var x = block.x,
				y = block.y;
			// v2优化，只绘制范围内的部分
			if (core.bigmap.v2) {
				if (x < core.bigmap.posX - core.bigmap.extend || x > core.bigmap.posX + core.__SIZE__ + core.bigmap.extend ||
					y < core.bigmap.posY - core.bigmap.extend || y > core.bigmap.posY + core.__SIZE__ + core.bigmap.extend) {
					return;
				}
			}
			var id = block.event.id;
			var item = core.material.items[id];
			if (item.cls === 'equips') {
				// 装备也显示
				var diff = core.clone(item.equip.value || {});
				var per = item.equip.percentage;
				for (var name in per) {
					diff[name + 'per'] = per[name].toString() + '%';
				}
				drawItemDetail(diff, x, y);
				return;
			}
			var before = core.clone(core.status.hero);
			// 跟数据统计原理一样 执行效果 前后比较
			core.setFlag("__statistics__", true);
			try {
				eval(item.itemEffect);
			} catch (error) {}
			var diff = compareObject(before, core.status.hero);
			core.status.hero = hero = before;
			flags = core.status.hero.flags;
			drawItemDetail(diff, x, y);
		});
	};
	// 比较两个对象之间每一项的数值差异（弱等于） 返回数值差异
	function compareObject(a, b) {
		a = a || {};
		b = b || {};
		var diff = {}; // 差异
		for (var name in a) {
			diff[name] = b[name] - (a[name] || 0);
			if (!diff[name]) diff[name] = void 0;
		}
		return diff;
	};
	// 绘制
	function drawItemDetail(diff, x, y) {
		var px = 32 * x + 2,
			py = 32 * y + 30;
		var content = "";
		// 获得数据和颜色
		var i = 0;
		for (var name in diff) {
			if (!diff[name]) continue;
			var color = "#ffffff";
			if (typeof diff[name] === 'number')
				diff[name] = core.formatBigNumber(diff[name], true);
			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 = "#cc6666";
				break;
			}
			content = diff[name];
			// 绘制
			core.status.damage.data.push({ text: content, px: px, py: py - 10 * i, color: color });
			i++;
		}
	}
},
    "道具": 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("var id = '" + id + "';\ncore.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);
					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) {
			var type = info.select.type;
			id = core.getEquip(type);
			info.index = null;
			info.select = {
				id: id,
				action: "unload",
				type: type
			};
			return;
		} else {
			info.select.type = null;
		}
		if (toChange == "index") info.index = items.indexOf(info.select.id) + 1;
		else {
			var id = info.pageItems[index - 1];
			info.select.id = id;
		}
	}

	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 = {};
			return core.addItemListboxPage(0);
		} else {
			var m = Math.abs(info.select.type);
			if (info.select.type < 0) info.select.type = max - m;
			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();
		core.drawToolbox();
	}

	core.events.openEquipbox = function (fromUserAction) {
		if (core.isReplaying()) return;
		if (!this._checkStatus('equipbox', fromUserAction)) return;
		core.initThisEventInfo();
		core.drawEquipbox();
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

			var width = core.floors[floorId].width,
				height = core.floors[floorId].height;
			var startX = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posX - core.bigmap.extend) : 0;
			var endX = onMap && core.bigmap.v2 ? Math.min(width, core.bigmap.posX + core.__SIZE__ + core.bigmap.extend + 1) : width;
			var startY = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posY - core.bigmap.extend) : 0;
			var endY = onMap && core.bigmap.v2 ? Math.min(height, core.bigmap.posY + core.__SIZE__ + core.bigmap.extend + 1) : height;
			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 - 1, y: y, text: damage, px: px - 32, py: py, color: '#ffaa33', alpha: alpha });
							if (!find(x + 1, y)) core.status.damage.extraData.push({ x: x + 1, y: y, text: damage, px: px + 32, py: py, color: '#ffaa33', alpha: alpha });
						} else if (top) {
							if (!find(x, y - 1)) core.status.damage.extraData.push({ x: x, y: y - 1, text: damage, px: px, py: py - 32, color: '#ffaa33', alpha: alpha });
							if (!find(x, y + 1)) core.status.damage.extraData.push({ x: x, y: y + 1, text: damage, px: px, py: py + 32, 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 () {
	//如果检测到怪物，这个子程序给绘制初始化
	this.startEnemyInfoDisplay = function (enemy_x, enemy_y) {
		//我们要提前获得特殊属性以判断窗口高度
		var mon_id = core.getBlockId(enemy_x, enemy_y);
		var special = core.getSpecialText(mon_id);
		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 = 168 + Math.ceil(special.length / 2 + extra_line) * 20;
		//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.getEnemys();
		//在下面设置你的字体和字体大小
		var font = "Arial"; //这里"......"填写你的字体。如果不修改的话，默认Arial字体。
		var fontSize = 15;
		//绘制窗口
		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, mon_id, fontSize, font);
		core.plugin.addEnemyInfo(info, 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 + 22,
			[255, 255, 255, 1], "Bold " + fontSize + "px " + font);
		core.drawIcon('enemyInfo', mon_id, cell_x + width / 2 - 16, cell_y + 30);
		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);
	}

	//这个子程序调整字体的亮度
	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, 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 + 98 +
					10 * i, core.arrayToRGBA(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.getSpecialColor(mon_id)[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 + 98 + 10 * i, core.arrayToRGBA(core.getSpecialColor(mon_id)[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 (info, 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");
		*/

		core.setTextAlign('enemyInfo', 'left');
		core.fillText("enemyInfo", "生命", cell_x + 8,
			cell_y + 118 + 10 * i, [192, 224, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", "攻击", cell_x + 8,
			cell_y + 138 + 10 * i, [192, 224, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", "防御", cell_x + 8,
			cell_y + 158 + 10 * i, [192, 224, 255, 1], fontSize + "px " + font);
		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].hp, true), cell_x + width - 8,
			cell_y + 118 + 10 * i, [255, 255, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", core.formatBigNumber(info[mon_id].atk, true), cell_x + width - 8,
			cell_y + 138 + 10 * i, [255, 255, 255, 1], fontSize + "px " + font);
		core.fillText("enemyInfo", core.formatBigNumber(info[mon_id].def, true), cell_x + width - 8,
			cell_y + 158 + 10 * i, [255, 255, 255, 1], fontSize + "px " + font);

		//这里单独注释一下，如果金币经验>1000且<100w文字会覆盖，处理了一下
		[displayMoney, displayEXP] = this.ThousandTransform(info, mon_id);
		core.fillText("enemyInfo", displayMoney + "/" + displayEXP, cell_x + width - 8,
			cell_y + 178 + 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 (info, mon_id) {
		var displayMoney = "",
			displayEXP = "";
		if (info[mon_id].money >= 1000 && info[mon_id].money < 100000) {
			displayMoney = info[mon_id].money / 1000;
			displayMoney = displayMoney.toString() + "k";
		} else if (info[mon_id].money >= 100000 && info[mon_id].money < 1000000) {
			displayMoney = info[mon_id].money / 10000;
			displayMoney = displayMoney.toString() + "w";
		} else
			displayMoney = core.formatBigNumber(info[mon_id].money, true);
		if (info[mon_id].exp >= 1000 && info[mon_id].exp < 100000) {
			displayEXP = info[mon_id].exp / 1000;
			displayEXP = displayEXP.toString() + "k";
		} else if (info[mon_id].exp >= 100000 && info[mon_id].exp < 1000000) {
			displayEXP = info[mon_id].exp / 10000;
			displayEXP = displayEXP.toString() + "w";
		} else
			displayEXP = core.formatBigNumber(info[mon_id].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), core.__SIZE__ - 1 + core.bigmap.offsetX / (32 * core.domStyle.scale));
			var enemy_y = core.clamp(Math.floor((mouse_y + offsetY / (32 * core.domStyle.scale))), core.bigmap.offsetY /
				(32 * core.domStyle.scale), core.__SIZE__ - 1 + core.bigmap.offsetY / (32 * core.domStyle.scale));
			//如果该图块有怪物，则绘制窗口
			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 = null;
		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");
	}
}
}