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

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

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

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

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

	var outerBackground = document.createElement('canvas');
	outerBackground.style.position = 'absolute';
	outerBackground.style.zIndex = 5;
	outerBackground.width = 640;
	outerBackground.height = 422;
	outerBackground.id = 'outerBackground';
	main.dom.outerBackground = outerBackground;
	main.dom.startPanel.insertAdjacentElement('afterend', outerBackground);

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

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

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

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

	var _resize_canvas = function (obj) {
		main.dom.outerBackground.style.width = obj.totalWidth + 'px';
		main.dom.outerBackground.style.height = obj.totalHeight + 'px';
		main.dom.outerUI.style.width = obj.totalWidth + 'px';
		main.dom.outerUI.style.height = obj.totalHeight + 'px';

		var innerSize = (obj.canvasWidth * core.domStyle.scale) + "px";
		for (var i = 0; i < core.dom.gameCanvas.length; ++i)
			core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[i].style.height = innerSize;
		core.dom.gif.style.width = core.dom.gif.style.height = innerSize;
		core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize;
		core.dom.gameDraw.style.width = core.dom.gameDraw.style.height = innerSize;
		core.dom.gameDraw.style.top = obj.borderHeight * core.domStyle.scale + "px";
		core.dom.gameDraw.style.right = (obj.borderWidth + obj.barWidth + 1) * core.domStyle.scale + "px";

		// resize bigmap
		core.bigmap.canvas.forEach(function (cn) {
			var ratio = core.canvas[cn].canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1;
			core.canvas[cn].canvas.style.width = core.canvas[cn].canvas.width / ratio * core.domStyle.scale + "px";
			core.canvas[cn].canvas.style.height = core.canvas[cn].canvas.height / ratio * core.domStyle.scale + "px";
		});
		// resize dynamic canvas
		for (var name in core.dymCanvas) {
			var ctx = core.dymCanvas[name],
				canvas = ctx.canvas;
			var ratio = canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1;
			canvas.style.width = canvas.width / ratio * core.domStyle.scale + "px";
			canvas.style.height = canvas.height / ratio * core.domStyle.scale + "px";
			canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px";
			canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px";
		}
		// resize next
		main.dom.next.style.width = main.dom.next.style.height = 5 * core.domStyle.scale + "px";
		main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px";

	}

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

		var clientWidth = main.dom.body.clientWidth,
			clientHeight = main.dom.body.clientHeight;
		var canvasWidth = core.__PIXELS__,
			barWidth = 125;
		var borderWidth = 19,
			borderHeight = 24,
			infoBarHeight = 22;

		var horizontalMaxRatio = clientHeight / (canvasWidth + infoBarHeight + 2 * borderHeight);

		core.domStyle.isVertical = false;
		core.domStyle.availableScale = [];
		[1, 1.25, 1.5, 1.75, 2].forEach(function (v) {
			if (clientWidth >= v * (canvasWidth + barWidth * 2 + borderWidth * 2) && horizontalMaxRatio >= v) {
				core.domStyle.availableScale.push(v);
			}
		});

		if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) {
			core.domStyle.scale = Math.min(1, horizontalMaxRatio);
		} else if (core.getLocalStorage('scale') == null && core.domStyle.availableScale.length >= 2) {
			core.domStyle.scale = core.domStyle.availableScale[core.domStyle.availableScale.length - 2];
			core.setLocalStorage('scale', core.domStyle.scale);
		}

		var totalWidth = (canvasWidth + 2 * barWidth + 2 * borderWidth) * core.domStyle.scale;
		var totalHeight = (canvasWidth + 2 * borderHeight + infoBarHeight) * core.domStyle.scale;

		var obj = {
			clientWidth: clientWidth,
			clientHeight: clientHeight,
			canvasWidth: canvasWidth,
			barWidth: barWidth,
			borderWidth: borderWidth,
			borderHeight: borderHeight,
			infoBarHeight: infoBarHeight,
			totalWidth: totalWidth,
			totalHeight: totalHeight,
			globalAttribute: core.status.globalAttribute || core.initStatus.globalAttribute,
		};

		_resize_gameGroup(obj);
		_resize_canvas(obj);

		if (core.status.automaticRoute == null) core.status.automaticRoute = {};
		core.control.setViewport(32, 32);
		core.updateStatusBar();
	}

	function statusBar() {
		this.itemMx = [
			["book", "wand", "fly"],
			["cross", "superPotion", "pickaxe"],
			["bomb", "centerFly", "upFly"],
			["downFly", "knife", "snow"],
			["bigKey", "earthquake", "coin"],
		];
	}

	statusBar.prototype.init = function () {
		var bgctx = document.getElementById("outerBackground").getContext("2d");
		var bg = core.material.images.images["statusBackground.jpg"];
		bgctx.drawImage(bg, 0, 0, 640, 400);
		bgctx.fillStyle = "#676767";
		bgctx.fillRect(0, 400, 640, 22);
		core.setTextAlign('outerUI', 'center');

		this.toolbarAction = [
			[main.core.openKeyBoard, main.core.openQuickShop, function () { core.insertAction([{ type: "insert", name: "游戏说明" }]); }],
			[main.core.save, main.core.load, main.core.openSettings]
		];
		this.replayAction = [
			[core.triggerReplay, core.stopReplay, core.rewindReplay],
			[core.speedDownReplay, core.speedUpReplay, core.saveReplay]
		];
	}

	statusBar.prototype.update = function () {
		this._update_props();
		this._update_items();
		this._update_equips();
		this._update_keys();
		this._update_infoWindow();
	}

	// 更新属性
	statusBar.prototype._update_props = function (updatedFloorTitle) {
		core.clearMap("outerUI", 10, 40, 105, 130);
		var tc = "#E1E1E1";
		core.setFont("outerUI", 'bold 16px Verdana');
		// 设置楼层名
		if (!updatedFloorTitle && core.status.floorId) {
			updatedFloorTitle = core.status.maps[core.status.floorId].title;
		}
		if (updatedFloorTitle) {
			core.fillText("outerUI", updatedFloorTitle, 62, 61, tc);
		}

		var statusList = ['hp', 'atk', 'def', 'money'];
		var curh = 93;
		core.setTextAlign('outerUI', 'right');
		statusList.forEach(function (item) {
			// 四舍五入
			core.status.hero[item] = Math.round(core.status.hero[item]);
			// 大数据格式化
			core.fillText("outerUI", core.getRealStatus(item), 110, curh, tc);
			curh += 24;
		});
		core.setTextAlign('outerUI', 'center');
	}

	statusBar.prototype._update_items = function () {
		core.clearMap("outerUI", 10, 180, 105, 185);
		for (var i = 0; i < this.itemMx.length; i++) {
			for (var j = 0; j < this.itemMx[i].length; j++) {
				var item = this.itemMx[i][j];
				if (core.hasItem(item)) {
					var icon = core.material.icons.items[item],
						image = core.material.images.items;
					var posx = 13 + j * 33,
						posy = 190 + i * 32;
					core.drawImage('outerUI', image, 0, 32 * icon, 32, 32, posx, posy, 32, 32);
					//if (item == "centerFly")
					core.fillText('outerUI', core.itemCount(item), posx + 25, posy + 28, '#FFFFFF', "bold 12px Verdana");
					if (this.selectId == item)
						core.strokeRect('outerUI', posx + 17, posy - 4, 40, 40, '#FFD700');
				}
			}
		}
	}

	statusBar.prototype._update_equips = function () {
		core.clearMap("outerUI", 525, 40, 105, 95);
		core.setFont("outerUI", 'bold 16px Verdana');
		var drawEquip = function (baseX, baseY, id, color, back) {
			if (!id) core.fillText("outerUI", back, baseX + 50, baseY + 22, color);
			else {
				core.fillText("outerUI", core.material.items[id].name, baseX + 32, baseY + 22, color);
				var icon = core.material.icons.items[id];
				core.drawImage('outerUI', core.material.images.items, 0, 32 * icon, 32, 32, baseX + 64, baseY, 32, 32);
			}
		}
		drawEquip(525, 48, core.getFlag("nowWeapon"), "#FFCFAE", "无武器");
		drawEquip(525, 96, core.getFlag("nowShield"), "#D1CEFF", "无防具");
	}

	statusBar.prototype._update_keys = function () {
		core.clearMap("outerUI", 525, 140, 105, 75);
		var todraw = [],
			keyList = ['yellowKey', 'blueKey', 'redKey'];
		keyList.forEach(function (key, i) {
			todraw[i] = core.itemCount(key);
		});
		var baseX = 528,
			baseY = 142,
			dn = 2,
			dc = 0;
		while (dn >= 0 && dc < 28) {
			if (todraw[dn]) {
				this.drawKey(keyList[dn], baseX + (dc % 7) * 14, baseY + parseInt(dc / 7) * 17);
				todraw[dn]--, dc++;
			} else dn--;
		}
	}

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

	statusBar.prototype._update_infoWindow = function () {
		core.clearMap("outerUI", 525, 220, 105, 100);
		this.selectedItem = null;
	}

	statusBar.prototype.showInfo = function (itemId) {
		core.clearMap("outerUI", 525, 220, 105, 100);
		var icon = core.material.icons.items[itemId];
		core.fillText("outerUI", core.material.items[itemId].name, 525 + 32, 220 + 25, "#D1CEFF");
		core.drawImage('outerUI', core.material.images.items, 0, 32 * icon, 32, 32, 525 + 64, 224, 32, 32);
		core.ui.drawTextContent("outerUI", core.material.items[itemId].text, {
			left: 526,
			top: 256,
			maxWidth: 105,
			color: "#D1CEFF"
		});
		this.selectedItem = itemId;
	}

	statusBar.prototype._update_toolBox = function () {
		core.clearMap("outerUI", 520, 310, 115, 80);
		var tools = [];
		if (core.isReplaying()) {
			tools = [
				[core.status.replay.pausing ? "play" : "pause", "stop", "rewind"],
				["speedDown", "speedUp", "save"],
			]
		} else {
			tools = [
				["keyboard", "shop", "help"],
				["save", "load", "settings"],
			]
		}
		for (var i = 0; i < tools.length; i++) {
			for (var j = 0; j < tools[i].length; j++) {
				core.drawIcon("outerUI", tools[i][j], 525 + j * 34, 318 + i * 34, 32, 32);
			}
		}
	}

	statusBar.prototype.onclick = function (x, y) {
		if (x <= 112 && x >= 13 && y <= 350 && y >= 190) {
			if (core.isReplaying() || core.status.lockControl || core.isMoving()) return;
			var itemId = this.itemMx[parseInt((y - 190) / 32)][parseInt((x - 13) / 33)];
			if (!core.hasItem(itemId)) return;
			if (core.material.items[itemId].cls == "constants") {
				switch (itemId) {
				case "book":
					core.openBook(true);
					break;
				case "fly":
					core.useFly(true);
					break;
				case "wand":
					core.useItem("wand");
					break;
				case "snow":
					core.useItem("snow");
					break;
				default:
					this.showInfo(itemId);
				}
			} else if (itemId != this.selectedItem) {
				this.showInfo(itemId);
			} else {
				switch (itemId) {
				case "centerFly":
					core.ui.drawCenterFly();
					break;
				default:
					core.useItem(itemId);
				}
			}
			return;
		}
		if (x <= 625 && x >= 525 && y <= 382 && y >= 317) {
			var row = parseInt((x - 525) / 32),
				col = parseInt((y - 317) / 32);
			if (core.isReplaying()) {
				this.replayAction[col][row].call(core);
			} else if (core.isPlaying()) {
				this.toolbarAction[col][row].call(core, true);
			}
			return;
		}
	}

	statusBar.prototype.print = function (text, cnt) {
		if (!text) {
			var _this = this;
			setTimeout(function () {
				if (!_this.infocnt) core.clearMap("outerUI", 0, 400, 640, 22);
			}, 200);
			this.infocnt = 0;
			return;
		}
		core.clearMap("outerUI", 0, 400, 640, 22);
		core.setFont("outerUI", 'bold 16px Verdana');
		core.setTextAlign('outerUI', 'left');
		core.fillText("outerUI", text, 10, 416, "#E1E1E1");
		core.setTextAlign('outerUI', 'center');
		this.infocnt = cnt || 1;
	}

	statusBar.prototype.infoRules = [
		{ id: "lava", text: "岩浆好热啊!" },
		{ id: "upFloor", text: "你看到了楼梯" },
		{ id: "downFloor", text: "你看到了楼梯" },
		{ id: "blueShop", text: "你看到了一个祭坛" },
		{ id: "man", text: "你看到了一个老人" },
		{ id: "woman", text: "你看到了一个商人" },
		{ id: "fairy", text: "你看到了一个商人" },
		{ id: "thief", text: "你看到了一个小偷" },
	]

	statusBar.prototype.printEnvironmentInfo = function () {
		if (!this.infocnt) {
			var ids = [];
			for (var i in core.status.thisMap.blocks) {
				var block = core.status.thisMap.blocks[i];
				if (!block.disable && core.nearHero(block.x, block.y)) ids.push(block.event.id);
			}
			for (var i in this.infoRules) {
				if (ids.indexOf(this.infoRules[i].id) >= 0) {
					this.print(this.infoRules[i].text);
					return;
				}
			}
		}
	}

	statusBar.prototype.clearInfo = function (etype) {
		if (this.infocnt == 1) this.print();
		else if (this.infocnt > 1) this.infocnt--;
	}

	core.ui.statusBar = new statusBar();

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

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

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

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

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

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

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

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

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

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

		var width = core.floors[floorId].width;
		var height = core.floors[floorId].height;
		// 绘制到tempCanvas上面
		var tempCanvas = core.bigmap.tempCanvas;
		options.v2 = false;
		tempCanvas.canvas.width = width * 32;
		tempCanvas.canvas.height = height * 32;
		options.ctx = tempCanvas;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		var color = '#000000';

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

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

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

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

	core.control.checkBlock = function () {
		var x = core.getHeroLoc('x'),
			y = core.getHeroLoc('y'),
			loc = x + "," + y;
		var damage = core.status.checkBlock.damage[loc];
		if (damage) {
			core.status.hero.hp -= damage;
			core.drawTip("你没有神圣盾不能防御，受到 " + damage + " 点魔法伤害");
			core.playSound('zone.mp3');
			this._checkBlock_disableQuickShop();
			core.status.hero.statistics.extraDamage += damage;
			if (core.status.hero.hp <= 0) {
				core.status.hero.hp = 0;
				core.updateStatusBar();
				core.events.lose();
				return;
			} else {
				core.updateStatusBar();
			}
		}
		this._checkBlock_ambush(core.status.checkBlock.ambush[loc]);
		this._checkBlock_repulse(core.status.checkBlock.repulse[loc]);
	}

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

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

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

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

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

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

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

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

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

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

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

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

	if (window.jsinterface && window.jsinterface.requestLandscape) {
		window.jsinterface.requestLandscape();
	}
},
    "shop": function () {
	// 【全局商店】相关的功能
	// 
	// 打开一个全局商店
	// shopId：要打开的商店id；noRoute：是否不计入录像
	this.openShop = function (shopId, noRoute) {
		var shop = core.status.shops[shopId];
		// Step 1: 检查能否打开此商店
		if (!this.canOpenShop(shopId)) {
			core.drawTip("该商店尚未开启");
			return false;
		}

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

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

		_shouldProcessKeyUp = true;

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

	////// 将一个全局商店转变成可预览的公共事件 //////
	this._convertShop = function (shop) {
		return [
			{ "type": "function", "function": "function() {core.addFlag('@temp@shop', 1);}" },
			{
				"type": "while",
				"condition": "true",
				"data": [
					// 检测能否访问该商店
					{
						"type": "if",
						"condition": "core.isShopVisited('" + shop.id + "')",
						"true": [
							// 可以访问，直接插入执行效果
							{ "type": "function", "function": "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', false) }" },
						],
						"false": [
							// 不能访问的情况下：检测能否预览
							{
								"type": "if",
								"condition": shop.disablePreview,
								"true": [
									// 不可预览，提示并退出
									{ "type": "playSound", "name": "操作失败" },
									"当前无法访问该商店！",
									{ "type": "break" },
								],
								"false": [
									// 可以预览：将商店全部内容进行替换
									{ "type": "tip", "text": "当前处于预览模式，不可购买" },
									{ "type": "function", "function": "function() { core.plugin._convertShop_replaceChoices('" + shop.id + "', true) }" },
								]
							}
						]
					}
				]
			},
			{ "type": "function", "function": "function() {core.addFlag('@temp@shop', -1);}" }
		];
	}

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

	/// 是否访问过某个快捷商店
	this.isShopVisited = function (id) {
		if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {});
		var shops = core.getFlag("__shops__");
		if (!shops[id]) shops[id] = {};
		return shops[id].visited;
	}

	/// 当前应当显示的快捷商店列表
	this.listShopIds = function () {
		return Object.keys(core.status.shops).filter(function (id) {
			return core.isShopVisited(id) || !core.status.shops[id].mustEnable;
		});
	}

	/// 是否能够打开某个商店
	this.canOpenShop = function (id) {
		if (this.isShopVisited(id)) return true;
		var shop = core.status.shops[id];
		if (shop.item || shop.commonEvent || shop.mustEnable) return false;
		return true;
	}

	/// 启用或禁用某个快捷商店
	this.setShopVisited = function (id, visited) {
		if (!core.hasFlag("__shops__")) core.setFlag("__shops__", {});
		var shops = core.getFlag("__shops__");
		if (!shops[id]) shops[id] = {};
		if (visited) shops[id].visited = true;
		else delete shops[id].visited;
	}

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

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

	var _shouldProcessKeyUp = true;

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

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

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

	// 允许长按屏幕连续执行操作
	core.registerAction('longClick', 'shops', function (x, y, px, py) {
		if (!core.status.lockControl || !core.hasFlag("@temp@shop") || core.status.event.id != 'action') return false;
		if (core.status.event.data.type != 'choices') return false;
		var data = core.status.event.data.current;
		var choices = data.choices;
		var topIndex = core.actions._getChoicesTopIndex(choices.length);
		if (x >= core.actions.CHOICES_LEFT && x <= core.actions.CHOICES_RIGHT && y >= topIndex && y < topIndex + choices.length) {
			core.actions._clickAction(x, y);
			return true;
		}
		return false;
	}, 60);
},
    "draw": function () {
	// 在此增加新插件
	// -------------------- 插件说明 -------------------- //

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

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

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

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

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

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

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

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

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

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

// 5. 执行动画

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// 6. 自定义动画属性

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

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

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

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

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

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

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

// 7. 运行动画

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

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

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

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

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

// 9. 多个属性绑定

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

var M = Object.defineProperty;
var E = (n, s, t) => s in n ? M(n, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[s] = t;
var o = (n, s, t) => (E(n, typeof s != "symbol" ? s + "" : s, t), t);
let b = [];
const k = (n) => {
  for (const s of b)
    if (s.status === "running")
      try {
        for (const t of s.funcs)
          t(n - s.startTime);
      } catch (t) {
        s.destroy(), console.error(t);
      }
  requestAnimationFrame(k);
};
requestAnimationFrame(k);
class I {
  constructor() {
    o(this, "funcs", []);
    o(this, "status", "stop");
    o(this, "startTime", 0);
    this.status = "running", b.push(this), requestAnimationFrame((s) => this.startTime = s);
  }
  add(s, t = !1) {
    return t ? this.funcs.unshift(s) : this.funcs.push(s), this;
  }
  remove(s) {
    const t = this.funcs.findIndex((e) => e === s);
    if (t === -1)
      throw new ReferenceError(
        "You are going to remove nonexistent ticker function."
      );
    return this.funcs.splice(t, 1), this;
  }
  clear() {
    this.funcs = [];
  }
  destroy() {
    this.clear(), this.stop();
  }
  stop() {
    this.status = "stop", b = b.filter((s) => s !== this);
  }
}
class F {
  constructor() {
    o(this, "timing");
    o(this, "relation", "absolute");
    o(this, "easeTime", 0);
    o(this, "applying", {});
    o(this, "getTime", Date.now);
    o(this, "ticker", new I());
    o(this, "value", {});
    o(this, "listener", {});
    this.timing = (s) => s;
  }
  async all() {
    if (Object.values(this.applying).every((s) => s === !0))
      throw new ReferenceError("There is no animates to be waited.");
    await new Promise((s) => {
      const t = () => {
        Object.values(this.applying).every((e) => e === !1) && (this.unlisten("end", t), s("all animated."));
      };
      this.listen("end", t);
    });
  }
  async n(s) {
    const t = Object.values(this.applying).filter((i) => i === !0).length;
    if (t < s)
      throw new ReferenceError(
        `You are trying to wait ${s} animate, but there are only ${t} animate animating.`
      );
    let e = 0;
    await new Promise((i) => {
      const r = () => {
        e++, e === s && (this.unlisten("end", r), i(`${s} animated.`));
      };
      this.listen("end", r);
    });
  }
  async w(s) {
    if (this.applying[s] === !1)
      throw new ReferenceError(`The ${s} animate is not animating.`);
    await new Promise((t) => {
      const e = () => {
        this.applying[s] === !1 && (this.unlisten("end", e), t(`${s} animated.`));
      };
      this.listen("end", e);
    });
  }
  listen(s, t) {
    var e, i;
    (i = (e = this.listener)[s]) != null || (e[s] = []), this.listener[s].push(t);
  }
  unlisten(s, t) {
    const e = this.listener[s].findIndex((i) => i === t);
    if (e === -1)
      throw new ReferenceError(
        "You are trying to remove a nonexistent listener."
      );
    this.listener[s].splice(e, 1);
  }
  hook(...s) {
    const t = Object.entries(this.listener).filter(
      (e) => s.includes(e[0])
    );
    for (const [e, i] of t)
      for (const r of i)
        r(this, e);
  }
}
function T(n) {
  return n != null;
}
async function R(n) {
  return new Promise((s) => setTimeout(s, n));
}
class Y extends F {
  constructor() {
    super();
    o(this, "shakeTiming");
    o(this, "path");
    o(this, "multiTiming");
    o(this, "value", {});
    o(this, "size", 1);
    o(this, "angle", 0);
    o(this, "targetValue", {
      system: {
        move: [0, 0],
        moveAs: [0, 0],
        resize: 0,
        rotate: 0,
        shake: 0,
        "@@bind": []
      },
      custom: {}
    });
    o(this, "animateFn", {
      system: {
        move: [() => 0, () => 0],
        moveAs: () => 0,
        resize: () => 0,
        rotate: () => 0,
        shake: () => 0,
        "@@bind": () => 0
      },
      custom: {}
    });
    o(this, "ox", 0);
    o(this, "oy", 0);
    o(this, "sx", 0);
    o(this, "sy", 0);
    o(this, "bindInfo", []);
    this.timing = (t) => t, this.shakeTiming = (t) => t, this.multiTiming = (t) => [t, t], this.path = (t) => [t, t], this.applying = {
      move: !1,
      scale: !1,
      rotate: !1,
      shake: !1
    }, this.ticker.add(() => {
      const { running: t } = this.listener;
      if (T(t))
        for (const e of t)
          e(this, "running");
    });
  }
  get x() {
    return this.ox + this.sx;
  }
  get y() {
    return this.oy + this.sy;
  }
  mode(t, e = !1) {
    return typeof t(0) == "number" ? e ? this.shakeTiming = t : this.timing = t : this.multiTiming = t, this;
  }
  time(t) {
    return this.easeTime = t, this;
  }
  relative() {
    return this.relation = "relative", this;
  }
  absolute() {
    return this.relation = "absolute", this;
  }
  bind(...t) {
    return this.applying["@@bind"] === !0 && this.end(!1, "@@bind"), this.bindInfo = t, this;
  }
  unbind() {
    return this.applying["@@bind"] === !0 && this.end(!1, "@@bind"), this.bindInfo = [], this;
  }
  move(t, e) {
    return this.applying.move && this.end(!0, "move"), this.applySys("ox", t, "move"), this.applySys("oy", e, "move"), this;
  }
  rotate(t) {
    return this.applySys("angle", t, "rotate"), this;
  }
  scale(t) {
    return this.applySys("size", t, "resize"), this;
  }
  shake(t, e) {
    this.applying.shake === !0 && this.end(!0, "shake"), this.applying.shake = !0;
    const { easeTime: i, shakeTiming: r } = this, h = this.getTime();
    if (this.hook("start", "shakestart"), i <= 0)
      return this.end(!1, "shake"), this;
    const l = () => {
      const c = this.getTime() - h;
      if (c > i) {
        this.ticker.remove(l), this.applying.shake = !1, this.sx = 0, this.sy = 0, this.hook("end", "shakeend");
        return;
      }
      const a = c / i, m = r(a);
      this.sx = m * t, this.sy = m * e;
    };
    return this.ticker.add(l), this.animateFn.system.shake = l, this;
  }
  moveAs(t) {
    this.applying.moveAs && this.end(!0, "moveAs"), this.applying.moveAs = !0, this.path = t;
    const { easeTime: e, relation: i, timing: r } = this, h = this.getTime(), [l, u] = [this.x, this.y], [c, a] = (() => {
      if (i === "absolute")
        return t(1);
      {
        const [d, f] = t(1);
        return [l + d, u + f];
      }
    })();
    if (this.hook("start", "movestart"), e <= 0)
      return this.end(!1, "moveAs"), this;
    const m = () => {
      const f = this.getTime() - h;
      if (f > e) {
        this.end(!0, "moveAs");
        return;
      }
      const v = f / e, [g, w] = t(r(v));
      i === "absolute" ? (this.ox = g, this.oy = w) : (this.ox = l + g, this.oy = u + w);
    };
    return this.ticker.add(m, !0), this.animateFn.system.moveAs = m, this.targetValue.system.moveAs = [c, a], this;
  }
  register(t, e) {
    if (typeof this.value[t] == "number")
      return this.error(
        `Property ${t} has been regietered twice.`,
        "reregister"
      );
    this.value[t] = e, this.applying[t] = !1;
  }
  apply(t, e, i = !1) {
    this.applying[t] === !0 && this.end(!1, t), t in this.value || this.error(
      `You are trying to execute nonexistent property ${t}.`
    ), this.applying[t] = !0;
    const r = this.value[t], h = this.getTime(), { timing: l, relation: u, easeTime: c } = this, a = u === "absolute" ? e - r : e;
    if (this.hook("start"), c <= 0)
      return this.end(!1, t), this;
    const m = () => {
      const f = this.getTime() - h;
      if (f > c) {
        this.end(!1, t);
        return;
      }
      const v = f / c, g = l(v);
      this.value[t] = r + g * a;
    };
    return this.ticker.add(m, i), this.animateFn.custom[t] = m, this.targetValue.custom[t] = a + r, this;
  }
  applyMulti(t = !1) {
    this.applying["@@bind"] === !0 && this.end(!1, "@@bind"), this.applying["@@bind"] = !0;
    const e = this.bindInfo, i = e.map((m) => this.value[m]), r = this.getTime(), { multiTiming: h, relation: l, easeTime: u } = this, c = h(1);
    if (c.length !== i.length)
      throw new TypeError(
        `The number of binded animate attributes and timing function returns's length does not match. binded: ${e.length}, timing: ${c.length}`
      );
    if (this.hook("start"), u <= 0)
      return this.end(!1, "@@bind"), this;
    const a = () => {
      const d = this.getTime() - r;
      if (d > u) {
        this.end(!1, "@@bind");
        return;
      }
      const f = d / u, v = h(f);
      e.forEach((g, w) => {
        l === "absolute" ? this.value[g] = v[w] : this.value[g] = i[w] + v[w];
      });
    };
    return this.ticker.add(a, t), this.animateFn.custom["@@bind"] = a, this.targetValue.system["@@bind"] = c, this;
  }
  applySys(t, e, i) {
    i !== "move" && this.applying[i] === !0 && this.end(!0, i), this.applying[i] = !0;
    const r = this[t], h = this.getTime(), l = this.timing, u = this.relation, c = this.easeTime, a = u === "absolute" ? e - r : e;
    if (this.hook("start", `${i}start`), c <= 0)
      return this.end(!1, i);
    const m = () => {
      const f = this.getTime() - h;
      if (f > c) {
        this.end(!0, i);
        return;
      }
      const v = f / c, g = l(v);
      this[t] = r + a * g, t !== "oy" && this.hook(i);
    };
    this.ticker.add(m, !0), t === "ox" ? this.animateFn.system.move[0] = m : t === "oy" ? this.animateFn.system.move[1] = m : this.animateFn.system[i] = m, i === "move" ? (t === "ox" && (this.targetValue.system.move[0] = a + r), t === "oy" && (this.targetValue.system.move[1] = a + r)) : i !== "shake" && (this.targetValue.system[i] = a + r);
  }
  error(t, e) {
    throw e === "repeat" ? new Error(
      `Cannot execute the same animation twice. Info: ${t}`
    ) : e === "reregister" ? new Error(
      `Cannot register an animated property twice. Info: ${t}`
    ) : new Error(t);
  }
  end(t, e) {
    if (t === !0)
      if (this.applying[e] = !1, e === "move" ? (this.ticker.remove(this.animateFn.system.move[0]), this.ticker.remove(this.animateFn.system.move[1])) : e === "moveAs" ? this.ticker.remove(this.animateFn.system.moveAs) : e === "@@bind" ? this.ticker.remove(this.animateFn.system["@@bind"]) : this.ticker.remove(
        this.animateFn.system[e]
      ), e === "move") {
        const [i, r] = this.targetValue.system.move;
        this.ox = i, this.oy = r, this.hook("moveend", "end");
      } else if (e === "moveAs") {
        const [i, r] = this.targetValue.system.moveAs;
        this.ox = i, this.oy = r, this.hook("moveend", "end");
      } else
        e === "rotate" ? (this.angle = this.targetValue.system.rotate, this.hook("rotateend", "end")) : e === "resize" ? (this.size = this.targetValue.system.resize, this.hook("resizeend", "end")) : e === "@@bind" ? this.bindInfo.forEach((r, h) => {
          this.value[r] = this.targetValue.system["@@bind"][h];
        }) : (this.sx = 0, this.sy = 0, this.hook("shakeend", "end"));
    else
      this.applying[e] = !1, this.ticker.remove(this.animateFn.custom[e]), this.value[e] = this.targetValue.custom[e], this.hook("end");
  }
}
class j extends F {
  constructor() {
    super();
    o(this, "now", {});
    o(this, "target", {});
    o(this, "transitionFn", {});
    o(this, "value");
    o(this, "handleSet", (t, e, i) => (this.transition(e, i), !0));
    o(this, "handleGet", (t, e) => this.now[e]);
    this.timing = (t) => t, this.value = new Proxy(this.target, {
      set: this.handleSet,
      get: this.handleGet
    });
  }
  mode(t) {
    return this.timing = t, this;
  }
  time(t) {
    return this.easeTime = t, this;
  }
  relative() {
    return this.relation = "relative", this;
  }
  absolute() {
    return this.relation = "absolute", this;
  }
  transition(t, e) {
    if (e === this.target[t])
      return this;
    if (!T(this.now[t]))
      return this.now[t] = e, this;
    this.applying[t] && this.end(t, !0), this.applying[t] = !0, this.hook("start");
    const i = this.getTime(), r = this.easeTime, h = this.timing, l = this.now[t], u = e + (this.relation === "absolute" ? 0 : l), c = u - l;
    this.target[t] = u;
    const a = () => {
      const d = this.getTime() - i;
      if (d >= r) {
        this.end(t);
        return;
      }
      const f = d / r;
      this.now[t] = h(f) * c + l, this.hook("running");
    };
    return this.transitionFn[t] = a, r <= 0 ? (this.end(t), this) : (this.ticker.add(a), this);
  }
  end(t, e = !1) {
    const i = this.transitionFn[t];
    if (!T(i))
      throw new ReferenceError(
        `You are trying to end an ended transition: ${t}`
      );
    this.ticker.remove(this.transitionFn[t]), delete this.transitionFn[t], this.applying[t] = !1, this.hook("end"), e || (this.now[t] = this.target[t]);
  }
}
const x = (...n) => n.reduce((s, t) => s + t, 0), y = (n) => {
  if (n === 0)
    return 1;
  let s = n;
  for (; n > 1; )
    n--, s *= n;
  return s;
}, A = (n, s) => Math.round(y(s) / (y(n) * y(s - n))), p = (n, s, t = (e) => 1 - s(1 - e)) => n === "in" ? s : n === "out" ? t : n === "in-out" ? (e) => e < 0.5 ? s(e * 2) / 2 : 0.5 + t((e - 0.5) * 2) / 2 : (e) => e < 0.5 ? t(e * 2) / 2 : 0.5 + s((e - 0.5) * 2) / 2, $ = Math.cosh(2), z = Math.acosh(2), V = Math.tanh(3), P = Math.atan(5);
function O() {
  return (n) => n;
}
function q(...n) {
  const s = [0].concat(n);
  s.push(1);
  const t = s.length, e = Array(t).fill(0).map((i, r) => A(r, t - 1));
  return (i) => {
    const r = e.map((h, l) => h * s[l] * (1 - i) ** (t - l - 1) * i ** l);
    return x(...r);
  };
}
function U(n, s) {
  if (n === "sin") {
    const t = (i) => Math.sin(i * Math.PI / 2);
    return p(s, (i) => 1 - t(1 - i), t);
  }
  if (n === "sec") {
    const t = (i) => 1 / Math.cos(i);
    return p(s, (i) => t(i * Math.PI / 3) - 1);
  }
  throw new TypeError(
    "Unexpected parameters are delivered in trigo timing function."
  );
}
function C(n, s) {
  if (!Number.isInteger(n))
    throw new TypeError(
      "The first parameter of power timing function only allow integer."
    );
  return p(s, (e) => e ** n);
}
function G(n, s) {
  if (n === "sin")
    return p(s, (e) => (Math.cosh(e * 2) - 1) / ($ - 1));
  if (n === "tan") {
    const t = (i) => Math.tanh(i * 3) * 1 / V;
    return p(s, (i) => 1 - t(1 - i), t);
  }
  if (n === "sec") {
    const t = (i) => 1 / Math.cosh(i);
    return p(s, (i) => 1 - (t(i * z) - 0.5) * 2);
  }
  throw new TypeError(
    "Unexpected parameters are delivered in hyper timing function."
  );
}
function N(n, s) {
  if (n === "sin") {
    const t = (i) => Math.asin(i) / Math.PI * 2;
    return p(s, (i) => 1 - t(1 - i), t);
  }
  if (n === "tan") {
    const t = (i) => Math.atan(i * 5) / P;
    return p(s, (i) => 1 - t(1 - i), t);
  }
  throw new TypeError(
    "Unexpected parameters are delivered in inverse trigo timing function."
  );
}
function B(n, s = () => 1) {
  let t = -1;
  return (e) => (t *= -1, e < 0.5 ? n * s(e * 2) * t : n * s((1 - e) * 2) * t);
}
function D(n, s = 1, t = [0, 0], e = 0, i = (h) => 1, r = !1) {
  return (h) => {
    const l = s * h * Math.PI * 2 + e * Math.PI / 180, u = Math.cos(l), c = Math.sin(l), a = n * i(i(r ? 1 - h : h));
    return [a * u + t[0], a * c + t[1]];
  };
}
function H(n, s, ...t) {
  const e = [n].concat(t);
  e.push(s);
  const i = e.length, r = Array(i).fill(0).map((h, l) => A(l, i - 1));
  return (h) => {
    const l = r.map((c, a) => c * e[a][0] * (1 - h) ** (i - a - 1) * h ** a), u = r.map((c, a) => c * e[a][1] * (1 - h) ** (i - a - 1) * h ** a);
    return [x(...l), x(...u)];
  };
}

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

core.plugin.animate = {
    Animation: Y,
    AnimationBase: F,
    Ticker: I,
    Transition: j,
    sleep: R,
    circle: D,
    bezierPath: H,
    linear: O,
    bezier: q,
    trigo: U,
    power: C,
    hyper: G,
    inverseTrigo: N,
    shake: B
}

},
    "auto": function () {
	// 在此增加新插件
	/**
 * --------------- 安装说明 ---------------
 *
 * 首先安装高级动画插件
 * 然后将该插件复制到插件编写里面即可
 * 注意高级动画插件要在本插件之前
 *
 * --------------- 使用说明 ---------------
 *
 * 变量__auto__控制功能，为一个数字。它需要由一下几个变量构成：
 * 1. core.plugin.AUTO_BATTLE
 * 2. core.plugin.AUTO_ITEM
 * 具体使用方法如下：
 * flags.__auto__ = core.plugin.AUTO_BATTLE; // 开启自动清怪，关闭自动拾取
 * flags.__auto__ = core.plugin.AUTO_BATTLE | core.plugin.AUTO_ITEM; // 二者都开启
 * flags.__auto__ = core.plugin.AUTO_ITEM; // 开启自动拾取，关闭自动清怪
 * flags.__auto__ = 0; 关闭所有功能
 * 更多内容可以在插件注释中查看
 */

const { Transition, hyper, Ticker } = core.plugin.animate ?? {};

// 磁吸特效的时长，单位毫秒
const transitionTime = 600;

this.AUTO_BATTLE = 1;
this.AUTO_ITEM = 2;

const transitionList = [];

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

control.prototype.moveOneStep = function (callback) {
    const res = this.controldata.moveOneStep(callback);
    update();
    return res;
};

control.prototype.moveDirectly = function (destX, destY, ignoreSteps) {
    const res = this.controldata.moveDirectly(
        destX,
        destY,
        ignoreSteps
    );
    update();
    return res;
};

function update() {
    core.auto();
    if (main.replayChecking) return;
    for (let i = 0; i < transitionList.length; i++) {
        const t = transitionList[i];
        let { x, y } = core.status.hero.loc;
        t.value.x = x * 32 - core.bigmap.offsetX;
        t.value.y = y * 32 - core.bigmap.offsetY;
    }
}

/**
 * 是否清这个怪，可以修改这里来实现对不同怪的不同操作
 * @param {string} enemy
 * @param {number} x
 * @param {number} y
 */
function canBattle(enemy, x, y) {
    const loc = `${x},${y}`;
    const floor = core.floors[core.status.floorId];
    const e = core.material.enemys[enemy];
    const hasEvent =
        has(floor.afterBattle[loc]) ||
        has(floor.beforeBattle[loc]) ||
        has(e.beforeBattle) ||
        has(e.afterBattle) ||
        has(floor.events[loc]);
    // 有事件，不清
    if (hasEvent) return false;
    const damage = core.getDamageInfo(enemy, void 0, x, y)?.damage;
    // 0伤或负伤，清
    if (has(damage) && damage <= 0) return true;
    return false;
}

/**
 * 判断一个点是否能遍历
 */
function judge(block, nx, ny, tx, ty, dir, floorId) {
    if (!has(block)) return {};
    const cls = block.event.cls;
    const loc = `${tx},${ty}`;
    const floor = core.floors[floorId];
    const changeFloor = floor.changeFloor[loc];
    const isEnemy =
        flags.__auto__ & core.plugin.AUTO_BATTLE &&
        cls.startsWith('enemy');
    const isItem =
        flags.__auto__ & core.plugin.AUTO_ITEM && cls === 'items';

    if (has(changeFloor)) {
        if (!core.noPass(tx, ty, floorId) && !core.canMoveHero(nx, ny, dir)) {
            return false;
        }
        if (changeFloor.ignoreChangeFloor ?? core.flags.ignoreChangeFloor) {
            return true;
        }
        return false
    }

    if (has(core.floors[floorId].events[loc])) return false;

    if (isEnemy || isItem)
        return {
            isEnemy,
            isItem
        };

    return false;
}

/**
 * 是否捡拾这个物品
 */
function canGetItem(item, loc, floorId) {
    // 可以用于检测道具是否应该被捡起，例如如果捡起后血量超过80%则不捡起可以这么写：
    // if (item.cls === 'items') {
    //     let diff = {};
    //     const before = core.status.hero;
    //     const hero = core.clone(core.status.hero);
    //     const handler = {
    //         set(target, key, v) {
    //             diff[key] = v - (target[key] || 0);
    //             if (!diff[key]) diff[key] = void 0;
    //             return true;
    //         }
    //     };
    //     core.status.hero = new Proxy(hero, handler);

    //     eval(item.itemEffect);

    //     core.status.hero = before;
    //     window.hero = before;
    //     window.flags = before.flags;
    //     if (
    //         diff.hp &&
    //         diff.hp + core.status.hero.hp > core.status.hero.hpmax * 0.8
    //     )
    //         return false;
    // }
    return true;
}

/**
 * @template T
 * @param {T} v
 * @returns {v is NonNullable<T>}
 */
function has(v) {
    return v !== null && v !== undefined;
}

/**
 * 广搜，搜索可以到达的需要清的怪
 * @param {string} floorId
 */
function bfs(floorId, deep = Infinity) {
    core.extractBlocks(floorId);
    const objs = core.getMapBlocksObj(floorId);
    const { x, y } = core.status.hero.loc;
    /** @type {[direction, number, number][]} */
    const dir = Object.entries(core.utils.scan).map(v => [
        v[0],
        v[1].x,
        v[1].y
    ]);
    const floor = core.status.maps[floorId];

    /** @type {[number, number][]} */
    const queue = [[x, y]];
    const mapped = {
        [`${x},${y}`]: true
    };
    while (queue.length > 0 && deep > 0) {
        const [nx, ny] = queue.shift();
        dir.forEach(v => {
            const [tx, ty] = [nx + v[1], ny + v[2]];
            if (
                tx < 0 ||
                ty < 0 ||
                tx >= floor.width ||
                ty >= floor.height
            ) {
                return;
            }
            const loc = `${tx},${ty}`;
            if (mapped[loc]) return;
            const block = objs[loc];
            mapped[loc] = true;
            const type = judge(block, nx, ny, tx, ty, v[0], floorId);
            if (type === false) return;
            const { isEnemy, isItem } = type;

            if (isEnemy) {
                if (
                    canBattle(block.event.id, tx, ty) &&
                    !block.disable
                ) {
                    core.battle(block.event.id, tx, ty);
                    core.updateCheckBlock();
                } else {
                    return;
                }
            } else if (isItem) {
                const item = core.material.items[block.event.id];
                if (canGetItem(item, loc, floorId)) {
                    core.getItem(item.id, 1, tx, ty);
                    if (!main.replayChecking) {
                        let px = tx * 32 - core.bigmap.offsetX;
                        let py = ty * 32 - core.bigmap.offsetY;
                        const t = new Transition();
                        t.mode(hyper('sin', 'out'))
                            .time(transitionTime)
                            .absolute()
                            .transition('x', px)
                            .transition('y', py);
                        let { x, y } = core.status.hero.loc;
                        t.value.x = x * 32 - core.bigmap.offsetX;
                        t.value.y = y * 32 - core.bigmap.offsetY;
                        transitionList.push(t);
                        t.ticker.add(() => {
                            core.drawIcon(
                                '_autoItem_',
                                item.id,
                                t.value.x,
                                t.value.y,
                                32,
                                32
                            );
                            let { x, y } = core.status.hero.loc;
                            if (
                                Math.abs(
                                    t.value.x -
                                    x * 32 +
                                    core.bigmap.offsetX
                                ) < 0.05 &&
                                Math.abs(
                                    t.value.y -
                                    y * 32 +
                                    core.bigmap.offsetY
                                ) < 0.05
                            ) {
                                t.ticker.destroy();
                                const index = transitionList.findIndex(
                                    v => v === t
                                );
                                transitionList.splice(index, 1);
                            }
                        });
                    }
                } else {
                    return;
                }
            }
            // 然后判断目标点是否有地图伤害等，没有就直接添加到队列
            const damage = core.status.checkBlock.damage[loc];
            const ambush = core.status.checkBlock.ambush[loc];
            const repulse = core.status.checkBlock.repulse[loc];

            if (
                (has(damage) && damage > 0) ||
                has(ambush) ||
                has(repulse)
            ) {
                return;
            }
            queue.push([tx, ty]);
        });
        deep--;
    }
}

this.auto = function () {
    const before = flags.__forbidSave__;
    // 如果勇士当前点有地图伤害，只清周围，如果有时间，直接不清了
    const { x, y } = core.status.hero.loc;
    const floor = core.floors[core.status.floorId];
    const loc = `${x},${y}`;
    const hasEvent = has(floor.events[loc]);
    if (hasEvent) return;
    const damage = core.status.checkBlock.damage[loc];
    const ambush = core.status.checkBlock.ambush[loc];
    const repulse = core.status.checkBlock.repulse[loc];
    let deep = Infinity;
    if ((has(damage) && damage > 0) || has(ambush) || has(repulse)) {
        deep = 1;
    }
    flags.__forbidSave__ = true;
    flags.__statistics__ = true;
    const ctx = core.getContextByName('_autoItem_');
    if (!ctx)
        core.createCanvas(
            '_autoItem_',
            0,
            0,
            core._PX_ ?? core.__PIXELS__,
            core._PY_ ?? core.__PIXELS__,
            75
        );
    bfs(core.status.floorId, deep);
    flags.__statistics__ = false;
    flags.__forbidSave__ = before;
    core.updateStatusBar();
};

control.prototype._replayAction_moveDirectly = function (action) {
    if (action.indexOf("move:")!=0) return false;
    // 忽略连续的瞬移事件；如果大地图某一边超过计算范围则不合并
    // if (!core.hasFlag('poison') && core.status.thisMap.width < 2 * core.bigmap.extend + core.__SIZE__
    //     && core.status.thisMap.height < 2 * core.bigmap.extend + core.__SIZE__) {
    //     while (core.status.replay.toReplay.length>0 &&
    //         core.status.replay.toReplay[0].indexOf('move:')==0) {
    //             core.status.route.push(action);
    //             action = core.status.replay.toReplay.shift();
    //     }
    // }

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

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