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

	// 可以写一些直接执行的代码
	// 在这里写的代码将会在【资源加载前】被执行，此时图片等资源尚未被加载。
	// 请勿在这里对包括bgm，图片等资源进行操作。


	this._afterLoadResources = function () {
		// 本函数将在所有资源加载完毕后，游戏开启前被执行
		// 可以在这个函数里面对资源进行一些操作。
		// 若需要进行切分图片，可以使用 core.splitImage() 函数，或直接在全塔属性-图片切分中操作
		core.box.cmii();
	}
	
	
	
	// 可以在任何地方（如afterXXX或自定义脚本事件）调用函数，方法为 core.plugin.xxx();
	// 从V2.6开始，插件中用this.XXX方式定义的函数也会被转发到core中，详见文档-脚本-函数的转发。
},
    "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);
},
    "animate": function () {
	// -------------------- 插件说明 -------------------- //

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

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

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

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

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

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

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

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

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

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

// 5. 执行动画

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// 6. 自定义动画属性

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

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

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

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

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

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

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

// 7. 运行动画

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

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

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

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

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

// 9. 多个属性绑定

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

core.plugin.animate = {
    Animation: Y,
    AnimationBase: F,
    Ticker: I,
    Transition: j,
    sleep: R,
    circle: D,
    bezierPath: H,
    linear: O,
    bezier: q,
    trigo: U,
    power: C,
    hyper: G,
    inverseTrigo: N,
    shake: B
}
	
},
    "parbox": function () {
	core.saves.autosave.max=1000;
	{
		const tt=(text,color)=>{
			return `<span style="color:${color};">${text}</span>`;
		}
		document.getElementById("startLogo").innerHTML = `帕特里克的箱子${tt("无","#cccccc")}${tt("穷","#3385ff")}${tt("奇","#33ff85")}${tt("遇","#ffad33")}`;
	}
	
	const bback=(dire)=>core.turnDirection(":back",dire);
	//颠倒方向，如"up"=>"down"
	let boxcreateCanvas = function (width, height,RF) {//创建一个指定大小的canvas
		var newCanvas = document.createElement("canvas");
		newCanvas.width = width;
		newCanvas.height = height;
		if(RF)return newCanvas.getContext('2d',{willReadFrequently:true});
		return newCanvas.getContext('2d');
	}
	let nulan={"type": "comment", "text": " "};//在insertAction里啥也不做，占位符
	let cmii=null;//等待初始化

	/*定义主要对象box*/
	core.box={
		can:{
			ctx:boxcreateCanvas(128,32),/*替换箱子贴图所用画布*/
			sol:boxcreateCanvas(128,32),/*替换实心箱子贴图所用画布*/
			play:boxcreateCanvas(128,128),/*替换玩家所用画布*/
			bca:boxcreateCanvas(416,416),bcb:boxcreateCanvas(352,352),/*替换游戏边框所用画布*/
			cra:boxcreateCanvas(416,416,true),crb:boxcreateCanvas(96,128,true)
		},
		list:{
			pbutton:167,//玩家位置
			bbutton:168,//箱子位置
			kuang:331,//白边框
			blocks:[169,...Array.from({length:100}, (_,i)=>(i+500))],//箱子
			hollowblocks:Array.from({length:100}, (_,i)=>(i+500)),//空心箱子
			num1:332,box1:500,//第一个数字/箱子的位置
			color:[ ['#cccccc', '#3385ff', '#33ff85', '#ffad33', '#b3006b', '#33c2ff'],
					['#ffa370', '#6688ff', '#7eff73', '#ffd466', '#cc60c1', '#33c2ff'],
					['#8fa7cc', '#e68845', '#2bd986', '#2ba5d9', '#bf3972', '#33c2ff'],
					['#7d7a99', '#63992e', '#ccad3d', '#cc573d', '#763fd1', '#33c2ff'],
					['#b3a4a4', '#5c89cc', '#f2943d', '#a7cc3d', '#d9552b', '#33c2ff'],
					['#5283cc', '#2d8ab3', '#30bf94', '#d9b536', '#b33636', '#33c2ff'],
					['#869dbf', '#7ad124', '#bfb800', '#4585e6', '#b32446', '#3dccaa'],
					['#576cd9', '#7167e6', '#b34768', '#5cccaa', '#bf4cb4', '#3d87cc'],
					['#b3ad98', '#b30056', '#91cc14', '#3dcccc', '#c36cd9', '#e68617'],
				["#5cff33","#d6ff33","#8566ff","#2996cc","#ffc633","#14cc69","#ff3364",
					"#8b7acc","#97bf26","#1f8399","#50e6b0"]
			],
			setting:{
				pl:{A:"a1",B:"a2",C:"a3",D:"a4",E:"a5",F:"a6",G:"a7",/*a8帮助房间*/H:"a9"},//字母对应root中哪个箱子
				style:{
					A:0,B:2,C:4,D:1,E:5,F:0,G:3
				}
			},
			templevel:{heroloc:""}
		},gbls:function(boxlevel){//根据boxlevel获取楼层
			return core.floorIds.filter((id)=>{if(!core.floors[id].boxsetting)return false;return core.floors[id].boxsetting.boxlevel===boxlevel});
		},gabl:function(boxlevel,codename){//根据boxlevel和代号获取唯一的楼层
			const levels=box.gbls(boxlevel);
			let ls=levels.find((id)=>(core.floors[id].boxsetting.codename===codename));
			if(!ls)ls=levels.find((id)=>(core.floors[id].boxsetting.codename==="a1"));//若无，找a1
			if(!ls)ls=levels[0];//若还无，找第一个，如果一个都没有，返回undefined
			return ls;
		},showlevel:function(floorId=core.status.floorId,isforce){//将楼层渲染到箱子上
			if(main.mode === 'editor'||main.replayChecking)return;//屏蔽录像
			const bl=core.floors[floorId].boxsetting.boxlevel;
			let oneshow=(cn)=>{
				if(cn!=1&&(box.gabl(bl,"a1")===box.gabl(bl,"a"+cn)))return;//后面和a1相同，说明已经没了，不执行
				if(!(isforce||ce[cn]))return;
				let heroin=(core.status.floorId===box.gabl(bl,"a"+cn));//检测玩家是否在该地图内
				box.drawt(box.gabl(bl,"a"+cn),heroin);//实际将图画到can.ctx上
				cmii["box"+cn+".png"].src=box.can.ctx.canvas.toDataURL();//替换资料库中boxN.png为图的base64编码
			}
			if(isforce)core.setFlag('__show',-1);
			let showt=core.getFlag("__show",0);
			let depth=1;
			if(showt<=0)depth=6;
			else if(showt%4===0)depth=3;
			else if(showt%2===0)depth=2;
			let dparray=box.dpsearch(floorId,depth);
			let ce=box.llchange(dparray,bl);
			const cns=dparray.flat().reverse();
			for(let cn of cns){setTimeout(()=>{oneshow(cn)},0);}
			core.setFlag("__show",((showt>=7)?0:(showt+1)));
			box.outdraw();//画边框
		},drawt:function(floorId=core.status.floorId,heroin){//实际将图画到can.ctx上
			let fs=box.floorsize(floorId);
			let sca=((fs<13)?(416/fs):32);//小于13，大一点（会被截掉）；大于等于13，全都要
			core.drawThumbnail(floorId,null,{ctx:box.can.ctx,
				x:0,y:0,centerX:5,centerY:5/*实际测试，centerXY这样设可以好好显示*/,
				size:sca,all:true,/*大于13时需要全部显示*/
				heroLoc:((heroin)?(core.status.hero.loc):null)});
			for(let i=1;i<=3;i++)core.drawImage(box.can.ctx,box.can.ctx.canvas,0,0,32,32,32*i,0,32,32);//复制3次
		},deed:{//行动库（本来全都是基于insertAction，后来被迫放弃）
			move:(x,y,dire,floorId=core.status.floorId)=>{//移动
				let df=box.dxyf(x,y,dire,floorId);
				if(box.ishero(x,y,floorId)){//是玩家就移动玩家，不是就移动图块
					if(box.contin(x,y,df[0],df[1],floorId,df[2],dire)){//是否连续（无需判断是否在当前地图，因为ishero判断过了）
						events.prototype.eventMoveHero([dire],null);//在当前地图丝滑移动一格
					}else {
						//设定传送
						core.setFlag("__tofloor",[df[2],df[0],df[1]]);
					}
				}else{
					if(floorId===core.status.floorId&&box.contin(x,y,df[0],df[1],floorId,df[2],dire)&&core.status.replay.speed!==24){//是否在当前地图且连续
						core.moveBlock(x, y, [dire],core.values.moveSpeed/core.status.replay.speed, true);//丝滑移动方块
					}else {
						var block=core.getBlockNumber(x, y,floorId);
						core.removeBlock(x, y, floorId);
						core.setBlock(block,df[0],df[1],df[2]);//先删除，后放置
					}
				}
			},enter:(x,y,dire,floorId=core.status.floorId,toFloorId)=>{//进入
				var block=core.getBlockNumber(x, y,floorId);
				let bep=box.enterpos(bback(dire),toFloorId);
				core.setFlag("__fsl",true);
				if(box.ishero(x,y,floorId)){//是玩家就传送，不是就先删除后放置
					core.setFlag("__tofloor",[toFloorId,bep[0],bep[1]]);
				}else{
					core.removeBlock(x, y, floorId);
					core.setBlock(block,bep[0],bep[1],toFloorId);
				}
			},end:()=>{
				//最后进行传送，回调进行走一步的操作或删除"__tofloor"
				let tof=core.getFlag("__tofloor",null);
				if(tof)core.changeFloor(tof[0],null,{x:tof[1],y:tof[2]},0,()=>{core.removeFlag("__tofloor");});
				else box.onestep();
			}
		},enterpos:function(dire,floorId=core.status.floorId){//计算对应尺寸应进入的方位
			let fs=box.floorsize(floorId);
			let fsm=fs-1,hfsm=fsm/2;
			switch(dire){
				case "up"   :return [Math.ceil(hfsm),0               ];
				case "down" :return [Math.ceil(hfsm),fsm             ];
				case "left" :return [0              ,Math.floor(hfsm)];
				case "right":return [fsm            ,Math.floor(hfsm)];
			}
		},floorsize:function(floorId=core.status.floorId){//计算楼层大小
			if((!core.floors[floorId])||(!("width" in core.floors[floorId])))return 11;//默认11
			if(core.floors[floorId].width===13){//是否为大地图
				let nb=core.getFgNumber(12,12,floorId);//根据13*13的右下角确定尺寸
				if((nb>=bt.n)&&(nb<=bt.n+11))return nb-bt.n+1;else return 13;//没标记说明就是13
			}else return core.floors[floorId].width;//大地图直接输出宽
		},boxpos:function(floorId=core.status.floorId){//输出当前楼层所在箱子的位置
			if(floorId==="debug")return [10,10,"space"];
			if(floorId==="space"){return [10,10,"space"];}//宇宙的方位是一个被围死的地方
			let bl=core.floors[floorId].boxsetting.boxlevel,cn=Number(core.floors[floorId].boxsetting.codename.slice(1));
			let boxnum=cn+bt.bx-1;//当前箱对应方块编号
			for(let icn=1;icn<=100;icn++){//挨个找
				let ilevel=box.gabl(bl,"a"+icn);
				if(icn!==1&&(box.gabl(bl,"a1")===ilevel))continue;
				let lm=core.getMapArray(ilevel);
				let fsize=box.floorsize(ilevel);
				for(let y=0;y<fsize;y++){
					for(let x=0;x<fsize;x++){
						if(lm[y][x]===boxnum){
							return [x,y,ilevel];//找到输出
						}
					}
				}
			}
			return [3,3,"space"];//没找到输出宇宙
			
		},boxfloor:function(num){//输出方块对应楼层
			let cn=num-bt.bx+1,bl=core.floors[core.status.floorId].boxsetting.boxlevel;
			return box.gabl(bl,"a"+cn);
		},overstep:function(x,y,floorId=core.status.floorId,dire){//是否在箱子边上
			//if(floorId==="space")return false;
			switch(dire){
				case "up"   :return y===0;
				case "down" :return y===(box.floorsize(floorId)-1);
				case "left" :return x===0;
				case "right":return x===(box.floorsize(floorId)-1);
				case "all":return x===0||y===0||x===(box.floorsize(floorId)-1)||y===(box.floorsize(floorId)-1)
			}
		},pushbox:function(){//推箱子事件
			let hl=core.getHeroLoc(),dire=hl.direction;

			if(box.ping(hl.x,hl.y,dire)){//如果ping成功则进行行动
				
				core.autosave(true);
				let floorId=core.status.floorId;
				box.doing(hl.x,hl.y,dire,floorId);//行动
				box.wc(()=>bd.end());//在wc后进行结束
			}
		},ping:function(x,y,dire,floorId=core.status.floorId){//检测是否存在可行操作
			return box.moveping(x,y,dire,floorId)||box.enterping(x,y,dire,floorId);//行动ping和进入ping
		},doing:function(x,y,dire,floorId=core.status.floorId){//真正行动
			
			if(box.moveping(x,y,dire,floorId)){
				box.moving(x,y,dire,floorId);
			}else if(box.enterping(x,y,dire,floorId)){
				box.ego(x,y,dire,floorId,box.ecoming);
				setTimeout(()=>{box.doing(x,y,dire,floorId)},1);
				// 需要 setTimeout 执行，不然会出错，+1是为了录像检测系统
			}
			
		},enterping:function(x,y,dire,floorId=core.status.floorId){//"进入"检测
			if(floorId==="space")return false;//宇宙无法进入任何箱子
			return box.ego(x,y,dire,floorId,box.epcome);//进行ego，回调为ping性质的epcome
		},ego:function(x,y,dire,floorId=core.status.floorId,callback=()=>{},shed=[]){//为进入的ping和真正行动的共用前半部分
			var df=box.dxyf(x,y,dire,floorId),block=core.getBlockNumber(x,y,floorId);
			shed.unshift([x,y,floorId]);
			if(bt.bl.includes(block)||box.ishero(x,y,floorId)){
				return box.ego(df[0],df[1],dire,df[2],callback,shed);//进行递归
			}else{
				shed[0]=[null];
				return callback(dire,shed);//到地方后执行回调（epcome/ecoming）
			}
		},epcome:function(dire,shed){//回头，检测是否有进入
			//不好注释
			shed.last=shed.shift();//锯下一个，放到last
			if(shed.length===0){
				let lb=core.getBlockNumber(...shed.last);
				if(bt.hb.includes(lb)){
					let bf=box.boxfloor(lb),bep=box.enterpos(bback(dire),bf);
					return box.ping(bep[0],bep[1],dire,bf);
				}
				return false;
			}
			var df=shed.last,block=core.getBlockNumber(...shed[0]),bdf=shed[1];
			
			if(bt.hb.includes(block)){
				let bf=box.boxfloor(block),ep=box.enterpos(dire,bf),bep=box.enterpos(bback(dire),bf);
				if(df[0]&&box.ping(ep[0],ep[1],bback(dire),bf)){
					return true;
				}else if(bdf&&box.ping(bep[0],bep[1],dire,bf)){
					return true;
				}else return box.epcome(dire,shed);
			}else{
				return box.epcome(dire,shed);
			}
			
		},ecoming:function(dire,shed){//回头，执行“进入”
			//不好注释
			shed.last=shed.shift();
			if(shed.length===0){return;}
			var df=shed.last,block=core.getBlockNumber(...shed[0]),bdf=shed[1];
			
			if(bt.hb.includes(block)){
				let bf=box.boxfloor(block),ep=box.enterpos(dire,bf),bep=box.enterpos(bback(dire),bf);
				if(df[0]&&box.ping(ep[0],ep[1],bback(dire),bf)){//吞食
					box.doing(ep[0],ep[1],bback(dire),bf);bd.enter(df[0],df[1],bback(dire),df[2],box.ebf(bf,dire));
				}else if(bdf&&box.ping(bep[0],bep[1],dire,bf)){//进入
					box.doing(bep[0],bep[1],dire,bf);bd.enter(bdf[0],bdf[1],dire,bdf[2],box.ebf(bf,bback(dire)));
				}else box.ecoming(dire,shed);
			}else{
				box.ecoming(dire,shed);
			}
			
		},ishero:function(x,y,floorId=core.status.floorId){//检测此位置是否是玩家
			let hl=core.getHeroLoc();
			return (x===hl.x)&&(y===hl.y)&&(floorId===core.status.floorId);
		},moveping:function(x,y,dire,floorId=core.status.floorId,shed=[]){//检测是否能移箱子
			var df=box.dxyf(x,y,dire,floorId),
				block=core.getBlockNumber(x, y,floorId);
			if(shed.some((a)=>(a[0]===x&&a[1]===y&&a[2]===floorId)))return true;
			shed.unshift([x,y,floorId]);
			if(block===null&&(!box.ishero(x,y,floorId)))return true;//若有空地，则能移
			if(bt.bl.includes(block)||box.ishero(x,y,floorId)){
				return box.moveping(df[0],df[1],dire,df[2],shed);
			}
			return false;
		},moving:function(x,y,dire,floorId=core.status.floorId,shed=[]){//进行移动
			let df=box.dxyf(x,y,dire,floorId);
			if(shed.some((a)=>(a[0]===x&&a[1]===y&&a[2]===floorId)))return;
			shed.unshift([x,y,floorId]);
			if(core.getBlockNumber(x, y,floorId)===null&&(!box.ishero(x,y,floorId)))return;//到了空位就停止
			box.moving(df[0],df[1],dire,df[2],shed);//递归让所有块移动
			bd.move(x,y,dire,floorId);//真正移动
		},resetMap:function (floorId) {//和正常重置区别是：没有字，且会清前景
			floorId = floorId || core.status.floorId;
			if (!floorId) return;
			if (typeof floorId === 'string') floorId = [floorId];
			var needRefresh = false;
			floorId.forEach(function (t) {
				core.status.maps[t] = core.maps.loadFloor(t);
				// 重置本层的全部独立事件
				Object.keys(core.status.hero.flags).forEach(function (one) {
					if (one.startsWith(floorId + '@')) delete core.status.hero.flags[one];
				})
				// 重置本层的图块删除信息
				delete (flags.__disabled__ || {})[t];
				delete (core.status.mapBlockObjs || {})[t];
				if (t === core.status.floorId) needRefresh = true;
				//******清前景
				box.extinguish(t);
				//******
			});
			
			if (needRefresh) core.redrawMap();
		},load:function(bid){//进入，加载关卡
			core.autosave(true);//存档
			let reload=(bid==="reload");
			if(reload)bid=core.floors[core.status.floorId].boxsetting.boxlevel;
			box.resetMap(box.gbls(bid));
			if(!reload){
				core.floors.space.boxsetting.boxlevel=bid;
				box.color(box.gabl(bid,"a1"));//重置，染色
			}
			const fir=box.gabl(bid,"a1");
			const beg=box.gabl(bid,box.getBLS(fir).beginbox)||fir;
			box.showlevel(beg,true);
			let action={"type":"changeFloor","floorId":beg,"stair":"flyPoint","direction":"down"};
			if(reload)action.time=0;core.insertAction(action,null,null,()=>{box.showlevel(beg,true);box.vmlimit();});
		},onestep:function(){//一步后发生的事件
			box.showlevel(void(0),core.getFlag("__fsl",false));
			core.removeFlag("__fsl");let hl=core.getHeroLoc();
			if(core.getBgNumber(hl.x, hl.y,core.status.floorId)===bt.pb)box.winlevel();
			box.illumine();
		},extinguish:function(floorId=core.status.floorId){//灭掉过期前景
			let fgmap=core.getFgMapArray(floorId);
			let fsize=box.floorsize(floorId);
			for(let y=0;y<fsize;y++){
				for(let x=0;x<fsize;x++){
					if(fgmap[y][x]===bt.kg){
						if(core.getBgNumber(x,y,floorId)===bt.bb&&(!bt.bl.includes(core.getBlockNumber(x,y,floorId))))core.setBgFgBlock("fg",0,x,y,floorId);
						if(core.getBgNumber(x,y,floorId)===bt.pb)core.setBgFgBlock("fg",0,x,y,floorId);
					}
					
				}
			}
		},illumine:function(floorId=core.status.floorId){//点亮应该亮的地方（前景）
			box.extinguish(floorId);
			let bgmap=core.getBgMapArray(floorId);
			let fsize=box.floorsize(floorId);
			for(let y=0;y<fsize;y++){
				for(let x=0;x<fsize;x++){
					if(bgmap[y][x]===bt.bb){
						if(bt.bl.includes(core.getBlockNumber(x,y,floorId)))core.setBgFgBlock("fg",bt.kg,x,y,floorId);
					}
				}
			}
			let hl=core.getHeroLoc();
			if(core.getBgNumber(hl.x,hl.y,floorId)===bt.pb)core.setBgFgBlock("fg",bt.kg,hl.x,hl.y,floorId);
		},bg:function(floorId,ctx){//画背景和大关路线
			if(floorId===null)floorId=core.firstData.floorId;
			let bs=core.floors[floorId].boxsetting;
			if(bs){
				let gi=bs.groundimage;
				if(!gi){
					if(main.mode==='editor'){
						gi=window.makecnFloors[bs.boxlevel].filter(b=>(b.floorId===floorId))[0].groundimage;
					}else gi="bg0";
				}
				core.drawImage(ctx,box.bgimg.get(gi.replace(/^bg/,"")),0,0,416,416, 0, 0, 32*box.floorsize(floorId), 32*box.floorsize(floorId))
			}
			
			let ri=cmii[floorId+".png"];//若有路线，画路线
			if(ri){
				core.drawImage(ctx,ri,0,0,416,416, 0, 0, 32*box.floorsize(floorId), 32*box.floorsize(floorId))
			}
		},dxyf:function(x,y,dire,floorId=core.status.floorId,shed=[]){//输出走一步后的位置
			if(shed.some((a)=>(a[0]===x&&a[1]===y&&a[2]===floorId))||core.getFlag("_dxyfpile",0)>=1000){//有一样的或超过1000次视作死循环
				return box.dxyf(3,3,dire,"space",shed);//宇宙
			}
			shed.unshift([x,y,floorId]);
			let ud=core.utils.scan[dire];//使用scan判断移动方向
			if(box.overstep(x,y,floorId,dire)){//若出界
				core.setFlag("_dxyfpile",core.getFlag("_dxyfpile",0)+1);
				let bp=box.boxpos(floorId);
				return box.dxyf(bp[0],bp[1],dire,bp[2],shed);//输出外面箱子的位置
			}else {//若未出界
				core.removeFlag("_dxyfpile");
				return [x+ud.x,y+ud.y,floorId,shed];//输出位置
			}
		},wc:function(callback=()=>{}){//在waitAsync后执行
			core.insertAction({"type": "waitAsync"},null,null,callback);
		},ebf:function(bf,dire,shed=[]){
			let ep=box.enterpos(dire,bf),block=core.getBlockNumber(ep[0],ep[1],bf);
			if(shed.includes(bf)||core.getFlag("_ebfpile",0)>=1000){
				return "space";
			}
			shed.unshift(bf);
			if(block!==null){
				core.setFlag("_ebfpile",core.getFlag("_ebfpile",0)+1);
				return box.ebf(box.boxfloor(block),dire,shed);
			}else {
				core.removeFlag("_ebfpile");
				return bf;
			}
		},contin:function(xa,ya,xb,yb,fa,fb,dire){//位置是否连续
			let ud=core.utils.scan[dire];
			return (fa===fb)&&(xa+ud.x===xb)&&(ya+ud.y===yb);
		},jump:function(floorId){//将大关回城点设在关卡点旁边
			let hl=core.getHeroLoc();
			core.insertAction(
				{"type": "setFloor", "name": "flyPoint", "floorId": floorId, "value": [hl.x,hl.y]}
			);
		},mouseloc:function(pxo,pyo){//鼠标位置计算
			let px=pxo,py=pyo;
			px=parseInt(px*box.floorsize(core.status.floorId)/11);
			py=parseInt(py*box.floorsize(core.status.floorId)/11);
			let x = parseInt(px/32),y = parseInt(py/32);
			return [x,y,px,py];

		},csize:function(cas,size){//调整尺寸（直接调会导致画面丢失）
			if(cas.width===size)return false;//如果相同说明不需要调整
			/*let ida=ctx.getImageData(0,0,size,size);//使用getImageData的旧版
			cas.width=cas.height=size;
			ctx.putImageData(ida,0,0);*/
			const tempCanvas=document.createElement('canvas');
            tempCanvas.width=tempCanvas.height=size;
			tempCanvas.getContext('2d').drawImage(cas,0,0);//将原画面画到临时canvas上
			cas.width=cas.height=size;//对尺寸进行调整
			cas.getContext("2d").drawImage(tempCanvas,0,0);//画回去
			return true;
		},outclick:function(e){//点击外框的事件
			e.stopPropagation();//停止默认事件
			//录像，设置，非played状态时不执行
			if (core.actions._checkReplaying()||(!core.status.played) || core.status.lockControl)return;
			let loc=actions.prototype._getClickLoc(e.clientX, e.clientY);
			let [px,py]=[loc.x/core.domStyle.scale,loc.y/core.domStyle.scale];
			//真正执行outgo事件
			if(box.regional(px,py,0,32,32,352  ))box.outgo("left");
			else if(box.regional(px,py,384,32,32,352))box.outgo("right");
			else if(box.regional(px,py,32,0,352,32  ))box.outgo("up");
			else if(box.regional(px,py,32,384,352,32))box.outgo("down");
		},regional:function(px,py,x,y,w,h){//检测点击在不在指定框里
			return (px>=x)&&(px<=x+w)&&(py>=y)&&(py<=y+h);
		},outgo:function(dire){//点击外框真正触发的事件
			let hl=core.getHeroLoc();
			if(!box.overstep(hl.x,hl.y,core.status.floorId,dire))return;//不在边上不执行
			core.moveHero(dire,()=>{});//走一步
		},outdraw:function(){//画外面的框
			if(!box.out)core.resize();//resize会创建out图层
			let ctx=box.out;
			
			/*ctx.fillRect(0,32,32,352);//左右上下
			ctx.fillRect(384,32,32,352);
			ctx.fillRect(32,0,352,32);
			ctx.fillRect(32,384,352,32);*/
			ctx.fillStyle="black";
			ctx.fillRect(0,0,416,416);//黑底覆盖
			core.clearMap(box.can.bca);
			box.bcdraw(core.status.floorId,true);//真正作画
			core.removeFlag("_bcpile");//结束循环栈
			core.drawImage(box.out,box.can.bca.canvas,0,0,416,416,0,0,416,416);//真正画到out图层上
			ctx.fillStyle="#cccccc";let bd=2;/*bd:进行细微调整*/
			ctx.fillRect(32-bd,32-bd,352+2*bd,352+2*bd);
		},bcdraw:function(floorId,border,ns,shed=[]){//真正画外框
			//bca画布416*416，bcb画布352*352
			let bp=box.boxpos(floorId);
			let fs=box.floorsize(floorId);
			let rep=shed.filter((a)=>(a[0]===bp[0]&&a[1]===bp[1]&&a[2]===bp[2])).length>=2;//一个东西出现2次即为死循环
			shed.unshift([bp[0],bp[1],bp[2]]);
			if(border){
				core.setFlag("_bcpile",core.getFlag("_bcpile",0)+1);
				let par=[bp[2],box.overstep(...bp,"all"),[bp[0],bp[1]],shed];//如果目标在边上，继续递归
				if(rep||core.getFlag("_bcpile",0)>=100){//重复100次及以上视作死循环
					par=["space",false,[3,3],shed]
				}
				box.bcdraw(...par);//再次递归画框
				let bfs=box.floorsize(par[0]);
				let pos=(bb)=>(32+(par[2][bb]-1/11)*(352/bfs));
				//以正确的位置放大画
				core.drawImage(box.can.bca,box.can.bca.canvas,Math.round(pos(0)),Math.round(pos(1)),Math.round(416/bfs),Math.round(416/bfs),0,0,416,416);
			}
			let sca=((fs<13)?(416*11/fs):352);
			let heroin=(floorId===core.status.floorId);
			let blocks=null;
			if(ns){//ns位置如果存在，试图得到一个去掉块本身的方块表
				core.extractBlocks(floorId);
				
				blocks=core.clone(core.status.maps[floorId].blocks);
				if(blocks){
					let index=blocks.findIndex(e=>(e.x===ns[0]&&e.y===ns[1]));
  					if(index!==-1)blocks.splice(index,1);
				}
			}
			//在bcb上作图
			core.drawThumbnail(floorId,blocks,{ctx:box.can.bcb,x:0,y:0,centerX:5,centerY:5,size:sca,heroLoc: ((heroin)?(core.status.hero.loc):null),all:true});
			//将图挪到bca中间
			core.drawImage(box.can.bca,box.can.bcb.canvas,0,0,352,352,32,32,352,352);
		},vmlimit:function(floorId=core.status.floorId){//进行浏览地图的限制
			let bl=core.floors[floorId].boxsetting.boxlevel;
			if(core.getFlag("vmlimit",[])[0]===bl)return;//如果关卡不变，就跳过
			let act=[],oldlimit=core.getFlag("vmlimit"),newlimit=[bl,{}];
			for(n of core.floorIds){
				newlimit[n]=(core.floors[n].boxsetting.boxlevel!=bl);//不是当前关卡的，都不显示
				if((!oldlimit)||oldlimit[n]!=newlimit[n])
					act.push({"type": "setFloor", "name": "cannotViewMap",
						"floorId": n, "value":newlimit[n]});//和原设置不一样的，进行调整
			}
			core.setFlag("vmlimit",newlimit);
			core.insertAction(act);
		},winlevel:function(){//检测过关
			let ls=box.gbls(core.floors[core.status.floorId].boxsetting.boxlevel);
			let ret=function(){//检测是否过关
					for(let fid of ls){//遍历是否有未过关的地方
						let bgmap=core.getBgMapArray(fid);
						let fsize=box.floorsize(fid);
						for(let y=0;y<fsize;y++){
							for(let x=0;x<fsize;x++){
								if(bgmap[y][x]===bt.bb){
									if(!bt.bl.includes(core.getBlockNumber(x,y,fid)))return false;
								}
							}
						}
							
					}
					return true;//没有则输出true
			}
			if(ret()){//若过关
				let pf=box.gabl("root",box.list.setting.pl[box.getBLS().parent]);
				
				let orl=core.status.route.length;
				//类似B9这种最后一出箱子就通关的关卡，有的人会按住。这样会使录像多一步坏掉。orl可防止这种现象
				core.insertAction([
					{"type": "function", "function": "function(){\nif(core.status.route.length!=="+orl+")core.status.route.pop();\n}"},
					{"type": "setCurtain", "color": [255,255,255,0.5], "time": 1000, "keep": true},
					{"type": "sleep", "time": 200},
					{"type": "function", "function": "function(){\ncore.box.winset();console.log(core.status.route);\n}"},
					{"type": "function", "function": "function(){\ncore.box.color('"+pf+"');core.box.showlevel('"+pf+"',true);\n}"},
					{"type": "changeFloor", "floorId": pf, "stair": "flyPoint", "direction": "down" , "time": 0 },
					{"type": "function", "function": "function(){\ncore.box.vmlimit();core.box.showlevel('"+pf+"',true);\n}"},
					{"type": "setCurtain", "time": 0},
				]);
			}
		},color:function(floorId=core.status.floorId,editor){//更改颜色风格
			if(main.replayChecking)return;
			if(main.mode==='editor'&&editor!=='editor')return;
			let style=0;
			const ds=(style)=>{//解析style
				if((typeof style)==="number"){return box.list.color[style];}//数字则使用预设
				else if((typeof style)==="string"){return ds(Number(style));}//字符串则转为数字使用预设
				else if(Array.isArray(style)){return ((style.length<6)?(style.concat(new Array(6-style.length).fill("#ff00ff"))):style);}//数组则试图用紫色补位到6
				else return ds(0);//都不是的话输出0号预设
			}
			const bf=core.floors[floorId];
			if(bf){
				const fcs=box.getBLS(floorId).colorstyle;//关卡颜色风格
				const lcs=box.list.setting.style[box.getBLS(floorId).parent];//大关颜色风格
				if(fcs||(fcs===0))style=fcs;
				else if(lcs||(lcs===0))style=lcs;
			}
			style=ds(style);//进行解析
			const wcs=window.ColorStyle;
			if(wcs&&wcs.length===style.length&&wcs.every((v,i)=>v===style[i]))return;
			else window.ColorStyle=style;//在全局设定一个当前风格，若和之前相同则不调整
			const atp=cmii["autotile.png"];
			const bgp=cmii["bg.png"];
			const brp=cmii["boxbor.png"];
			const prp=cmii["playerbor.png"];
			const lt=(num)=>{if(num>255)return 255;if(num<0)return 0;return Math.round(num);};//颜色归到0~255内并取整
			const isis=(a,b)=>(a[0]===b[0])&&(a[1]===b[1])&&(a[2]===b[2]);//检测两个rgb数组是否完全相等
			//htr:将#rrggbb转为rgb数组
			function htr(hex) {const bi=Number("0x"+((hex.length===7)?hex.replace(/^#/,''):"000000"));return [(bi>>16)&255,(bi>>8)&255,bi&255];}
			for(k=0;k<style.length;k++){
				const nc=htr(style[k]);
				core.drawImage(box.can.cra,bgp,0,0,416,416,0,0,416,416);//将白背景画到cra图层
				const iData_a = box.can.cra.getImageData(0,0,416,416);
    			const dataa = iData_a.data;
				for(let i=0;i<dataa.length; i+=4){
					const dc=dataa[i+1];//原图是纯灰白，读取一个即可
					let mul=dc/255;//再*覆盖颜色值，实现正片叠底（原色*覆盖色/255）
					dataa[i]=lt(nc[0]*mul),dataa[i+1]=lt(nc[1]*mul),dataa[i+2]=lt(nc[2]*mul);
				}
				box.can.cra.putImageData(iData_a, 0, 0);
				box.bgimg.set(k,box.can.cra.canvas);
				core.drawImage(box.can.crb,atp,0,0,96,128,0,0,96,128);//将红绿蓝autotile画到crb图层
				const iData_b = box.can.crb.getImageData(0,0,96,128);
    			const datab = iData_b.data;
				for(let i=0;i<datab.length; i+=4){
					const dc=[datab[i],datab[i+1],datab[i+2]];
					let mul=1;
					if(isis(dc,[0xff,0,0]/*红绿蓝*/))/*原*/mul=1.05;
						else if(isis(dc,[0,0xff,0]))/*浅*/mul=1.39;
						else if(isis(dc,[0,0,0xff]))/*深*/mul=0.765;
						else continue;
					datab[i]=lt(nc[0]*mul),datab[i+1]=lt(nc[1]*mul),datab[i+2]=lt(nc[2]*mul);
				}
				box.can.crb.putImageData(iData_b, 0, 0);
				core.material.images.autotile["autotile"+k].src=box.can.crb.canvas.toDataURL();//覆盖原图
			}
			let solco=box.getBLS(floorId).solboxcolor||style[3];
			box.can.sol.fillStyle=`rgb(${Array.from(htr(solco),x=>lt(x*1.05))})`;
			box.can.sol.fillRect(0,0,128,32);
			core.drawImage(box.can.sol,brp,0,0,128,32,0,0,128,32);
			cmii["box.png"].src=box.can.sol.canvas.toDataURL();//覆盖原图
			let playco=box.getBLS(floorId).playercolor||style[4];
			box.can.play.fillStyle=`rgb(${Array.from(htr(playco),x=>lt(x*1.05))})`;
			box.can.play.fillRect(0,0,128,128);
			core.drawImage(box.can.play,prp,0,0,128,128,0,0,128,128);
			cmii["player.png"].src=box.can.play.canvas.toDataURL();//覆盖原图
			if(main.mode==='editor')setTimeout(()=>{core.redrawMap();},0);
		},dpsearch:function(floorId=core.status.floorId,depth=2){//进行深度搜索
			if(floorId==="debug")return [];
			const fcn=Number(core.floors[floorId].boxsetting.codename.slice(1));
			const bl=core.floors[floorId].boxsetting.boxlevel;
			const dparray=[box.search.many([fcn],bl)];
			for(let i=1;i<depth;i++){
				let arr=box.search.many(dparray[i-1],bl);
				let prev=dparray.flat();
				dparray[i]=arr.filter(x=>!prev.includes(x));
			}
			return dparray;
		},bgimg:{
			bg:{},
			get:function(name){//可能输出canvas/img标签
				if(["0","1","2","3","4","5"].includes(name))return cmii["bg"+name+".png"];
				return box.bgimg.bg[name]||cmii["bg0.png"];
			},set:function(name,can){//可输入canvas标签
				name=""+name;
				if(["0","1","2","3","4","5"].includes(name)){cmii["bg"+name+".png"].src=can.toDataURL();return};
				if(!box.bgimg.bg[name]){
					box.bgimg.bg[name]=document.createElement('canvas');
					const nbg=box.bgimg.bg[name];
					nbg.width=nbg.height=416;
					nbg.getContext('2d').drawImage(can,0,0);
				}else box.bgimg.bg[name].getContext('2d').drawImage(can,0,0);
			},
		},search:{
			ash:function(icn,bl){
				const oneset=new Set();
				const level=box.gabl(bl,"a"+icn)
				const map=core.getMapArray(level);
				let fsize=box.floorsize(level);
				for(let y=0;y<fsize;y++){
					for(let x=0;x<fsize;x++){
						let cn=map[y][x]-bt.bx+1;
						if(cn>=1&&cn<=100)oneset.add(cn);
					}
				}
				return [...oneset];
			},many:function(cns,bl){
				const arrays=[];
				for(let cn of cns)arrays.push(box.search.ash(cn,bl));
				return [...new Set(arrays.flat())];
			},every:function(cns,bl){
				const out={};
				for(let cn of cns)out[cn]=box.search.ash(cn,bl);
				return out;
			}
		},getBLS:function(floorId=core.status.floorId){
			if(!core.floors[floorId].boxsetting)return {};
			let Alevel=box.gabl(core.floors[floorId].boxsetting.boxlevel,"a1");
			return core.floors[Alevel].boxlevelsetting;
		},llchange:function(dparray,bl){
			
			const cutmap=(floorId)=>{
				let map=core.clone(core.getMapArray(floorId));
				const fsize=box.floorsize(floorId);
				map.length=fsize;
				for(let l of map)l.length=fsize;
				return map;
			}
			const isis=(a,b)=>{
				if(a.length!==b.length)return false;
				const size=a.length;
				for(let y=0;y<size;y++){
					for(let x=0;x<size;x++){
						if(a[y][x]!==b[y][x])return false;
					}
				}
				return true;
			};
			const hc=core.status.floorId;
			const ohc=box.list.templevel.heroloc;
			box.list.templevel.heroloc=hc;
			const ce={},dp=dparray,dlm=dp.length-1;
			for(let i=dlm;i>=0;i--){
				let ey;if(i!==dlm)ey=box.search.every(dp[i],bl);
				ot:for(let cn of dp[i]){
					let fid=box.gabl(bl,"a"+cn);
					let cmap=cutmap(fid);
					if([hc,ohc].includes(fid)||(!box.list.templevel[fid])||
						(!isis(box.list.templevel[fid],cmap))){
						box.list.templevel[fid]=cmap;ce[cn]=true;continue;
					}else if(i!==dlm){
						const boxs=ey[cn];
						for(let box of boxs){if(ce[box]){ce[cn]=true;continue ot;}}
						ce[cn]=false;
					}else ce[cn]=false;
				}
			}
			return ce;
		},spt:function(array,chunkSize,beg){
			let arrs=[];
			for (let i=0;i<array.length;i+=chunkSize){
				let chunk=array.slice(i,i+chunkSize);
				let str=(i===0)?beg:"";
				str+=JSON.stringify(chunk);
				if(i+chunkSize<array.length)str+="…"
				arrs.push(str);
			}
			return arrs;
		},winset:function(){
			let winset=core.getFlag("winset");
			let bid=core.floors[core.status.floorId].boxsetting.boxlevel;
			if(bid!=="root")winset.push(bid);
			if(winset.includes("root")){//虽然理论上不会再出现，但还是防一手古存档
				core.setFlag("winset",winset.filter((e)=>e!=="root"));
			}
		},nowin:function(){
			let winarr=[...new Set(core.getFlag("winset"))];
			let allarr=[...new Set(core.floorIds.filter((i)=>(i!=="space")).map((i)=>core.floors[i].boxsetting.boxlevel))];
			let nowinarr=allarr.filter((l)=>((l!=="root")&&(l!=="debug")&&(!winarr.includes(l))));
			return nowinarr.map((bid)=>{
				return core.floors[core.box.gabl(bid, "a1")].title;
			});
		},visitroot:function(){
			let visited=core.getFlag("__visited__");
			if(!visited){visited={};core.setFlag("__visited__",visited)}
			for(l of box.gbls("root"))visited[l]=true;
		},makecn:function(){
			let fids=core.floorIds.filter((i)=>((i!=="space")&&(i!=="debug")));
			let blfloors={};
			for(i of fids){
				let f=core.floors[i];
				let bs=((main.mode==="editor")?core.clone(f.boxsetting):(f.boxsetting));
				if(main.mode==="editor")bs.floorId=f.floorId;
				if(!(bs&&bs.boxlevel)){
					if(main.mode==="editor")continue;
					alert("makecn方法未完全执行，请作者检查！");return;
				}
				let bl=bs.boxlevel;
				if(!blfloors[bl]){blfloors[bl]=[];}
				blfloors[bl].push(bs);//blfloors里的是各楼层的boxsetting
			}
			for(let id in blfloors){
				let fs=blfloors[id];
				let cns=[],ncfloors=[];
				for(let b of fs){
					let cn=b.codename;
					if(cn)cns.push(cn);
					else ncfloors.push(b);
				}
				for(let i=1;ncfloors.length!==0;i++){
					let ac="a"+i;
					if(cns.includes(ac))continue;
					ncfloors[0].codename=ac;
					ncfloors.shift();
				}
				for(let b of fs){
					if(b.groundimage)continue;
					let cn=Number(b.codename.slice(1));
					b.groundimage="bg"+(cn-1);
				};
			}
			if(main.mode==="editor")window.makecnFloors=blfloors;
		},cmii:function(){cmii=core.material.images.images;}
	};
	const box=core.box;const bd=box.deed;
	let bt={pb:box.list.pbutton,bb:box.list.bbutton,kg:box.list.kuang,bl:box.list.blocks,n:box.list.num1,bx:box.list.box1,hb:box.list.hollowblocks};
	
	box.makecn();
	main.dom.loadGame.onclick=()=>
		core.myconfirm('确定要去原作者的steam主页支持作者吗？',()=>
			window.open("https://store.steampowered.com/app/1260520/Patricks_Parabox"));
	
	control.prototype.moveHero = function (direction, callback) {
		// 如果正在移动，直接return
		if (core.status.heroMoving!=0) return;
		if (core.isset(direction))
			core.setHeroLoc('direction', direction);
		box.extinguish();
		if(callback)return this.moveAction(callback);
		this._moveHero_moving();
	}
	control.prototype._moveAction_noPass = function (canMove, callback) {
		core.status.route.push(core.getHeroLoc('direction'));
		core.status.automaticRoute.moveStepBeforeStop = [];
		core.status.automaticRoute.lastDirection = core.getHeroLoc('direction');
		box.pushbox();
		if (canMove) core.trigger(core.nextX(), core.nextY());
		core.drawHero();
		box.illumine();
		if (core.status.automaticRoute.moveStepBeforeStop.length==0) {
			core.clearContinueAutomaticRoute();
			core.stopAutomaticRoute();
		}
		if (callback) callback();
	}
	control.prototype.moveAction = function (callback) {
		if (core.status.heroMoving>0) return;
		var noPass = core.noPass(core.nextX(), core.nextY()), canMove = core.canMoveHero();
		// 下一个点如果不能走
		if (noPass || !canMove) return this._moveAction_noPass(canMove, callback);
		core.autosave();this._moveAction_moving(callback);
	}
	
	events.prototype._changeFloor_changing = function (info, callback) {
		this.changingFloor(info.floorId, info.heroLoc);
		box.onestep();
		// 回归视角
		var __lockViewport__ = flags.__lockViewport__;
		core.setFlag('__lockViewport__', null);
		core.drawHero();
		core.setFlag('__lockViewport__', __lockViewport__);    
		if (info.time == 0)
			this._changeFloor_afterChange(info, callback);
		else
			core.hideWithAnimate(core.dom.floorMsgGroup, info.time / 4, function () {
				core.events._changeFloor_afterChange(info, callback);
			});
	}
	actions.prototype.__checkLeftHandPrefer = function (e) {
		//强制左手模式，不管存读档
		var map = {
			87: 38, // W -> up 
			83: 40, // S -> down
			65: 37, // A -> left
			68: 39, // D -> right
		}
		var newEvent = {};
		for (var one in e) {
			if (!(e[one] instanceof Function)) {
				newEvent[one] = e[one];
			}
		};
		["stopPropagation", "stopImmediatePropagation", "preventDefault"].forEach(function (one) {
			newEvent[one] = function () {
				return e[one]();
			}
		});
		newEvent.keyCode = map[e.keyCode] || e.keyCode;
		return newEvent;
	}
	events.prototype.openBook = function (fromUserAction) {core.doSL("autoSave", "load");}
	ui.prototype._drawQuickShop = function () {core.doSL("autoSave", "reload");}
	main.statusBar.image.fly.onclick = function (e) {
		if (core.isReplaying()){core.stopReplay();return;}// 播放录像时
		if (main.core.isPlaying()) {
			if (!main.core.flags.equipboxButton) {
				if (core.status.event.id=='viewMaps')main.core.useFly(true);
				else core.useItem('goout', true);
			}else{main.core.openEquipbox(true);}
		}
	}
	actions.prototype._clickSwitchs_action_moveSpeed = function (delta) {
		core.values.moveSpeed = core.clamp(core.values.moveSpeed + delta, 10, 200);
		core.setLocalStorage("moveSpeed", core.values.moveSpeed);
		core.ui._drawSwitchs_action();
	}
	main.dom.playGame.onclick = function () {
		main.dom.startButtons.style.display='none';
		main.core.control.checkBgm();
		cmii["box2.png"].src="data:image/png;base64,iVBORw0KGgoA"+
		"AAANSUhEUgAAAIAAAAAgCAYAAADaInAlAAAAAXNSR0IArs4c6QAABa5JREFUeF7tW11sFFUU/mZ2t6"+
		"E2hmhihFStXdqaEnhoIVWUCImJgBE0qaIY8Qdoq6jBFEM0URBqQ7WNYtIHWkTANqZdfyJiqjzx0tAf"+
		"IoIYlUq7NSIxMcZoNDa0u9fce+fevTM7s+12uzMvd18289cz/c53zvnuuWeNGtJGMIvPX/0j+OPYWf"+
		"bkskPN+BMXZvFXZv+Itj83+Bs5EaDrG06AjjeCIYC2nzP+kgBjdT08HGk+MI1UaBLiOAYM9boSxIQQ"+
		"/P4gf3btuqasSKHtB4O/nQBO5ys8MAzlgHrYcSjIs/nKz5hKElz8oit7Amj79mDzAf8UAbb1uBq3OV"+
		"51upMQNFMAeCgeR5IA4ye7Md7/iWeRn7+ywnZtTNsPBP8UAep7Uw6xHC2dT4/V6HSLflE+2LelK8WX"+
		"45jeEu18xE4AbT8Q/NMJ4OZ8Gu2qFhDRLxyrZgN6jqYA8YxFDKoPpMbIRABtn8FkC7484m8ngAV+8/"+
		"ouz9R9dXJSXms6VW8Xi0rwM8JQnxOCr7q/Z/eteaxSksA1A2j7DB8/8beLQNNgzCsbL0EkHMGmht2o"+
		"uv02nB++iO721xEyTWzYtgufH3kHD9Q3InbtURtRRj8dlOmfBbxFgpLhBBYuuRuD8/ohMkG0w1EC6C"+
		"pE2/cd/xQBGrgGoAQovVSM1ffcj0VL78LhA69g64v72Tf9PLp9D3756QxuWVyDllPbJQGiG2ow+tmQ"+
		"dDp1viDB3jXH2H17+p7wJoC2Hwj+NgKwuqMKPnpI1/zivGGwY+rssRPD0vnF565HUeQa1O1qRWfLTi"+
		"QIwWjlr4wMjASJpFIq6GmCtAzQ0MvrnrafEtw+4O9OAMXhLCuEOAk4GYDoeosApskcu2/VIXQf3Ie6"+
		"xha0v/UyCpKTGKm8zP4RQgWhogfYuekIoO3zQLDEdT7xt4lA4WAR8TL66buETfZSfZ3nZTTf92yVLb"+
		"K5o7n4Y463nP/l0e/YfWsfX8zZDaD04Eabfhir75UE0/Z5oPmBvyRA/JlYinUW+xgBrRIgWNjXcQ47"+
		"V9eidstLaB58TjqRiTsp/AhIwiJDkqB0JIotL+zGq31PSoI4CaDtB4N/ZgJYKd8I8einH5oBTn8wwJ"+
		"crQxYB1IaPkvIJrf0EaLr3CLs/awJo+zwT5BF/bwKw2s8dL1+A1nwmByyxpiZxq7aXfLsQbe/34uG2"+
		"lSBTXPxV/HgTrianEK+4MvMMoO37gr8vBJjs+wflt5YFRgBt3xt/VwLcXD7IIvdy/M60FISQaW9T0h"+
		"unKQFuK4FMGkDb9w9/VwJMnFmOcKQAkWUDaSJQKnTHbqCXCGRikJFEFYnpqwBVBGr7/uHv2gcQvft1"+
		"Ty2RTSCpA1j9d9cAXstAWzPIaxmoNIK0fb534gf+M24EFfy3H39fOI6yjW8jHmtEzWutGNi7A0AIJP"+
		"Ev5ldvxoLlqzByeAdAEpi34uycNoK0/fzg790Kpss+Vu95R2pisJqxsmxTu9T+l7q2wjQjSGAKRqQI"+
		"FU+/i5H3nmeJoJARwGoF06WhUgam7QTS27V9X/B33QxaUD7E/PXb2B28BSzakmp70jEXyISeo9arO4"+
		"KyQ2jNBbjtBdDH6RJT2/cPf9ft4ImvqxEOFyJcdVp2BykJFtWusLVv6QHbAhYTQmLAXNkJFN1BShDq"+
		"3JlsB2v7/uHvOhBy8sMfmKNZ7965MWMNBzkng7mDU9NgNtVvLRXVqaBMAyHavn/4Zx4JE5EtlnxKCU"+
		"hLBeKEWO6JcuDifHrKcyZQHQnT9jmqecR/2qFQGrUy2p2zaU4WOGYBnWlfzgpmMRSq7ecXf0mA67DU"+
		"M6jdLtCR7xtb+RRR4fFYVs/Sm50/JdP2g8E/p5+GFR/4iDk+8nFPVj8CyZotLg/Q3wZq+7njnxMBbn"+
		"iTZ4CiE7FACKDt547/rAkwF1Gs/0bwCGgCBO+DQN/gf41QILc7R4leAAAAAElFTkSuQmCC";
		//一些magic小技巧，进行“优化”（此为magiclevelB.png的base64形式）
		if (main.levelChoose.length == 0) {
			core.events.startGame("");
		} else {
			main.dom.levelChooseButtons.style.display='block';
			main.selectedButton = null;
			main.selectButton(0);
		}
	}
	control.prototype._bindRoutePush = function () {
		core.status.route.push = function (element) {
			if(element==='item:seescore')return;
			// 忽视移动、转向、瞬移
			if (["up", "down", "left", "right", "turn"].indexOf(element) < 0 && !element.startsWith("move:")) {
				core.clearRouteFolding();
			}
			Array.prototype.push.call(core.status.route, element);
		}
	}
	events.prototype._changeFloor_playSound = function () {
		// 播放换层音效
		if (core.hasFlag('__fromLoad__')) // 是否是读档造成的切换
			core.playSound('读档');
		else if (core.hasFlag('__isFlying__')) // 是否是楼传造成的切换
			core.playSound('飞行器');
		//else
		//	core.playSound('上下楼');
	}
	
},
    "resize": function(){
	if(main.replayChecking)return;
	const box=core.box;
	let _drawBg_drawBackground=maps.prototype._drawBg_drawBackground;
	maps.prototype._drawMap_drawAll = function (floorId, config) {
		floorId = floorId || core.status.floorId || core.firstData.floorId;
		this.drawBg(floorId, config);
		this.drawEvents(floorId);
		this.drawFg(floorId, config);
	}
	
	maps.prototype._drawBg_drawBackground = function (floorId, config) {
		_drawBg_drawBackground.call(this,floorId, config);
		box.bg(floorId,config.ctx);
	}
	maps.prototype._drawBlockInfo_bgfg = function (blockInfo, name, x, y, ctx) {
		var image = blockInfo.image, posX = blockInfo.posX, posY = blockInfo.posY, height = blockInfo.height;
		var px = 32 * x - 32 * core.bigmap.posX;
		var py = 32 * y - 32 * core.bigmap.posY;
		if (ctx == null) ctx = name;
	
		/*删去若干*/
		var alpha = null;
		if (blockInfo.opacity != null) alpha = core.setAlpha(ctx, blockInfo.opacity);
		else if (name == 'fg' && this._drawBlockInfo_shouldBlurFg(x, y)) alpha = core.setAlpha(ctx, 0.6);
		core.setFilter(ctx, blockInfo.filter);
		core.drawImage(ctx, image, posX * 32, posY * height, 32, height, px, py + 32 - height, 32, height);
		core.setFilter(ctx, null);
		if (alpha != null) core.setAlpha(ctx, alpha);
	}
	maps.prototype._drawBlockInfo_bigImage = function (blockInfo, x, y, ctx) {
		var bigImageInfo = this._getBigImageInfo(blockInfo.bigImage, blockInfo.face, blockInfo.posX);
		var per_width = bigImageInfo.per_width, per_height = bigImageInfo.per_height, sx = bigImageInfo.sx, sy = bigImageInfo.sy;
		var bigImage = blockInfo.bigImage;
	
		if (main.mode == 'editor') {
			var px = 32 * x - 32 * core.bigmap.posX;
			var py = 32 * y - 32 * core.bigmap.posY;
			if (ctx == null) ctx = 'event';
			core.clearMap(ctx, px, py, 32, 32);
			core.drawImage(ctx, bigImage, sx, sy, per_width, per_height, px, py, 32, 32);
			return;
		}
	
		var px = 32 * x - core.bigmap.offsetX;
		var py = 32 * y - core.bigmap.offsetY;
		
		// 上半部分 - 会遮挡勇士；z值高于event2，为51
		var header = "_bigImage_header_" + x + "_" + y;
		// 下半部分 - 会被勇士遮挡；z值高于event，为31
		var body = "_bigImage_body_" + x + "_" + y;
		var dx = bigImageInfo.dx, dy = bigImageInfo.dy;
	
		switch (bigImageInfo.face) {
			case "down": case "up": case "left": case "right":
				let hc=core.createCanvas(header, px + dx, py + dy, per_width, -dy, 51);
				hc.imageSmoothingEnabled=false;
				this._drawBlockInfo_drawWithFilter(blockInfo, header, function () {
					core.drawImage(header, bigImage, sx, sy, per_width, -dy, 0, 0, per_width, -dy);
				});
				let bc=core.createCanvas(body, px + dx, py, per_width, 32, 31);
				bc.imageSmoothingEnabled=false;
				this._drawBlockInfo_drawWithFilter(blockInfo, body, function () {
					core.drawImage(body, bigImage, sx, sy - dy, per_width, 32, 0, 0, per_width, 32);
				})
				break;
		}
		if (core.dymCanvas[header]) {
			core.dymCanvas[header].canvas.setAttribute('_ox', 32 * x + dx);
			core.dymCanvas[header].canvas.setAttribute('_oy', 32 * y + dy);
		}
		if (core.dymCanvas[body]) {
			core.dymCanvas[body].canvas.setAttribute('_ox', 32 * x + dx);
			core.dymCanvas[body].canvas.setAttribute('_oy', 32 * y);
		}
	}
	if(main.mode == 'editor')return;
	(()=>{
		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;
	})();
	control.prototype.resize = function() {
		var clientWidth = main.dom.body.clientWidth, clientHeight = main.dom.body.clientHeight;
		//改该行
		var CANVAS_WIDTH = 416, BAR_WIDTH = 129;
		var BORDER = 3;
		var extendToolbar = core.flags.extendToolbar;
	
		var horizontalMaxRatio = (clientHeight - 2 * BORDER - (extendToolbar ? BORDER : 0)) / (CANVAS_WIDTH + (extendToolbar ? 38 : 0));
	
		if (clientWidth - 3 * BORDER >= CANVAS_WIDTH + BAR_WIDTH || (clientWidth > clientHeight && horizontalMaxRatio < 1)) {
			// 横屏
			core.domStyle.isVertical = false;
	
			core.domStyle.availableScale = [];
			[1, 1.25, 1.5, 1.75, 2].forEach(function (v) {
				if (clientWidth - 3 * BORDER >= v*(CANVAS_WIDTH + BAR_WIDTH) && horizontalMaxRatio >= v) {
					core.domStyle.availableScale.push(v);
				}
			});
			if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) {
				core.domStyle.scale = Math.min(1, horizontalMaxRatio);
			}
		}
		else {
			// 竖屏
			core.domStyle.isVertical = true;
			core.domStyle.scale = Math.min(1, (clientWidth - 2 * BORDER) / CANVAS_WIDTH);
			core.domStyle.availableScale = [];
			extendToolbar = false;
		}
	
		var statusDisplayArr = this._shouldDisplayStatus(), count = statusDisplayArr.length;
		var statusCanvas = core.flags.statusCanvas, statusCanvasRows = core.values.statusCanvasRowsOnMobile || 3;
		var col = statusCanvas ? statusCanvasRows : Math.ceil(count / 3);
		if (col > 5) {
			if (statusCanvas) alert("自绘状态栏的在竖屏下的行数应不超过5！");
			else alert("当前状态栏数目("+count+")大于15，请调整到不超过15以避免手机端出现显示问题。");
		}
		var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute;
	
		var obj = {
			clientWidth: clientWidth,
			clientHeight: clientHeight,
			CANVAS_WIDTH: CANVAS_WIDTH,
			BORDER: BORDER,
			BAR_WIDTH: BAR_WIDTH,
			TOOLBAR_HEIGHT: 38,
			outerSize: CANVAS_WIDTH * core.domStyle.scale + 2 * BORDER,
			globalAttribute: globalAttribute,
			border: '3px ' + core.arrayToRGBA(globalAttribute.borderColor) + ' solid',
			statusDisplayArr: statusDisplayArr,
			count: count,
			col: col,
			statusBarHeightInVertical: core.domStyle.isVertical ? (32 * col + 6) * core.domStyle.scale + 2 * BORDER : 0,
			toolbarHeightInVertical: core.domStyle.isVertical ? 38 * core.domStyle.scale + 2 * BORDER : 0,
			extendToolbar: extendToolbar,
			is15x15: core.__SIZE__ == 15
		};
	
		this._doResize(obj);
		this.setToolbarButton();
		core.updateStatusBar();
	}
	core.control._drawHero_updateViewport=function(x, y, offset){}
	control.prototype._resize_canvas = function (obj) {
		let cw=core.__PIXELS__;//352
		let sca=core.domStyle.scale;
		let gamedraw=core.dom.gameDraw,mul=11/box.floorsize(core.status.floorId);
		for (var i = 0; i < core.dom.gameCanvas.length; ++i)
		{
			core.dom.gameCanvas[i].style.width = core.dom.gameCanvas[i].style.height = (cw * sca) + "px";
		}
		core.dom.gif.style.width = core.dom.gif.style.height = (cw*sca/mul)+"px";
		core.dom.gif2.style.width = core.dom.gif2.style.height = (cw*sca/mul)+"px";	
		
		
		gamedraw.style.width = parseInt(cw*sca/mul)+"px";
		gamedraw.style.height = parseInt(cw*sca/mul)+"px";
		gamedraw.style.top = (obj.statusBarHeightInVertical+32*sca+3) + "px";
		gamedraw.style.right = parseInt(32+cw-cw/mul)*sca+3 + "px";
		gamedraw.style["transform-origin"]="top left";
		gamedraw.style.transform="scale("+mul+")";
		gamedraw.style.overflow="visible";
		let data=core.canvas.data.canvas,ui=core.canvas.ui.canvas;
		data.style["transform-origin"]="top left";
		data.style.transform="scale("+(1/mul)+")";
		ui.style["transform-origin"]="top left";
		ui.style.transform="scale("+(1/mul)+")";
		let ac=false;
		let width=352;width/=mul;
		core.bigmap.canvas.forEach(function (cn) {
			let cs=core.canvas[cn].canvas;
			cs.style.width = width*sca + "px";
			cs.style.height = width*sca + "px";
			
			if (core.domStyle.hdCanvas.indexOf(cn) >= 0)
				core.maps._setHDCanvasSize(core.canvas[cn],width,width);
			else {
				let c=box.csize(cs,width);
				ac=ac||c;
			}
		});
		
		if(ac)core.redrawMap();
		["hero","animate","curtain"].forEach(function (cn) {
			let cs=core.canvas[cn].canvas;
			cs.style.width = width*sca + "px";
			cs.style.height = width*sca + "px";
			box.csize(cs,width);
		});

		// 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 * sca + "px";
			canvas.style.height = canvas.height  / ratio * sca + "px";
			canvas.style.left = parseFloat(canvas.getAttribute("_left"))  * sca + "px";
			canvas.style.top = parseFloat(canvas.getAttribute("_top"))  * sca + "px";
			if(name==="replay"){
				canvas.style.top=parseFloat(canvas.getAttribute("_top"))*sca/mul+"px";
				canvas.style["transform-origin"]="top left";
				canvas.style.transform="scale("+(1/mul)+")";
			}
			
		}
		// resize next
		main.dom.next.style.width = main.dom.next.style.height = 5 * sca + "px";
		main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * sca + "px";
		

		let outcs;
		//试图创建out层
		if(!box.out){
			outcs= document.createElement('canvas');
			box.out=outcs.getContext('2d');
			outcs.width=outcs.height=416;
			outcs.style.position="absolute";
			outcs.style.border = obj.border;
			outcs.style.right="0";
			outcs.onmouseup=box.outclick;
			core.dom.gameGroup.appendChild(outcs);
		}else outcs=box.out.canvas;
		
		outcs.style.top=obj.statusBarHeightInVertical+"px";
		outcs.style.width=outcs.style.height=416*sca+"px";
		
		[box.out,...Object.values(box.can)].forEach(function (scs) {
			scs.imageSmoothingEnabled=false;
		});


	}
	core.registerResize("canvas", control.prototype._resize_canvas);
	ui.prototype._drawHelp = function () {
		core.playSound('打开界面');
		core.clearUI();
		if (core.material.images.keyboard) {
			core.status.event.id = 'help';
			core.lockControl();
			core.setAlpha('ui', 1);
			core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000');
			core.drawImage('ui', core.material.images.keyboard,0,0,352,352);//****
		}
		else {
			core.drawText([//****
				"\t[键盘快捷键列表]"+
				"[Z] 取消操作   [X] 重新操作\n" + 
				"[R] 重置谜题   [ESC] 系统菜单\n" +
				"[CTRL] 跳过对话\n" +
				"[G] "+core.material.items['fly'].name+"\n" +
				"[K/L] 存读档页面\n" +
				"[T] 道具页面   [Q] 装备页面\n" +
				"[B] 数据统计   [H] 帮助页面\n" +
				"[Y] 回放录像   [E] 显示光标\n" +
				"[P] 游戏主页\n" +
				"[O] 查看工程   [F7] 打开debug穿墙模式\n" +
				"[PgUp/PgDn] 浏览地图\n"+
				"[Alt+0~9] 快捷换装",
				"\t[鼠标操作]"+
				"点状态栏中图标： 进行对应的操作\n"+
				"点任意块： 寻路并移动\n"+
				"点任意块并拖动： 指定寻路路线\n"+
				"单击勇士： 转向\n"+
				"长按任意位置：跳过剧情对话或打开虚拟键盘"
			]);
		}
	}
},
    "mouse": function(){
	const box=core.box;
	if(main.replayChecking)return;
	actions.prototype.doRegisteredAction = function (action) {
		var actions = this.actions[action];
		if (!actions) return false;
		switch(action){
			case"ondown":case"onmove":case"onup":
				arguments[1]-=1;arguments[2]-=1;arguments[3]-=32;arguments[4]-=32;
		}
		for (var i = 0; i < actions.length; ++i) {
			try {
				if (core.doFunc.apply(core, [actions[i].func, this].concat(Array.prototype.slice.call(arguments, 1))))
					return true;
			}
			catch (e) {
				main.log(e);
				main.log("ERROR in actions["+actions[i].name+"].");
			}
		}
		return false;
	}
	core.registerAction('onmove', '_sys_onmove', function (xo, yo, pxo, pyo) {
		let [x,y,px,py]=box.mouseloc(pxo,pyo);
		if (core.status.lockControl) return false;
	
		if (core.status.preview.dragging) {
			core.setViewport(core.bigmap.offsetX - px + core.status.preview.px, core.bigmap.offsetY - py + core.status.preview.py);
			core.status.preview.px = px;
			core.status.preview.py = py;
			return true;
		}
		if (core.status.preview.prepareDragging) {
			if (Math.abs(px - core.status.preview.px) <= 20 && Math.abs(py - core.status.preview.py) <= 20) 
				return true;
			else core.status.preview.prepareDragging = false;
		}
	
		clearTimeout(core.timeout.onDownTimeout);
		core.timeout.onDownTimeout = null;
	
		if ((core.status.stepPostfix || []).length > 0) {
			var pos = {'x': parseInt((px + core.bigmap.offsetX) / 32), 'y': parseInt((py + core.bigmap.offsetY) / 32)};
			var pos0 = core.status.stepPostfix[core.status.stepPostfix.length - 1];
			var directionDistance = [pos.y - pos0.y, pos0.x - pos.x, pos0.y - pos.y, pos.x - pos0.x];
			var max = 0, index = 4;
			for (var ii = 0; ii < 4; ii++) {
				if (directionDistance[ii] > max) {
					index = ii;
					max = directionDistance[ii];
				}
			}
			pos = [{'x': 0, 'y': 1}, {'x': -1, 'y': 0}, {'x': 0, 'y': -1}, {'x': 1, 'y': 0}, false][index]
			if (pos) {
				pos.x += pos0.x;
				pos.y += pos0.y;
				core.status.stepPostfix.push(pos);
				//删去一行
			}
		}
		return true;
	}, 0);
	core.registerAction('ondown', '_sys_ondown',function (xo, yo, pxo, pyo) {
		let [x,y,px,py]=box.mouseloc(pxo,pyo);
		if (core.status.lockControl) return false;
		core.status.downTime = new Date();
		core.deleteCanvas('route');
		var pos = {'x': parseInt((px + core.bigmap.offsetX) / 32), 'y': parseInt((py + core.bigmap.offsetY) / 32)};
		core.status.stepPostfix = [];
		core.status.stepPostfix.push(pos);
		//删去一行
	
		clearTimeout(core.timeout.onDownTimeout);
		core.timeout.onDownTimeout = null;
		core.status.preview.prepareDragging = false;
		if (!core.hasFlag('__lockViewport__') && (core.status.thisMap.width > core.__SIZE__ || core.status.thisMap.height > core.__SIZE__)) {
			core.status.preview.prepareDragging = true;
			core.status.preview.px = px;
			core.status.preview.py = py;
			core.timeout.onDownTimeout = setTimeout(function () {
				core.clearMap('ui');
				core.status.preview.prepareDragging = false;
				core.status.preview.enabled = true;
				core.status.preview.dragging = true;
				core.drawTip('已进入预览模式，可直接拖动大地图');
				core.status.stepPostfix = [];
			}, 500);
		} 
	}, 0);
	core.registerAction('onup', '_sys_onup',function (xo, yo, pxo, pyo) {
		let [x,y,px,py]=box.mouseloc(pxo,pyo);
		return actions.prototype._sys_onup.call(this,x,y,px,py);
	}, 0);
	maps.prototype._automaticRoute_deepAdd = function (x, y, blocks) {
		// 判定每个可通行点的损耗值，越高越应该绕路
		var deepAdd = 1;
		var block = blocks[x+","+y];
		if (block && !block.disable) {
			var id = block.event.id;
			// 绕过亮灯
			if (id == "light") deepAdd += 100;
			// 绕过路障
			if (id.endsWith("Net") && !core.hasFlag(id.substring(0, id.length - 3))) deepAdd += 100;
			// 绕过血瓶和绿宝石
			if (core.hasFlag('__potionNoRouting__') && (id.endsWith("Potion") || id == 'greenGem')) deepAdd += 100;
			// 绕过传送点
			if (core.getBgNumber(x,y)===301) deepAdd+=10;
		}
		// 绕过存在伤害的地方
		deepAdd += (core.status.checkBlock.damage[x+","+y]||0) * 100;
		// 绕过捕捉
		if (core.status.checkBlock.ambush[x+","+y]) deepAdd += 1000;
		return deepAdd;
	}
	maps.prototype._automaticRoute_bfs = function (startX, startY, destX, destY) {
		var route = {}, canMoveArray = this.generateMovableArray();
		// 使用优先队列
		var queue = new PriorityQueue({comparator: function (a,b) { return a.depth - b.depth; }});
		route[startX + "," + startY] = '';
		queue.queue({depth: 0, x: startX, y: startY});
		var blocks = core.getMapBlocksObj();
		while (queue.length!=0) {
			var curr = queue.dequeue(), deep = curr.depth, nowX = curr.x, nowY = curr.y;
			for (var direction in core.utils.scan) {
				if (!core.inArray(canMoveArray[nowX][nowY], direction)) continue;
				var nx = nowX + core.utils.scan[direction].x;
				var ny = nowY + core.utils.scan[direction].y;
				//此句做修改***********
				if (nx<0 || ny<0 || route[nx+","+ny] != null) continue;
				// 重点
				if (nx == destX && ny == destY) {
					route[nx+","+ny] = direction;
					break;
				}
				// 不可通行
				if (core.noPass(nx, ny)) continue;
				route[nx+","+ny] = direction;
				queue.queue({depth: deep + this._automaticRoute_deepAdd(nx, ny, blocks), x: nx, y: ny});
			}
			if (route[destX+","+destY] != null) break;
		}
		return route;
	}
}
}