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

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

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


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

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

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

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

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

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

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

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

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

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

		_shouldProcessKeyUp = true;

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

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

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

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

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

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

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

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

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

	var _shouldProcessKeyUp = true;

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

		if (!core.hasFlag("@temp@shop") || core.status.event.data.type != 'choices') return false;
		var data = core.status.event.data.current;
		var choices = data.choices;
		var topIndex = core.actions._getChoicesTopIndex(choices.length);
		if (keycode == 88 || keycode == 27) { // X, ESC
			core.actions._clickAction(core.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);
},
    "removeMap": function () {
	// 高层塔砍层插件，删除后不会存入存档，不可浏览地图也不可飞到。
	// 推荐用法：
	// 对于超高层或分区域塔，当在1区时将2区以后的地图删除；1区结束时恢复2区，进二区时删除1区地图，以此类推
	// 这样可以大幅减少存档空间，以及加快存读档速度

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		core.insertAction([{
				"type": "while",
				"condition": "true",
				"data": [
					{ "type": "function", "function": "function () { core.plugin._drawItemShop(); }" },
					{ "type": "wait" },
					{ "type": "function", "function": "function() { core.plugin._performItemShopAction(); }" }
				]
			},
			{
				"type": "function",
				"function": "function () { core.deleteCanvas('uievent'); core.ui.clearUIEventSelector(); }"
			}
		]);
	}

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

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

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

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

	// 也可以复写其他的属性颜色如怪物攻防等，具体参见下面的例子的注释部分
	core.ui._drawBook_drawRow1 = function (index, enemy, top, left, width, position) {
		// 绘制第一行
		core.setTextAlign('ui', 'left');
		var b13 = this._buildFont(13, true),
			f13 = this._buildFont(13, false);
		var col1 = left,
			col2 = left + width * 9 / 25,
			col3 = left + width * 17 / 25;
		core.fillText('ui', '生命', col1, position, '#DDDDDD', f13);
		core.fillText('ui', core.formatBigNumber(enemy.hp || 0), col1 + 30, position, /*'red' */ null, b13);
		core.fillText('ui', '攻击', col2, position, null, f13);
		core.fillText('ui', core.formatBigNumber(enemy.atk || 0), col2 + 30, position, /* '#FF0000' */ null, b13);
		core.fillText('ui', '防御', col3, position, null, f13);
		core.fillText('ui', core.formatBigNumber(enemy.def || 0), col3 + 30, position, /* [255, 0, 0, 1] */ null, b13);
	}


},
    "dynamicHp": function () {
	// 此插件允许人物血量动态进行变化
	// 原作：Fux2（老黄鸡）

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

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

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

	core.registerAnimationFrame('dynamicHp', true, function () {
		_check();
		if (core.status.hero.hp != _currentHp) {
			var dis = (_currentHp - core.status.hero.hp) * speed;
			if (Math.abs(dis) < 2) {
				_currentHp = core.status.hero.hp;
			} else {
				_currentHp -= dis;
			}
			core.setStatusBarInnerHTML('hp', _currentHp);
		}
	});
},
    "multiHeros": function () {
	// 多角色插件
	// Step 1: 启用本插件
	// Step 2: 定义每个新的角色各项初始数据（参见下方注释）
	// Step 3: 在游戏中的任何地方都可以调用 `core.changeHero()` 进行切换；也可以 `core.changeHero(1)` 来切换到某个具体的角色上

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

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

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

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

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

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

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

		var saveList = Object.keys(hero1);

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

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

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

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

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

		// 插入事件：改变角色行走图并进行楼层切换
		var toFloorId = data.floorId || core.status.floorId;
		var toLoc = data.loc || core.status.hero.loc;
		core.insertAction([
			{ "type": "setHeroIcon", "name": data.image || "hero.png" }, // 改变行走图
			// 同层则用changePos，不同层则用changeFloor；这是为了避免共用楼层造成触发eachArrive
			toFloorId != core.status.floorId ? {
				"type": "changeFloor",
				"floorId": toFloorId,
				"loc": [toLoc.x, toLoc.y],
				"direction": toLoc.direction,
				"time": 0 // 可以在这里设置切换时间
			} : { "type": "changePos", "loc": [toLoc.x, toLoc.y], "direction": toLoc.direction }
			// 你还可以在这里执行其他事件，比如增加或取消跟随效果
		]);
		core.setFlag("heroId", toHeroId); // 保存切换到的角色ID
	}
},
    "itemCategory": function () {
	// 物品分类插件。此插件允许你对消耗道具和永久道具进行分类，比如标记「宝物类」「剧情道具」「药品」等等。
	// 使用方法：
	// 1. 启用本插件
	// 2. 在下方数组中定义全部的物品分类类型
	// 3. 点击道具的【配置表格】，找到“【道具】相关的表格配置”，然后在【道具描述】之后仿照增加道具的分类：
	/*
	 "category": {
	 	"_leaf": true,
	 	"_type": "textarea",
	 	"_string": true,
	 	"_data": "道具分类"
	 },
	 */
	// （你也可以选择使用下拉框的方式定义每个道具的分类，写法参见上面的cls）
	// 然后刷新编辑器，就可以对每个物品进行分类了

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

	// 在这里定义所有的道具分类类型，一行一个
	var categories = [
		"宝物类",
		"辅助类",
		"技能类",
		"剧情道具",
		"增益道具",
	];
	// 当前选中的道具类别
	var currentCategory = null;

	// 重写 core.ui._drawToolbox 以绘制分类类别
	var _drawToolbox = core.ui._drawToolbox;
	core.ui._drawToolbox = function (index) {
		_drawToolbox.call(this, index);
		core.setTextAlign('ui', 'left');
		core.fillText('ui', '类别[E]：' + (currentCategory || "全部"), 15, this.PIXEL - 13);
	}

	// 获得所有应该在道具栏显示的某个类型道具
	core.ui.getToolboxItems = function (cls) {
		// 检查类别
		return Object.keys(core.status.hero.items[cls])
			.filter(function (id) {
				return !core.material.items[id].hideInToolbox &&
					(currentCategory == null || core.material.items[id].category == currentCategory);
			}).sort();
	}

	// 注入道具栏的点击事件（点击类别）
	var _clickToolbox = core.actions._clickToolbox;
	core.actions._clickToolbox = function (x, y) {
		if (x >= 0 && x <= this.HSIZE - 4 && y == this.LAST) {
			drawToolboxCategory();
			return;
		}
		return _clickToolbox.call(core.actions, x, y);
	}

	// 注入道具栏的按键事件（E键）
	var _keyUpToolbox = core.actions._keyUpToolbox;
	core.actions._keyUpToolbox = function (keyCode) {
		if (keyCode == 69) {
			// 按E键则打开分类类别选择
			drawToolboxCategory();
			return;
		}
		return _keyUpToolbox.call(core.actions, keyCode);
	}

	// ------ 以下为选择道具分类的相关代码 ------ //

	// 关闭窗口时清除分类选择项
	var _closePanel = core.ui.closePanel;
	core.ui.closePanel = function () {
		currentCategory = null;
		_closePanel.call(core.ui);
	}

	// 弹出菜单以选择具体哪个分类
	// 直接使用 core.drawChoices 进行绘制
	var drawToolboxCategory = function () {
		if (core.status.event.id != 'toolbox') return;
		var selection = categories.indexOf(currentCategory) + 1;
		core.ui.closePanel();
		core.status.event.id = 'toolbox-category';
		core.status.event.selection = selection;
		core.lockControl();
		// 给第一项插入「全部」
		core.drawChoices('请选择道具类别', ["全部"].concat(categories));
	}

	// 选择某一项
	var _selectCategory = function (index) {
		core.ui.closePanel();
		if (index <= 0 || index > categories.length) currentCategory = null;
		else currentCategory = categories[index - 1];
		core.openToolbox();
	}

	var _clickToolBoxCategory = function (x, y) {
		if (!core.status.lockControl || core.status.event.id != 'toolbox-category') return false;

		if (x < core.actions.CHOICES_LEFT || x > core.actions.CHOICES_RIGHT) return false;
		var choices = core.status.event.ui.choices;
		var topIndex = core.actions._getChoicesTopIndex(choices.length);
		if (y >= topIndex && y < topIndex + choices.length) {
			_selectCategory(y - topIndex);
		}
		return true;
	}

	// 注入点击事件
	core.registerAction('onclick', 'toolbox-category', _clickToolBoxCategory, 100);

	// 注入光标跟随事件
	core.registerAction('onmove', 'toolbox-category', function (x, y) {
		if (!core.status.lockControl || core.status.event.id != 'toolbox-category') return false;
		core.actions._onMoveChoices(x, y);
		return true;
	}, 100);

	// 注入键盘光标事件
	core.registerAction('keyDown', 'toolbox-category', function (keyCode) {
		if (!core.status.lockControl || core.status.event.id != 'toolbox-category') return false;
		core.actions._keyDownChoices(keyCode);
		return true;
	}, 100);

	// 注入键盘按键事件
	core.registerAction('keyUp', 'toolbox-category', function (keyCode) {
		if (!core.status.lockControl || core.status.event.id != 'toolbox-category') return false;
		core.actions._selectChoices(core.status.event.ui.choices.length, keyCode, _clickToolBoxCategory);
		return true;
	}, 100);

},
    "heroFourFrames": function () {
	// 样板的勇士/跟随者移动时只使用2、4两帧，观感较差。本插件可以将四帧全用上。

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

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

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

	core.events._eventMoveHero_moving = function (step, moveSteps) {
		var curr = moveSteps[0];
		var direction = curr[0], x = core.getHeroLoc('x'), y = core.getHeroLoc('y');
		// ------ 前进/后退
		var o = direction == 'backward' ? -1 : 1;
		if (direction == 'forward' || direction == 'backward') direction = core.getHeroLoc('direction');
		var faceDirection = direction;
		if (direction == 'leftup' || direction == 'leftdown') faceDirection = 'left';
		if (direction == 'rightup' || direction == 'rightdown') faceDirection = 'right';
		core.setHeroLoc('direction', direction);
		if (curr[1] <= 0) {
			core.setHeroLoc('direction', faceDirection);
			moveSteps.shift();
			return true;
		}
		if (step <= 4) core.drawHero('stop', 4 * o * step);
		else if (step <= 8) core.drawHero('leftFoot', 4 * o * step);
		else if (step <= 12) core.drawHero('midFoot', 4 * o * (step - 8));
		else if (step <= 16) core.drawHero('rightFoot', 4 * o * (step - 8)); // if (step == 8) {
		if (step == 8 || step == 16) {
			core.setHeroLoc('x', x + o * core.utils.scan2[direction].x, true);
			core.setHeroLoc('y', y + o * core.utils.scan2[direction].y, true);
			core.updateFollowers();
			curr[1]--;
			if (curr[1] <= 0) moveSteps.shift();
			core.setHeroLoc('direction', faceDirection);
			return step == 16;
		}
		return false;
	}
},
    "startCanvas": function () {
	// 使用本插件可以将自绘的标题界面居中。仅在【标题开启事件化】后才有效。
	// 由于一些技术性的原因，标题界面事件化无法应用到覆盖状态栏的整个界面。
	// 这是一个较为妥协的插件，会在自绘标题界面时隐藏状态栏、工具栏和边框，并将画布进行居中。
	// 本插件仅在全塔属性的 "startCanvas" 生效；进入 "startText" 时将会离开居中状态，回归正常界面。

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

	// 检查【标题开启事件化】是否开启
	if (!core.flags.startUsingCanvas || main.mode != 'play') return;

	var _isTitleCanvasEnabled = false;
	var _getClickLoc = core.actions._getClickLoc;
	this._setTitleCanvas = function () {
		if (_isTitleCanvasEnabled) return;
		_isTitleCanvasEnabled = true;

		// 禁用窗口resize
		window.onresize = function () {};
		core.resize = function () {}

		// 隐藏状态栏
		core.dom.statusBar.style.display = 'none';
		core.dom.statusCanvas.style.display = 'none';
		core.dom.toolBar.style.display = 'none';
		// 居中画布
		if (core.domStyle.isVertical) {
			core.dom.gameDraw.style.top =
				(parseInt(core.dom.gameGroup.style.height) - parseInt(core.dom.gameDraw.style.height)) / 2 + "px";
		} else {
			core.dom.gameDraw.style.right =
				(parseInt(core.dom.gameGroup.style.width) - parseInt(core.dom.gameDraw.style.width)) / 2 + "px";
		}
		core.dom.gameDraw.style.border = '3px transparent solid';
		core.actions._getClickLoc = function (x, y) {
			var left = core.dom.gameGroup.offsetLeft + core.dom.gameDraw.offsetLeft + 3;
			var top = core.dom.gameGroup.offsetTop + core.dom.gameDraw.offsetTop + 3;
			var loc = { 'x': Math.max(x - left, 0), 'y': Math.max(y - top, 0), 'size': 32 * core.domStyle.scale };
			return loc;
		}
	}

	this._resetTitleCanvas = function () {
		if (!_isTitleCanvasEnabled) return;
		_isTitleCanvasEnabled = false;
		window.onresize = function () { try { main.core.resize(); } catch (e) { main.log(e); } }
		core.resize = function () { return core.control.resize(); }
		core.resize();
		core.actions._getClickLoc = _getClickLoc;
	}

	// 复写“开始游戏”
	core.events._startGame_start = function (hard, seed, route, callback) {
		console.log('开始游戏');
		core.resetGame(core.firstData.hero, hard, null, core.cloneArray(core.initStatus.maps));
		core.setHeroLoc('x', -1);
		core.setHeroLoc('y', -1);

		if (seed != null) {
			core.setFlag('__seed__', seed);
			core.setFlag('__rand__', seed);
		} else core.utils.__init_seed();

		core.clearStatusBar();
		core.plugin._setTitleCanvas();

		var todo = [];
		core.hideStatusBar();
		core.push(todo, core.firstData.startCanvas);
		core.push(todo, { "type": "function", "function": "function() { core.plugin._resetTitleCanvas(); core.events._startGame_setHard(); }" })
		core.push(todo, core.firstData.startText);
		this.insertAction(todo, null, null, function () {
			core.events._startGame_afterStart(callback);
		});

		if (route != null) core.startReplay(route);
	}

	var _loadData = core.control.loadData;
	core.control.loadData = function (data, callback) {
		core.plugin._resetTitleCanvas();
		_loadData.call(core.control, data, callback);
	}
},
    "naughtyMonkey": function () {
	// 淘气的猴子插件 20250327
	// 逻辑：
	// 1. 在游戏开始的时候接受一个input:x作为种子
	// 2. 使用这个种子来猴化这个魔塔。
	// 2a. 列举所有楼层里所有的enemy图块
	// 2b. 对里面出现的怪物子集，每个怪物的hp随机波动
	// 2c. 对每个enemy图块，有一个比例，里面的怪物的hp归为1
	// 
	// api设计：
	// （1）core.introMonkey()
	// 这个函数会core.insertAction 必要的事件，这个事件会寻求用户输入，是否要一个定制化的版本
	// 如果需要，则会给用户输入框。不然默认当前赛季的随机种子
	// 然后使用这个随机种子对魔塔进行猴化调整
	// （2）core.executeMonkey(seed: int)
	// 使用种子猴化。不能猴化两遍，这是未定义行为！
	// （3）core.monkeySubmit()
	// 这个函数检查当前种子是不是属于当前赛季。如果是，则允许提交并core.win 一个自定义的字符串，包含赛季信息
	// 
	// 内部辅助模块
	// （1）伪随机发生器（core.rand不可以，因为涉及到输入种子；可能可以直接调已有轮子）
	// （2）列举并生成正常地图里怪物列表
	// （3）猴化
	// 
	// 接口
	// （1）当前赛季和当前种子
	// （2）需要猴化的层
	// （3）hp随机波动范围
	// （4）hp归1的比例

	// 日期，赛季名，种子

	function newdate(yy, mm, dd, h, m, s) { return new Date(Date.UTC(yy, mm - 1, dd, h, m, s)); }

	// 日期、赛季号码、赛季名称、种子
	var season = [
		[newdate(2025, 3, 27, 0, 0, 0), 1, "测试赛季一", 333],
		[newdate(2025, 3, 30, 0, 0, 0), 2, "测试赛季二", 336],
		[newdate(2025, 3, 31, 12, 0, 0), 101, "第一赛季", 78042777],
		[newdate(2025, 4, 30, 12, 0, 0), 102, "第二赛季", 37539153],
		[newdate(2025, 5, 15, 12, 0, 0), 103, "第三赛季A", 62649607],
		[newdate(2025, 5, 31, 12, 0, 0), 104, "第四赛季", 20387607],
		[newdate(2025, 6, 15, 12, 0, 0), 105, "第五赛季", 92648616],
		[newdate(2025, 6, 28, 12, 0, 0), 105, "第六赛季", 29589132],
		[newdate(2025, 7, 12, 12, 0, 0), 105, "第七赛季", 70331351],
		[newdate(2025, 7, 26, 12, 0, 0), 105, "第八赛季", 10399400],
		[newdate(2025, 8, 9, 12, 0, 0), 105, "第九赛季", 27360651],
		// 以下赛季肯定用不完吧，肯定会在某个赛季更改规则的！
		// 现在有点忙，先不想怎么更改记分规则
		[newdate(2025, 8, 23, 12, 0, 0), 105, "第十赛季", 48035265],
		[newdate(2025, 9, 6, 12, 0, 0), 105, "第十一赛季", 48902739],
		[newdate(2025, 9, 20, 12, 0, 0), 105, "第十二赛季", 85612191],
		[newdate(2025, 10, 4, 12, 0, 0), 105, "第十三赛季", 11650347],
		[newdate(2025, 10, 18, 12, 0, 0), 105, "第十四赛季", 22547077],
		[newdate(2025, 11, 1, 12, 0, 0), 106, "第15赛季", 37040366],
		[newdate(2025, 11, 29, 12, 0, 0), 106, "第16赛季", 41676022],
		[newdate(2025, 12, 27, 12, 0, 0), 106, "第17赛季", 12308009],
		[newdate(2026, 1, 24, 12, 0, 0), 106, "第18赛季", 71684423],
		[newdate(2026, 2, 21, 12, 0, 0), 106, "第19赛季", 62464074],
		[newdate(2026, 3, 21, 12, 0, 0), 106, "第20赛季", 27030041],
		[newdate(2026, 4, 18, 12, 0, 0), 106, "第21赛季", 78009384],
		[newdate(2026, 5, 16, 12, 0, 0), 106, "第22赛季", 34650655],
		[newdate(2026, 6, 13, 12, 0, 0), 106, "第23赛季", 76830242],
		[newdate(2026, 7, 11, 12, 0, 0), 106, "第24赛季", 11105199],
		[newdate(2026, 8, 8, 12, 0, 0), 106, "第25赛季", 20739271],
		[newdate(2026, 9, 5, 12, 0, 0), 106, "第26赛季", 93072983],
		[newdate(2026, 10, 3, 12, 0, 0), 106, "第27赛季", 81295343],
		[newdate(2026, 10, 31, 12, 0, 0), 106, "第28赛季", 63671112],
		[newdate(2026, 11, 28, 12, 0, 0), 106, "第29赛季", 98974187],
		[newdate(2026, 12, 26, 12, 0, 0), 106, "第30赛季", 33127402],
		[newdate(2027, 1, 23, 12, 0, 0), 106, "第31赛季", 34120205],
		[newdate(2027, 2, 20, 12, 0, 0), 106, "第32赛季", 88797296],
		[newdate(2027, 3, 20, 12, 0, 0), 106, "第33赛季", 28530114],
		[newdate(2027, 4, 17, 12, 0, 0), 106, "第34赛季", 37950266],

	];
	// TODO 以下配置可能应当设置为不同赛季可以变换的设置
	var monkey_floors = [];
	for (var i = 1; i <= 39; i++) { monkey_floors.push("D" + i); }

	function get_config(season_number) {
		var hp_range = [0.8, 1.05];
		var hp_prob = 0.5;
		var hp_to1_prob = 0.2;
		if (season_number == 102) {
			hp_prob = 0.5;
			hp_to1_prob = 0.25;
		}
		var handler = 2;
		if (season_number == 101) {
			handler = 1;
		}
		return {
			//"monkey_floors": monkey_floors,
			"hp_range": hp_range,
			"hp_prob": hp_prob,
			"hp_to1_prob": hp_to1_prob,
			"handler": handler
		}
	}

	var common_seed_text = "monkey playground 20250327";

	function get_now_date() {
		return new Date();
	}

	// 确定当前赛季
	this.findSeason = function () {
		// 赛季名，种子
		var now = get_now_date();
		var sea = null,
			nextsea = null;
		for (var i = 0; i < season.length; i++) {
			if (i == season.length - 1 || season[i + 1][0] - now >= 0) {
				sea = season[i];
				//nextsea = season[i == season.length - 1 ? i : i+1];
				break;
			}
		}
		return [sea[2], sea[3], sea[1]];
	}

	function date_to_pretty(date) {
		return date.toLocaleString('zh-cn', { timeZoneName: 'long' });
	}

	this.findSeasonEnd = function () {
		// 赛季结束时间
		var now = get_now_date();
		var sea = null,
			nextsea = null;
		for (var i = 0; i < season.length; i++) {
			if (i == season.length - 1 || season[i + 1][0] - now >= 0) {
				sea = season[i];
				nextsea = season[i == season.length - 1 ? i : i + 1];
				break;
			}
		}
		return date_to_pretty(nextsea[0]);
	}

	this.findNow = function () {
		// 现在时间
		var now = get_now_date();
		return date_to_pretty(now);
	}
	// 实现
	// Define the Murmur3Hash function
	function MurmurHash3(string) {
		let i = 0;
		let hash = 1779033703 ^ string.length;
		for (i = 0; i < string.length; i++) {
			let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
			hash = Math.imul(bitwise_xor_from_character, 3432918353);
			hash = hash << 13 | hash >>> 19;
		}
		return () => {
			// Return the hash that you can use as a seed
			hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
			hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
			return (hash ^= hash >>> 16) >>> 0;
		}
	}

	function SimpleFastCounter32(seed_1, seed_2, seed_3, seed_4) {
		return () => {
			seed_1 >>>= 0;
			seed_2 >>>= 0;
			seed_3 >>>= 0;
			seed_4 >>>= 0;
			let cast32 = (seed_1 + seed_2) | 0;
			seed_1 = seed_2 ^ seed_2 >>> 9;
			seed_2 = seed_3 + (seed_3 << 3) | 0;
			seed_3 = (seed_3 << 21 | seed_3 >>> 11);
			seed_4 = seed_4 + 1 | 0;
			cast32 = cast32 + seed_4 | 0;
			seed_3 = seed_3 + cast32 | 0;
			return (cast32 >>> 0) / 4294967296;
		}
	}

	var common_seed = MurmurHash3(common_seed_text)();

	function PRNG(seed) {
		var res = SimpleFastCounter32(common_seed, seed);
		for (var i = 0; i < 10; i++) {
			res();
		}
		return res;
	}

	//var tttt = PRNG(78042777);
	//for (var i = 0; i < 1000; i++) { console.log(tttt()) }

	function monkey_dig(x, y, floor) {
		core.setEnemyOnPoint(x, y, floor, "hp", 1, null, null, true);
		core.setBlockOpacity(0.6, x, y, floor);
	}

	function get_enemy_list() {
		var enemy_list = [];
		for (var i = 0; i < monkey_floors.length; i++) {
			var floorid = monkey_floors[i];
			var mp = core.status.maps[floorid];
			for (var py = 0; py < mp.height; py++) {
				for (var px = 0; px < mp.width; px++) {
					var blockcls = core.getBlockCls(px, py, floorid);
					if (blockcls != 'enemys') {
						continue;
					}
					enemy_list.push([px, py, floorid, core.getBlockId(px, py, floorid)]);
				}
			}
		}
		var enemy_id_list = [];
		var enemy_been = {};
		for (var i = 0; i < enemy_list.length; i++) {
			var eid = enemy_list[i][3];
			if (eid in enemy_been) {
				continue;
			}
			enemy_been[eid] = 1;
			var einfo = core.getEnemyInfo(eid);
			if (einfo.notBomb) {
				continue;
			}
			enemy_id_list.push(eid);
		}
		var enemy_filtered_list = [];
		for (var i = 0; i < enemy_list.length; i++) {
			var etile = enemy_list[i];
			var eid = etile[3];
			var einfo = core.getEnemyInfo(eid);
			if (einfo.notBomb) {
				continue;
			}
			enemy_filtered_list.push(etile);
		}
		return [enemy_filtered_list, enemy_id_list, enemy_list];
	}

	function monkey_handler1(config, seed) {
		var enemy_res = get_enemy_list();
		var enemy_list = enemy_res[2],
			enemy_id_list = enemy_res[1];
		var rand = PRNG(seed);
		for (var i = 0; i < enemy_id_list.length; i++) {
			var eid = enemy_id_list[i];
			if (rand() < config.hp_prob) {
				var shift = rand() * (config.hp_range[1] - config.hp_range[0]) + config.hp_range[0];
				var hp = core.material.enemys[eid]["hp"];
				hp = Math.ceil(hp * shift);
				core.setEnemy(eid, "hp", hp, null, null, true);
				// log?
			}
		}
		for (var i = 0; i < enemy_list.length; i++) {
			var etile = enemy_list[i];
			var eid = etile[3];
			var einfo = core.getEnemyInfo(eid);
			if (einfo.notBomb) {
				continue;
			}
			if (rand() < config.hp_to1_prob) {
				monkey_dig(etile[0], etile[1], etile[2]);
			}
		}
	}

	function random_sample(l, k, rand) {
		var n = l.length;
		var il = [];
		for (var i = 0; i < n; i++) { il.push(i); }
		var res = [];
		for (var i = 0; i < k; i++) {
			var j = i + Math.floor(rand() * (n - i));
			if (i != j) {
				var t = il[i];
				il[i] = il[j];
				il[j] = t;
			}
			res.push(l[il[i]]);
		}
		return res;
	}

	function monkey_handler2(config, seed) {
		var enemy_res = get_enemy_list();
		var enemy_list = enemy_res[0],
			enemy_id_list = enemy_res[1];
		var rand = PRNG(seed);
		var hp_ids = random_sample(enemy_id_list, Math.floor(enemy_id_list.length * config.hp_prob), rand);
		for (var i = 0; i < hp_ids.length; i++) {
			var eid = hp_ids[i];
			var shift = rand() * (config.hp_range[1] - config.hp_range[0]) + config.hp_range[0];
			var hp = core.material.enemys[eid]["hp"];
			hp = Math.ceil(hp * shift);
			core.setEnemy(eid, "hp", hp, null, null, true);
		}
		var tile_ids = random_sample(enemy_list, Math.floor(enemy_list.length * config.hp_to1_prob), rand);
		for (var i = 0; i < tile_ids.length; i++) {
			var etile = tile_ids[i];
			monkey_dig(etile[0], etile[1], etile[2]);
		}
		var floorcut = ~~core.getFlag('hard_floorcut');
		if (floorcut) {
			var cut_floor = random_sample(monkey_floors, Math.floor(floorcut / 2), rand);
			// there are those floor that is low enough to be of zero damage; we need to be smarter
			// now half of floors are chosen from the first floors to make sure that there is no more easy floors
			for (var i = 0; i < floorcut; i++) {
				if (cut_floor.length >= floorcut) { break; }
				if (!cut_floor.includes(monkey_floors[i])) {
					cut_floor.push(monkey_floors[i]);
				}
			}
			core.setFlag('cut_floor', cut_floor);
			core.setFlag('cut_floor_cut_hp', 30000 * cut_floor.length);
		}
	}

	this.executeMonkey = function (seed, config_id) {
		var config = get_config(config_id);
		if (core.getFlag("monkeybeen")) {
			return;
		} else {
			core.setFlag("monkeybeen", 1);
		}
		if (config.handler == 1) {
			monkey_handler1(config, seed);
		}
		if (config.handler == 2) {
			monkey_handler2(config, seed);
		}
		core.updateStatusBar();
	};

	var intro_monkey_actions = [
		{ "type": "setValue", "name": "flag:season", "value": "core.findSeason()" },
		{ "type": "setValue", "name": "flag:monkeyseed", "value": "flag:season[1]" },
		"\t[赛事主办方]欢乐神猴大赛规则：\n在游戏开始，会有一只神猴随机隔空战斗掉地图上一部分怪物！\n为了避免猴子拿走战利品导致卡关，每个被战斗掉的怪物的hp都变成了1。\n每个赛季猴子战斗掉的怪物都不一样！赛季也会将一部分怪物的hp随机升降，增加变化！",
		{
			"type": "choices",
			"text": "\t[赛事主办方]当前赛季是\r[yellow]${flag:season[0]}\r。\n现在时间：\n${core.findNow()}\n当前赛季将结束于：\n${core.findSeasonEnd()}\n你可以使用当前赛季种子或者自定义种子。",
			"choices": [{
					"text": "参加当前赛季",
					"action": [
						"\t[赛事主办方]好的，欢迎参加本塔的欢乐神猴大赛。",
						{ "type": "insert", "name": "赛季内选项" },
					]
				},
				{
					"text": "自定义种子游玩",
					"action": [
						{ "type": "input", "text": "请输入一个数" },
						{ "type": "setValue", "name": "flag:monkeyseed", "value": "~~flag:input" },
						"\t[赛事主办方]你的种子是${flag:monkeyseed}。你不能在当前赛季提交。",
						{ "type": "insert", "name": "赛季内选项" },
					]
				},
				{
					"text": "试验新赛季规则",
					"action": [
						{ "type": "input", "text": "请输入一个数作为种子" },
						{ "type": "setValue", "name": "flag:monkeyseed", "value": "~~flag:input" },
						"\t[赛事主办方]你的种子是${flag:monkeyseed}。你不能在当前赛季提交。",
						{ "type": "insert", "name": "规则实验室" },
					]
				},


			]
		},
		{ "type": "function", "function": "function(){\ncore.executeMonkey(core.status.hero.flags.monkeyseed,core.status.hero.flags.season[2])\n}" },
	];

	this.introMonkey = function () {
		core.insertAction(intro_monkey_actions);
	}

	var hard_name = {
		"102_1": "青春模式",
		"102_2": "畅玩模式",
		"103_1": "青春模式s3",
		"103_2": "畅玩模式s3",
		"103_3": "完整模式s3",
		"104_1": "青春模式w",
		"104_2": "完整模式w",
		"105_1": "青春模式x",
		"105_2": "完整模式x",
		"106_1": "青春模式y",
		"106_2": "完整模式y",
	};

	this.monkeyEndingName = function () {
		var flags = core.status.hero.flags;
		if (((flags.season.toString() == core.findSeason().toString()) && (flags.season[1] == flags.monkeyseed))) {
			var base = "通关" + core.status.hero.flags.season[0];
			if (flags.hard_season) {
				var name = hard_name[flags.season[2] + "_" + flags.hard_season];
				if (name) {
					base += name;
				}
			}
			return base;
		} else {
			return "";
		}
	}

	var monkey_submit_actions = [{
		"type": "if",
		"condition": "((flag:season.toString()==core.findSeason().toString())&&(flag:season[1]==flag:monkeyseed))",
		"true": [
			{ "type": "function", "function": "function(){\ncore.win(core.monkeyEndingName())\n}" },
		],
		"false": [
			"\t[赛事主办方]你已通关，但是不能提交。\n你可能可以通过在最新的游戏版本上从头播放一遍录像或者进行适当的接续播放录像操作来解决这一问题。\n你的种子是${flag:monkeyseed}。",
			{ "type": "lose", "reason": "自定义种子通关" },
		]
	}, ];

	this.monkeySubmit = function () {
		core.insertAction(monkey_submit_actions);
	}

},
    "crazyElephant": function () {
	// 疯狂的大象插件 20250427
	// 由猴插件调用或者直接外部调用
	// 给定一组楼层，将楼层里的所有怪物和宝物清空（踩扁），加成加到勇者
	// 然后，仅保留走向完好的楼层的传送点；所有踩扁楼层，如果其所有传送点没有走到剩余楼层的，那么这个楼层直接消失
	// 如果踩扁楼层有通向完好楼层的传送点，那么只保留这样的传送点，并把这样的踩扁楼层加入到楼传的visited
	// 逻辑：
	// 1. 随机生成这样的列表
	// 2. 将这些楼层的加成加到勇者身上，并删除相关事件
	// 3. 处理传送点和visited
	// 
	// api设计：
	// （1）core.unleashElephant(floorid_list)
	// 
	// 内部辅助模块
	// （1）
	// 
	// 接口
	// （1）需要大象踩的楼层直接传参即可
	function eat_floor(floorid) {
		var mp = core.status.maps[floorid];
		for (var py = 0; py < mp.height; py++) {
			for (var px = 0; px < mp.width; px++) {
				var blockcls = core.getBlockCls(px, py, floorid);
				var blockid = core.getBlockId(px, py, floorid);
				if (blockcls == 'enemys') {
					var exp = core.getEnemyValue(blockid, "exp", px, py, floorid);
					core.status.hero.exp += exp;
					core.status.hero.statistics.exp += exp;
					core.removeBlock(px, py, floorid)
				}
				if (blockcls == 'items') {
					//core.getItemEffect(blockid, 1);
					//copy the part here without updateStatusBar
					var itemId = blockid;
					var itemCls = core.material.items[itemId].cls;
					// 消耗品
					if (itemCls === 'items') {
						var curr_hp = core.status.hero.hp;
						var itemEffect = core.material.items[itemId].itemEffect;
						if (itemEffect) {
							try {
								eval(itemEffect);
							} catch (e) {
								main.log(e);
							}
						}
						core.status.hero.statistics.hp += core.status.hero.hp - curr_hp;
					} else {
						core.addItem(itemId, 1);
					}
					core.removeBlock(px, py, floorid);
				}
			}
		}
	}

	function play_elephant(floors) {
		if (!floors) return;
		var been = {};
		var current_floor_id = core.status.floorId;
		core.status.floorId = '';
		for (var i = 0; i < floors.length; i++) {
			eat_floor(floors[i]);
			been[floors[i]] = 1;
		}
		core.status.floorId = current_floor_id;
		core.drawMap();
		for (var i = 0; i < floors.length; i++) {
			var floorid = floors[i];
			var mp = core.status.maps[floorid];
			var kept = false;
			for (var py = 0; py < mp.height; py++) {
				for (var px = 0; px < mp.width; px++) {
					var blk = core.getBlock(px, py, floorid);
					if (blk && blk.event && blk.event.trigger == 'changeFloor') {
						if (been[blk.event.data.floorId]) {
							// bye
							core.removeBlock(px, py, floorid);
						} else {
							// keep
							if (!kept) {
								core.visitFloor(floorid);
								core.getFlag("__leaveLoc__")[floorid] = {
									'direction': 'down',
									'x': px,
									'y': py
								};
								kept = true;
							}
						}
					}
				}
			}
			if (!kept && floorid != core.status.floorId) {
				core.removeMaps(floorid);
			}
		}

	}
	//this.eat_floor = eat_floor;
	this.unleashElephant = play_elephant;
},
    "potionShow": function () {
	/* 宝石血瓶左下角显示数值
	 * 需要将 变量：itemDetail改为true才可正常运行
	 * 请尽量减少勇士的属性数量，否则可能会出现严重卡顿（划掉，现在你放一万个属性也不会卡）
	 * 注意：这里的属性必须是core.status.hero里面的，flag无法显示
	 * 如果不想显示，可以core.setFlag("itemDetail", false);
	 * 然后再core.getItemDetail();
	 * 如有bug在大群或造塔群@古祠
	 */

	const ignore = ['superPotion'];
	const origin = core.control.updateStatusBar;
	core.updateStatusBar = core.control.updateStatusBar = function () {
		if (core.getFlag('__statistics__')) return;
		else return origin.apply(core.control, arguments);
	}

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

		// 没有怪物手册
		if (!core.hasItem('book')) return;
		core.status.damage.posX = core.bigmap.posX;
		core.status.damage.posY = core.bigmap.posY;
		if (!onMap) {
			const width = core.floors[floorId].width,
				height = core.floors[floorId].height;
			// 地图过大的缩略图不绘制显伤
			if (width * height > core.bigmap.threshold) return;
		}
		this._updateDamage_damage(floorId, onMap);
		this._updateDamage_extraDamage(floorId, onMap);
		core.getItemDetail(floorId); // 宝石血瓶详细信息
		this.drawDamage(ctx);
	};
	// 获取宝石信息 并绘制
	this.getItemDetail = function (floorId) {
		//if (!core.getFlag('itemDetail')) return;
		floorId = floorId || core.status.thisMap.floorId;
		let diff = {};
		const before = core.status.hero;
		const hero = core.clone(core.status.hero);
		const handler = {
			set: function (target, key, v) {
				diff[key] = v - (target[key] || 0);
				if (!diff[key]) diff[key] = void 0;
				return true;
			}
		};
		core.status.hero = new Proxy(hero, handler);
		core.status.maps[floorId].blocks.forEach(function (block) {
			if (block.event && block.event.trigger == 'changeFloor') {
				var dest = block.event.data.floorId;
				const px = 32 * block.x + 2,
					py = 32 * block.y + 30;
				core.status.damage.data.push({
					text: core.status.maps[dest].title,
					px: px,
					py: py,
					color: '#ffffbf'
				});
				core.status.damage.data.push({
					text: 'Go to',
					px: px,
					py: py - 10,
					color: '#ffffbf'
				});

			}
			if (
				block.event.cls !== 'items' ||
				ignore.includes(block.event.id) ||
				block.disable
			)
				return;
			const x = block.x,
				y = block.y;
			// v2优化，只绘制范围内的部分
			if (core.bigmap.v2) {
				if (
					x < core.bigmap.posX - core.bigmap.extend ||
					x > core.bigmap.posX + core._SIZE_ + core.bigmap.extend ||
					y < core.bigmap.posY - core.bigmap.extend ||
					y > core.bigmap.posY + core._SIZE_ + core.bigmap.extend
				) {
					return;
				}
			}
			diff = {};
			const id = block.event.id;
			const item = core.material.items[id];
			if (item.cls === 'equips') {
				// 装备也显示
				const diff = item.equip.value || {};
				const per = item.equip.percentage || {};
				for (const name in per) {
					diff[name + 'per'] = per[name].toString() + '%';
				}
				drawItemDetail(diff, x, y);
				return;
			}
			// 跟数据统计原理一样 执行效果 前后比较
			core.setFlag('__statistics__', true);
			try {
				eval(item.itemEffect);
			} catch (error) {}
			drawItemDetail(diff, x, y);
		});
		core.status.hero = before;
		window.hero = before;
		window.flags = before.flags;
	};

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

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

	var mode = 3; // 3 is on website
	var domain = null; //"https://h5mota.com";
	var tower_name = null;

	var currentList = []; // 用于存储当前缓存的列表
	var use_remote = false;
	var impersonate = true;

	if (mode == 0) {
		// local
	} else if (mode == 1) {
		// local online
		domain = "https://h5mota.com";
		use_remote = true;
	} else if (mode == 2) {
		// remote online but impersonate
		use_remote = true;
	} else if (mode == 3) {
		// remote online
		use_remote = true;
		impersonate = false;
	}


	function getListUrl(domain, tower_name, i) {
		if (use_remote) {
			return domain + "/backend/submission/list.php?name=" + tower_name + "&page=" + i;
		} else {
			return "/__backend/" + i + ".json";
		}
		// 
	}

	function getUid() {
		if (!impersonate) {
			return parseInt(core.getCookie('id'));
		} else {
			return 1270;
		}
		// 
	}
	this.getScoreboardUid = getUid;

	function isValidSubmission(record) {
		if (record.verify != -1) {
			// 不是绿录像
			return false;
		}
		if (record.user.id <= 0) {
			// 黑名提交
			return false;
		}
		var tm = record.user.template;
		if (tm.includes('*') || tm.includes('不计')) {
			// 不计
			return false;
		}
		return true;
	}

	function getList() {
		if (main.replayChecking) return;
		var cur_domain = domain || "";
		var cur_tower_name = tower_name || core.data.firstData.name;

		function ith_page_url(i) {
			return getListUrl(cur_domain, cur_tower_name, i);
		}

		fetch(ith_page_url(1))
			.then(res => res.json())
			.then(json => {
				var list = json.list || []; // 确保 list 是一个数组
				var total = json.total || 0; // 确保 total 是一个数字
				var reqlist = [];
				var length = Math.ceil(total / 50);
				for (var i = 2; i <= length; i++) {
					reqlist.push(fetch(ith_page_url(i)).then(res => res.json()));
				}
				return Promise.all(reqlist).then(responses => {
					// 按顺序合并所有分页的数据
					currentList = responses.reduce((acc, pageData) => {
						return acc.concat(pageData.list || []).filter(isValidSubmission);
					}, list);
				});
			})
			.catch(error => {
				console.error('Error fetching data:', error);
			});
	}

	function init() {
		// 初始化时立即调用getList
		getList();

		// 每10分钟调用一次getList
		if (!main.replayChecking) {
			setInterval(getList, 10 * 60 * 1000);
		}

		// 返回一个函数，这个函数返回当前缓存的最新list
		var f = function () {
			return currentList;
		};

		return f;
	}

	// 使用初始化器获取新函数
	this.updateRawScoreList = getList;
	this.getRawScoreList = init();

},
    "calcScoreboard": function () {
	// 记分版的数据逻辑和数据
	// 希希传说欢乐大赛记分逻辑
	// 赛季
	// 记分形式
	// 赛道
	// 
	// season_list: list[season]
	// season: {
	// 		name: "第一赛季",
	// 		begin_time: time,
	// 		end_time: time,
	// 		track: list[track],
	// 		scorer: list[scorer]}
	// track: {name: "首周奖励，青春版", end_time: time, ending_name: "结局名"}
	// scorer: {name: "首周奖励", formula: function(track_value)->score}
	// 

	if (main.replayChecking) return;

	function newdate(yy, mm, dd, h, m, s) { return new Date(Date.UTC(yy, mm - 1, dd, h, m, s)); }

	var competition_list = [{
			"name": "第一赛季",
			"begin_time": newdate(2025, 3, 31, 12, 0, 0),
			"end_time": newdate(2025, 4, 30, 12, 0, 0),
			"track": [
				{ "name": "首日榜", "end_time": newdate(2025, 4, 1, 12, 0, 0), "ending_name": "通关第一赛季" },
				{ "name": "首周榜", "end_time": newdate(2025, 4, 6, 12, 0, 0), "ending_name": "通关第一赛季" },
				{ "name": "月中榜", "end_time": newdate(2025, 4, 15, 12, 0, 0), "ending_name": "通关第一赛季" },
				{ "name": "月末榜", "end_time": newdate(2025, 4, 30, 12, 0, 0), "ending_name": "通关第一赛季" }
			],
			"scorer": [
				{ "name": "首日榜一积分", "formula": "function(ranks){ return firstonly(ranks[0], 50); }" },
				{ "name": "首周全榜积分", "formula": "function(ranks){ return everysat(ranks[1], 1); }" },
				{ "name": "月中榜一积分", "formula": "function(ranks){ return firstonly(ranks[2], 50); }" },
				{ "name": "月末全榜积分", "formula": "function(ranks){ return everysat(ranks[3], 1); }" }
			]
		},
		{
			"name": "第二赛季",
			"begin_time": newdate(2025, 4, 30, 12, 0, 0),
			"end_time": newdate(2025, 5, 15, 12, 0, 0),
			"track": [
				{ "name": "青春模式首日榜", "end_time": newdate(2025, 5, 1, 12, 0, 0), "ending_name": "通关第二赛季青春模式" },
				{ "name": "畅玩模式首日榜", "end_time": newdate(2025, 5, 1, 12, 0, 0), "ending_name": "通关第二赛季畅玩模式" },
				{ "name": "青春模式次周榜", "end_time": newdate(2025, 5, 5, 12, 0, 0), "ending_name": "通关第二赛季青春模式" },
				{ "name": "畅玩模式次周榜", "end_time": newdate(2025, 5, 5, 12, 0, 0), "ending_name": "通关第二赛季畅玩模式" },
				{ "name": "青春模式最终榜", "end_time": newdate(2025, 5, 15, 12, 0, 0), "ending_name": "通关第二赛季青春模式" },
				{ "name": "畅玩模式最终榜", "end_time": newdate(2025, 5, 15, 12, 0, 0), "ending_name": "通关第二赛季畅玩模式" }
			],
			"scorer": [
				{ "name": "首日榜一积分", "formula": "function(ranks){ return firstonly(max(...ranks.slice(0,2)), 50); }" },
				{ "name": "次周全榜积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }" },
				{ "name": "最终全榜积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(4,6)), 1); }" }
			]
		},
		{
			"name": "第三赛季",
			"begin_time": newdate(2025, 5, 15, 12, 0, 0),
			"end_time": newdate(2025, 5, 31, 12, 0, 0),
			"track": [
				{ "name": "青春模式首日榜", "end_time": newdate(2025, 5, 16, 12, 0, 0), "ending_name": "通关第三赛季A青春模式s3" },
				{ "name": "畅玩模式首日榜", "end_time": newdate(2025, 5, 16, 12, 0, 0), "ending_name": "通关第三赛季A畅玩模式s3" },
				{ "name": "完整模式首日榜", "end_time": newdate(2025, 5, 16, 12, 0, 0), "ending_name": "通关第三赛季A完整模式s3" },
				{ "name": "青春模式最终榜", "end_time": newdate(2025, 5, 31, 12, 0, 0), "ending_name": "通关第三赛季A青春模式s3" },
				{ "name": "畅玩模式最终榜", "end_time": newdate(2025, 5, 31, 12, 0, 0), "ending_name": "通关第三赛季A畅玩模式s3" },
				{ "name": "完整模式最终榜", "end_time": newdate(2025, 5, 31, 12, 0, 0), "ending_name": "通关第三赛季A完整模式s3" }
			],
			"scorer": [
				{ "name": "首日积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(0,3)), 1); }" },
				{ "name": "最终积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(3,6)), 1); }" }
			]
		},
		{
			"name": "第四赛季",
			"begin_time": newdate(2025, 5, 31, 12, 0, 0),
			"end_time": newdate(2025, 6, 15, 12, 0, 0),
			"track": [
				{ "name": "青春模式首日榜", "end_time": newdate(2025, 6, 1, 12, 0, 0), "ending_name": "通关第四赛季青春模式w" },
				{ "name": "完整模式首日榜", "end_time": newdate(2025, 6, 1, 12, 0, 0), "ending_name": "通关第四赛季" },
				{ "name": "青春模式最终榜", "end_time": newdate(2025, 6, 15, 12, 0, 0), "ending_name": "通关第四赛季青春模式w" },
				{ "name": "完整模式最终榜", "end_time": newdate(2025, 6, 15, 12, 0, 0), "ending_name": "通关第四赛季" }
			],
			"scorer": [
				{ "name": "首日积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }" },
				{ "name": "最终积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }" }
			]
		},
		{
			"name": "第五赛季",
			"begin_time": newdate(2025, 6, 15, 12, 0, 0),
			"end_time": newdate(2025, 6, 28, 12, 0, 0),
			"track": [
				{ "name": "青春模式首日榜", "end_time": newdate(2025, 6, 16, 12, 0, 0), "ending_name": "通关第五赛季青春模式x" },
				{ "name": "完整模式首日榜", "end_time": newdate(2025, 6, 16, 12, 0, 0), "ending_name": "通关第五赛季完整模式x" },
				{ "name": "青春模式最终榜", "end_time": newdate(2025, 6, 28, 12, 0, 0), "ending_name": "通关第五赛季青春模式x" },
				{ "name": "完整模式最终榜", "end_time": newdate(2025, 6, 28, 12, 0, 0), "ending_name": "通关第五赛季完整模式x" }
			],
			"scorer": [
				{ "name": "首日积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }" },
				{ "name": "最终积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }" }
			]
		},
		{
			"name": "第六赛季",
			"begin_time": newdate(2025, 6, 28, 12, 0, 0),
			"end_time": newdate(2025, 7, 12, 12, 0, 0),
			"track": [
				{ "name": "青春模式首日榜", "end_time": newdate(2025, 6, 29, 12, 0, 0), "ending_name": "通关第六赛季青春模式x" },
				{ "name": "完整模式首日榜", "end_time": newdate(2025, 6, 29, 12, 0, 0), "ending_name": "通关第六赛季完整模式x" },
				{ "name": "青春模式最终榜", "end_time": newdate(2025, 7, 12, 12, 0, 0), "ending_name": "通关第六赛季青春模式x" },
				{ "name": "完整模式最终榜", "end_time": newdate(2025, 7, 12, 12, 0, 0), "ending_name": "通关第六赛季完整模式x" }
			],
			"scorer": [
				{ "name": "首日积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }" },
				{ "name": "最终积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }" }
			]
		},
		{
			"name": "第七赛季",
			"begin_time": newdate(2025, 7, 12, 12, 0, 0),
			"end_time": newdate(2025, 7, 26, 12, 0, 0),
			"track": [
				{ "name": "青春模式首日榜", "end_time": newdate(2025, 7, 13, 12, 0, 0), "ending_name": "通关第七赛季青春模式x" },
				{ "name": "完整模式首日榜", "end_time": newdate(2025, 7, 13, 12, 0, 0), "ending_name": "通关第七赛季完整模式x" },
				{ "name": "青春模式最终榜", "end_time": newdate(2025, 7, 26, 12, 0, 0), "ending_name": "通关第七赛季青春模式x" },
				{ "name": "完整模式最终榜", "end_time": newdate(2025, 7, 26, 12, 0, 0), "ending_name": "通关第七赛季完整模式x" }
			],
			"scorer": [
				{ "name": "首日积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }" },
				{ "name": "最终积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }" }
			]
		},
		{
			"name": "第八赛季",
			"begin_time": newdate(2025, 7, 26, 12, 0, 0),
			"end_time": newdate(2025, 8, 9, 12, 0, 0),
			"track": [
				{ "name": "青春模式首日榜", "end_time": newdate(2025, 7, 27, 12, 0, 0), "ending_name": "通关第八赛季青春模式x" },
				{ "name": "完整模式首日榜", "end_time": newdate(2025, 7, 27, 12, 0, 0), "ending_name": "通关第八赛季完整模式x" },
				{ "name": "青春模式最终榜", "end_time": newdate(2025, 8, 9, 12, 0, 0), "ending_name": "通关第八赛季青春模式x" },
				{ "name": "完整模式最终榜", "end_time": newdate(2025, 8, 9, 12, 0, 0), "ending_name": "通关第八赛季完整模式x" }
			],
			"scorer": [
				{ "name": "首日积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }" },
				{ "name": "最终积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }" }
			]
		},
		{
			"name": "第九赛季",
			"begin_time": newdate(2025, 8, 9, 12, 0, 0),
			"end_time": newdate(2025, 8, 23, 12, 0, 0),
			"track": [
				{ "name": "青春模式最终榜", "end_time": newdate(2025, 8, 23, 12, 0, 0), "ending_name": "通关第九赛季青春模式x" },
				{ "name": "完整模式最终榜", "end_time": newdate(2025, 8, 23, 12, 0, 0), "ending_name": "通关第九赛季完整模式x" }
			],
			"scorer": [
				{ "name": "最终积分", "formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 2); }" }
			]
		},

		{
			"name": "第十赛季",
			"begin_time": newdate(2025, 8, 23, 12, 0, 0),
			"end_time": newdate(2025, 9, 6, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2025, 8, 24, 12, 0, 0),
					"ending_name": "通关第十赛季青春模式x"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2025, 8, 24, 12, 0, 0),
					"ending_name": "通关第十赛季完整模式x"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2025, 9, 6, 12, 0, 0),
					"ending_name": "通关第十赛季青春模式x"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2025, 9, 6, 12, 0, 0),
					"ending_name": "通关第十赛季完整模式x"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第十一赛季",
			"begin_time": newdate(2025, 9, 6, 12, 0, 0),
			"end_time": newdate(2025, 9, 20, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2025, 9, 7, 12, 0, 0),
					"ending_name": "通关第十一赛季青春模式x"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2025, 9, 7, 12, 0, 0),
					"ending_name": "通关第十一赛季完整模式x"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2025, 9, 20, 12, 0, 0),
					"ending_name": "通关第十一赛季青春模式x"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2025, 9, 20, 12, 0, 0),
					"ending_name": "通关第十一赛季完整模式x"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第十二赛季",
			"begin_time": newdate(2025, 9, 20, 12, 0, 0),
			"end_time": newdate(2025, 10, 4, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2025, 9, 21, 12, 0, 0),
					"ending_name": "通关第十二赛季青春模式x"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2025, 9, 21, 12, 0, 0),
					"ending_name": "通关第十二赛季完整模式x"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2025, 10, 4, 12, 0, 0),
					"ending_name": "通关第十二赛季青春模式x"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2025, 10, 4, 12, 0, 0),
					"ending_name": "通关第十二赛季完整模式x"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第十三赛季",
			"begin_time": newdate(2025, 10, 4, 12, 0, 0),
			"end_time": newdate(2025, 10, 18, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2025, 10, 5, 12, 0, 0),
					"ending_name": "通关第十三赛季青春模式x"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2025, 10, 5, 12, 0, 0),
					"ending_name": "通关第十三赛季完整模式x"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2025, 10, 18, 12, 0, 0),
					"ending_name": "通关第十三赛季青春模式x"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2025, 10, 18, 12, 0, 0),
					"ending_name": "通关第十三赛季完整模式x"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第十四赛季",
			"begin_time": newdate(2025, 10, 18, 12, 0, 0),
			"end_time": newdate(2025, 11, 1, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2025, 10, 19, 12, 0, 0),
					"ending_name": "通关第十四赛季青春模式x"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2025, 10, 19, 12, 0, 0),
					"ending_name": "通关第十四赛季完整模式x"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2025, 11, 1, 12, 0, 0),
					"ending_name": "通关第十四赛季青春模式x"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2025, 11, 1, 12, 0, 0),
					"ending_name": "通关第十四赛季完整模式x"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第15赛季",
			"begin_time": newdate(2025, 11, 1, 12, 0, 0),
			"end_time": newdate(2025, 11, 29, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2025, 11, 2, 12, 0, 0),
					"ending_name": "通关第15赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2025, 11, 2, 12, 0, 0),
					"ending_name": "通关第15赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2025, 11, 29, 12, 0, 0),
					"ending_name": "通关第15赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2025, 11, 29, 12, 0, 0),
					"ending_name": "通关第15赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第16赛季",
			"begin_time": newdate(2025, 11, 29, 12, 0, 0),
			"end_time": newdate(2025, 12, 27, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2025, 11, 30, 12, 0, 0),
					"ending_name": "通关第16赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2025, 11, 30, 12, 0, 0),
					"ending_name": "通关第16赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2025, 12, 27, 12, 0, 0),
					"ending_name": "通关第16赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2025, 12, 27, 12, 0, 0),
					"ending_name": "通关第16赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第17赛季",
			"begin_time": newdate(2025, 12, 27, 12, 0, 0),
			"end_time": newdate(2026, 1, 24, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2025, 12, 28, 12, 0, 0),
					"ending_name": "通关第17赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2025, 12, 28, 12, 0, 0),
					"ending_name": "通关第17赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 1, 24, 12, 0, 0),
					"ending_name": "通关第17赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 1, 24, 12, 0, 0),
					"ending_name": "通关第17赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第18赛季",
			"begin_time": newdate(2026, 1, 24, 12, 0, 0),
			"end_time": newdate(2026, 2, 21, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 1, 25, 12, 0, 0),
					"ending_name": "通关第18赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 1, 25, 12, 0, 0),
					"ending_name": "通关第18赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 2, 21, 12, 0, 0),
					"ending_name": "通关第18赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 2, 21, 12, 0, 0),
					"ending_name": "通关第18赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第19赛季",
			"begin_time": newdate(2026, 2, 21, 12, 0, 0),
			"end_time": newdate(2026, 3, 21, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 2, 22, 12, 0, 0),
					"ending_name": "通关第19赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 2, 22, 12, 0, 0),
					"ending_name": "通关第19赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 3, 21, 12, 0, 0),
					"ending_name": "通关第19赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 3, 21, 12, 0, 0),
					"ending_name": "通关第19赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第20赛季",
			"begin_time": newdate(2026, 3, 21, 12, 0, 0),
			"end_time": newdate(2026, 4, 18, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 3, 22, 12, 0, 0),
					"ending_name": "通关第20赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 3, 22, 12, 0, 0),
					"ending_name": "通关第20赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 4, 18, 12, 0, 0),
					"ending_name": "通关第20赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 4, 18, 12, 0, 0),
					"ending_name": "通关第20赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第21赛季",
			"begin_time": newdate(2026, 4, 18, 12, 0, 0),
			"end_time": newdate(2026, 5, 16, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 4, 19, 12, 0, 0),
					"ending_name": "通关第21赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 4, 19, 12, 0, 0),
					"ending_name": "通关第21赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 5, 16, 12, 0, 0),
					"ending_name": "通关第21赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 5, 16, 12, 0, 0),
					"ending_name": "通关第21赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第22赛季",
			"begin_time": newdate(2026, 5, 16, 12, 0, 0),
			"end_time": newdate(2026, 6, 13, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 5, 17, 12, 0, 0),
					"ending_name": "通关第22赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 5, 17, 12, 0, 0),
					"ending_name": "通关第22赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 6, 13, 12, 0, 0),
					"ending_name": "通关第22赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 6, 13, 12, 0, 0),
					"ending_name": "通关第22赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第23赛季",
			"begin_time": newdate(2026, 6, 13, 12, 0, 0),
			"end_time": newdate(2026, 7, 11, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 6, 14, 12, 0, 0),
					"ending_name": "通关第23赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 6, 14, 12, 0, 0),
					"ending_name": "通关第23赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 7, 11, 12, 0, 0),
					"ending_name": "通关第23赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 7, 11, 12, 0, 0),
					"ending_name": "通关第23赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第24赛季",
			"begin_time": newdate(2026, 7, 11, 12, 0, 0),
			"end_time": newdate(2026, 8, 8, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 7, 12, 12, 0, 0),
					"ending_name": "通关第24赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 7, 12, 12, 0, 0),
					"ending_name": "通关第24赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 8, 8, 12, 0, 0),
					"ending_name": "通关第24赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 8, 8, 12, 0, 0),
					"ending_name": "通关第24赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第25赛季",
			"begin_time": newdate(2026, 8, 8, 12, 0, 0),
			"end_time": newdate(2026, 9, 5, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 8, 9, 12, 0, 0),
					"ending_name": "通关第25赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 8, 9, 12, 0, 0),
					"ending_name": "通关第25赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 9, 5, 12, 0, 0),
					"ending_name": "通关第25赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 9, 5, 12, 0, 0),
					"ending_name": "通关第25赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第26赛季",
			"begin_time": newdate(2026, 9, 5, 12, 0, 0),
			"end_time": newdate(2026, 10, 3, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 9, 6, 12, 0, 0),
					"ending_name": "通关第26赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 9, 6, 12, 0, 0),
					"ending_name": "通关第26赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 10, 3, 12, 0, 0),
					"ending_name": "通关第26赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 10, 3, 12, 0, 0),
					"ending_name": "通关第26赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第27赛季",
			"begin_time": newdate(2026, 10, 3, 12, 0, 0),
			"end_time": newdate(2026, 10, 31, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 10, 4, 12, 0, 0),
					"ending_name": "通关第27赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 10, 4, 12, 0, 0),
					"ending_name": "通关第27赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 10, 31, 12, 0, 0),
					"ending_name": "通关第27赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 10, 31, 12, 0, 0),
					"ending_name": "通关第27赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第28赛季",
			"begin_time": newdate(2026, 10, 31, 12, 0, 0),
			"end_time": newdate(2026, 11, 28, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 11, 1, 12, 0, 0),
					"ending_name": "通关第28赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 11, 1, 12, 0, 0),
					"ending_name": "通关第28赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 11, 28, 12, 0, 0),
					"ending_name": "通关第28赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 11, 28, 12, 0, 0),
					"ending_name": "通关第28赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第29赛季",
			"begin_time": newdate(2026, 11, 28, 12, 0, 0),
			"end_time": newdate(2026, 12, 26, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 11, 29, 12, 0, 0),
					"ending_name": "通关第29赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 11, 29, 12, 0, 0),
					"ending_name": "通关第29赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2026, 12, 26, 12, 0, 0),
					"ending_name": "通关第29赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2026, 12, 26, 12, 0, 0),
					"ending_name": "通关第29赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第30赛季",
			"begin_time": newdate(2026, 12, 26, 12, 0, 0),
			"end_time": newdate(2027, 1, 23, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2026, 12, 27, 12, 0, 0),
					"ending_name": "通关第30赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2026, 12, 27, 12, 0, 0),
					"ending_name": "通关第30赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2027, 1, 23, 12, 0, 0),
					"ending_name": "通关第30赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2027, 1, 23, 12, 0, 0),
					"ending_name": "通关第30赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第31赛季",
			"begin_time": newdate(2027, 1, 23, 12, 0, 0),
			"end_time": newdate(2027, 2, 20, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2027, 1, 24, 12, 0, 0),
					"ending_name": "通关第31赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2027, 1, 24, 12, 0, 0),
					"ending_name": "通关第31赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2027, 2, 20, 12, 0, 0),
					"ending_name": "通关第31赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2027, 2, 20, 12, 0, 0),
					"ending_name": "通关第31赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		},
		{
			"name": "第32赛季",
			"begin_time": newdate(2027, 2, 20, 12, 0, 0),
			"end_time": newdate(2027, 3, 20, 12, 0, 0),
			"track": [{
					"name": "青春模式首日榜",
					"end_time": newdate(2027, 2, 21, 12, 0, 0),
					"ending_name": "通关第32赛季青春模式y"
				},
				{
					"name": "完整模式首日榜",
					"end_time": newdate(2027, 2, 21, 12, 0, 0),
					"ending_name": "通关第32赛季完整模式y"
				},
				{
					"name": "青春模式最终榜",
					"end_time": newdate(2027, 3, 20, 12, 0, 0),
					"ending_name": "通关第32赛季青春模式y"
				},
				{
					"name": "完整模式最终榜",
					"end_time": newdate(2027, 3, 20, 12, 0, 0),
					"ending_name": "通关第32赛季完整模式y"
				}
			],
			"scorer": [{
					"name": "首日积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(0,2)), 1); }"
				},
				{
					"name": "最终积分",
					"formula": "function(ranks){ return everysat(max(...ranks.slice(2,4)), 1); }"
				}
			]
		}



	];



	const usable_funcs = {
		min: Math.min,
		max: Math.max,
		firstonly: function (rank, score) { return rank == 0 ? score : 0 },
		everysat: function (rank, multiple) {
			return rank >= 0 ? Math.floor(multiple * (10 + Math.max(1, Math.floor(100 / (rank + 1))))) : 0;
		}
	}


	function generate_scorer() {
		const usable_funcs = {
			min: Math.min,
			max: Math.max,
			firstonly: (rank, score) => rank === 0 ? score : 0,
			everysat: (rank, multiple) =>
				rank >= 0 ? Math.floor(multiple * (10 + Math.max(1, Math.floor(100 / (rank + 1))))) : 0
		};

		// 1. 按赛季/赛道收集提交
		function splitBySeason(rawSubs, competition_list) {
			const seasonMap = {};
			//competition_list.forEach((s, si) => {
			//});

			rawSubs.forEach(sub => {
				const subDate = new Date(sub.timestamp * 1000);
				competition_list.forEach((s, si) => {
					if (subDate < s.begin_time || subDate > s.end_time) return;
					s.track.forEach((t, ti) => {
						if (sub.ending === t.ending_name && subDate <= t.end_time) {
							if (!seasonMap[si]) {
								seasonMap[si] = {
									scorer: s.scorer,
									tracks: s.track.map((t, ti) => ({ ti, subs: [] }))
								};
							}
							seasonMap[si].tracks[ti].subs.push(sub);
						}
					});
				});
			});
			return seasonMap;
		}


		// 2. 单赛道排名
		function rankOneTrack(subs) {
			const best = {}; // uid -> {score, time, sub}
			subs.forEach(sub => {
				const uid = sub.user.id;
				const cur = best[uid];
				if (!cur ||
					sub.score > cur.score ||
					(sub.score === cur.score && sub.timestamp < cur.timestamp)) {
					best[uid] = { score: sub.score, time: sub.timestamp, sub };
				}
			});

			const list = Object.keys(best).map(uid => best[uid]);
			list.sort((a, b) => b.score - a.score || a.time - b.time);

			const uid2rank = {};
			list.forEach((it, idx) => {
				const rank = idx > 0 && it.score === list[idx - 1].score ?
					uid2rank[list[idx - 1].sub.user.id] :
					idx;
				uid2rank[it.sub.user.id] = rank;
			});

			return list.map(it => ({
				uid: it.sub.user.id,
				score: it.score,
				time: it.time,
				rank: uid2rank[it.sub.user.id]
			}));
		}

		// 3. 计算赛季得分
		function calcSeasonScore(season, trackRanks) {
			const allUids = new Set();
			trackRanks.forEach(tr => tr.forEach(r => allUids.add(r.uid)));

			const res = {};
			allUids.forEach(uid => {
				const ranks = trackRanks.map(tr => {
					const r = tr.find(o => o.uid === uid);
					return r ? r.rank : -1;
				});

				const scorerScores = [];
				const { min, max, firstonly, everysat } = usable_funcs;
				season.scorer.forEach(sc => {
					const fn = eval('(' + sc.formula + ')');
					scorerScores.push(fn(ranks));
				});

				const total = scorerScores.reduce((a, b) => a + b, 0);
				res[uid] = { scorerScores, total };
			});
			return res;
		}

		// 4. 主入口
		return function buildLeaderboard(rawSubs, competition_list) {
			const seasonMap = splitBySeason(rawSubs, competition_list);
			const seasons = [];
			const uidTotal = {};
			const uidName = {};

			rawSubs.forEach((subm, si) => {
				uidName[subm.user.id] = subm.user.name;
			});

			competition_list.forEach((sDef, si) => {
				const s = seasonMap[si];
				if (!s) { return; }
				const trackRanks = s.tracks.map(t => rankOneTrack(t.subs));
				const uidScore = calcSeasonScore(s, trackRanks);

				Object.keys(uidScore).forEach(uid => {
					uidTotal[uid] = (uidTotal[uid] || 0) + uidScore[uid].total;
				});

				seasons.push({
					tracks: trackRanks,
					uidScore
				});
			});

			return { competitions: competition_list, seasons, uidTotal, uidName };
		};
	}

	function sort_and_fill_rank(table, {
		scoreIdx = 3,
		tieIdx = 1,
		rankIdx = 0,
		desc = true
	} = {}) {
		if (!Array.isArray(table)) return table;

		// 1. 排序
		const factor = desc ? -1 : 1;
		table.sort((a, b) => {
			const d = factor * (a[scoreIdx] - b[scoreIdx]);
			return d !== 0 ? d : a[tieIdx] - b[tieIdx];
		});

		// 2. 填充排名
		let rank = 1;
		for (let i = 0; i < table.length; i++) {
			if (i > 0 && table[i][scoreIdx] !== table[i - 1][scoreIdx]) {
				rank = i + 1; // 分数变化时，排名等于当前索引+1
			}
			table[i][rankIdx] = rank;
		}
		return table;
	}


	var buildLeaderboard = generate_scorer();

	this.generateScoreboardInfo = function () {
		var submissions = core.getRawScoreList();
		return buildLeaderboard(submissions, competition_list);
	}

	function calcTotalRank(info) {
		var total_body_table = [];
		Object.keys(info.uidTotal).forEach(uid => {
			if (info.uidTotal[uid] > 0) {
				total_body_table.push([0, uid, info.uidName[uid], info.uidTotal[uid]]);
			}
		});

		// 按第 3 列积分降序排，同分按第 1 列 uid 升序，排名写入第 0 列
		sort_and_fill_rank(total_body_table, { scoreIdx: 3, tieIdx: 1, rankIdx: 0 });
		return total_body_table;
	}

	this.getScoreboard = function () {
		var info = this.generateScoreboardInfo();

		var tables = [];


		var title_table = ["#", "id", "玩家名", "积分"];
		var total_table = [title_table].concat(calcTotalRank(info));


		tables.push([
			["总积分榜"], total_table
		]);

		info.competitions.forEach((competition, si) => {
			// TODO check whether it is in the future
			// skip if future
			if (!info.seasons[si]) return;
			// TODO sort seasons and recent first
			var season_name = competition.name;
			competition.track.forEach((track, ti) => {
				var track_name = track.name;
				var path = ["分榜", season_name, track_name];
				var orig_table = info.seasons[si].tracks[ti];
				var body_table = orig_table.map((x, i) => {
					return [x.rank + 1, x.uid, info.uidName[x.uid], x.score,
						new Date(1000 * x.time).toLocaleString('zh-cn')
					]
				});
				tables.push([path, [
					["#", "id", "玩家名", "得分", "提交时间"]
				].concat(body_table)])
			});
		});

		return tables;

	}

	function getSelfScoreString() {
		if (!core.getScoreboardUid) { return "点击刷新积分"; }
		var uid = core.getScoreboardUid && core.getScoreboardUid();
		if (!uid) { return "未登陆"; }
		var info = core.generateScoreboardInfo(); // TODO is it costly
		var table = calcTotalRank(info);
		if (!table || !table.length) { return "点击加载榜单"; }
		for (var i = 0; i < table.length; i++) {
			if (table[i][1] == uid) {
				return '🎯 ' + table[i][3] + '|🏆 #' + table[i][0];
			}
		}
		return "暂无积分";
	}

	// 榜单的准确显示

	/* ===== 全局样式字符串 ===== */
	const GLOBAL_STYLE_TEXT = `
.scoreboard-btn{
    display:flex;
    align-items:center;
    justify-content:center;
background:linear-gradient(45deg,#38bdf8,#0ea5e9);
                color:#fff;border:none;padding:12px 24px;font-size:14px;border-radius:50px;cursor:pointer;
                box-shadow:0 4px 15px rgba(56,189,248,.4);transition:all .3s ease;
				z-index:261;position: absolute;top: 0%;right: 5%;width: 20%;height: 5%;}
.scoreboard-btn:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(56,189,248,.55);}
.scoreboard-modal{display:none;position:fixed;inset:0;background:rgba(15,23,42,.85);z-index:1000;}
.scoreboard-content{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
                    background:rgba(30,41,59,.75);backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.1);
                    border-radius:16px;padding:32px;width:80%;max-width:800px;max-height:80vh;display:flex;flex-direction:column;}
.scoreboard-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;}
.scoreboard-title{font-size:24px;font-weight:700;color:#fff;}
.scoreboard-close-btn{font-size:28px;color:#94a3b8;cursor:pointer;transition:color .3s;}
.scoreboard-close-btn:hover{color:#fff;}
.selector-area{flex:0 0 auto;margin-bottom:20px;}
.selector-row{display:flex;align-items:center;margin-bottom:12px;}
.selector-label{width:100px;font-weight:600;color:#cbd5e1;}
.selector-combo{flex:1;padding:10px 14px;border:1px solid rgba(255,255,255,.2);border-radius:8px;
                background:rgba(0,0,0,.25);color:#fff;font-size:15px;}
.table-container{flex:1 1 auto;overflow:auto;border-radius:12px;background:rgba(0,0,0,.2);}
.scoreboard-table{width:100%;border-collapse:collapse;}
.scoreboard-table th{position:sticky;top:0;background:rgba(56,189,248,.8);color:#fff;padding:12px;text-align:left;}
.scoreboard-table td{padding:10px 12px;border-bottom:1px solid rgba(255,255,255,.05);color:#e2e8f0;}
.scoreboard-table tr:nth-child(even){background:rgba(255,255,255,.03);}
.scoreboard-table tr:hover{background:rgba(56,189,248,.15);}
.no-data{text-align:center;padding:40px;color:#94a3b8;}
`;
	/* ===== 外部注入的挂载函数 ===== */
	function appendChildToDest(el) {
		core.dom.startPanel.appendChild(el);
	}

	function escapeHtml(str) {
		// editor dislike this literal
		const map = JSON.parse(atob("eyImIjoiJmFtcDsiLCI8IjoiJmx0OyIsIj4iOiImZ3Q7IiwiXCIiOiImcXVvdDsiLCInIjoiJiMzOTsifQ=="));
		// 也可以写成 & a p o s ;，但 & # 3 9 ; 更通用
		return str.toString().replace(/[&<>"']/g, (char) => map[char]);
	}

	/* ===== 主嵌入函数：定义即执行 ===== */
	const initScoreboardWidget = (function () {
		/* 1. 注入样式 */
		const styleEl = document.createElement("style");
		styleEl.textContent = GLOBAL_STYLE_TEXT;
		document.head.appendChild(styleEl);

		/* 2. 创建按钮 */
		const btn = document.createElement("div");
		btn.className = "scoreboard-btn";
		btn.textContent = "🏆 排行";
		appendChildToDest(btn);
		/* 2+. 创建信息 widget */
		const infoWidget = document.createElement("div");
		infoWidget.className = "scoreboard-btn"; // 复用按钮样式
		infoWidget.style.right = "auto"; // 去掉右侧定位
		infoWidget.style.left = "5%"; // 水平对称
		infoWidget.style.cursor = "pointer";
		infoWidget.title = "点击刷新";
		/* 初始填充 */
		function refreshInfo() {
			var content = getSelfScoreString();
			if (content.includes("|")) {
				const [left, right] = getSelfScoreString().split('|'); // 按你实际的分隔符拆

				infoWidget.innerHTML = `
    <span>${escapeHtml(left)}</span>
    <span style="flex:1"></span>
    <span>${escapeHtml(right)}</span>
  `;
				// 弹性空白，把左右撑开

			} else {
				infoWidget.innerHTML = escapeHtml(content);
			}
			if (content.includes("点击")) {
				setTimeout(refreshInfo, 500);
			}
		}
		refreshInfo(); // 首次填充
		appendChildToDest(infoWidget);

		/* 点击刷新 */
		infoWidget.addEventListener("click", refreshInfo);

		/* 3. 创建模态框 */
		const modal = document.createElement("div");
		modal.className = "scoreboard-modal";
		modal.innerHTML =
			'<div class="scoreboard-content">' +
			'<div class="scoreboard-header">' +
			'<div class="scoreboard-title">排行榜</div>' +
			'<span class="scoreboard-close-btn">×</span>' +
			'</div>' +
			'<div class="selector-area"></div>' +
			'<div class="table-container"><div class="no-data">请选择榜单分类</div></div>' +
			'</div>';
		document.body.appendChild(modal);

		/* 4. 关闭按钮事件 */
		modal.querySelector(".scoreboard-close-btn").addEventListener("click", () => { modal.style.display = "none"; });

		/* 5. 主点击事件 */
		//btn.onclick = (() => {
		btn.addEventListener("click", () => {
			modal.style.display = "block";
			rebuildSelectors();
		});

		/* 6. 将扁平 tuples 转成树（仅用数组/对象字面量，无点号访问） */
		function tuplesToTree(tuples) {
			const root = Object.create(null); // 无 prototype
			for (let i = 0; i < tuples.length; i++) {
				const path = tuples[i][0];
				const table = tuples[i][1];
				let cur = root;
				for (let j = 0; j < path.length; j++) {
					const key = path[j];
					if (cur[key] === undefined) cur[key] = Object.create(null);
					cur = cur[key];
				}
				/* 用特殊键存表，避免与子节点冲突 */
				cur["__table__"] = table;
			}
			return root;
		}

		/* 7. 重建选择器 */
		function rebuildSelectors() {
			const area = modal.querySelector(".selector-area");
			area.innerHTML = "";
			const tuples = core.getScoreboard();
			const tree = tuplesToTree(tuples);
			buildSelectorRows(tree, area, []);
		}

		/* 8. 递归生成选择器行 */
		function buildSelectorRows(node, container, path) {
			const keys = Object.keys(node).filter(k => k !== "__table__");
			if (keys.length === 0) {
				/* 叶子：显示表格 */
				const tbl = node["__table__"];
				showTable(tbl);
				return;
			}
			const row = document.createElement("div");
			row.className = "selector-row";
			const lbl = document.createElement("div");
			lbl.className = "selector-label";
			lbl.textContent = path.length === 0 ? "分类" : path[path.length - 1];
			const sel = document.createElement("select");
			sel.className = "selector-combo";
			for (let i = 0; i < keys.length; i++) {
				const opt = document.createElement("option");
				opt.value = keys[i];
				opt.textContent = keys[i];
				sel.appendChild(opt);
			}
			sel.addEventListener("change", function () {
				/* 清除后续行 */
				let found = false;
				const rows = container.querySelectorAll(".selector-row");
				for (let i = 0; i < rows.length; i++) {
					if (found) rows[i].remove();
					if (rows[i] === row) found = true;
				}
				/* 继续下探 */
				const newPath = path.concat([this.value]);
				buildSelectorRows(node[this.value], container, newPath);
			});
			row.appendChild(lbl);
			row.appendChild(sel);
			container.appendChild(row);
			/* 默认选第一项并下探 */
			if (keys.length > 0) {
				const newPath = path.concat([keys[0]]);
				buildSelectorRows(node[keys[0]], container, newPath);
			}
		}

		/* 9. 显示表格 */


		function showTable(data) {
			const box = modal.querySelector(".table-container");
			if (!Array.isArray(data) || data.length === 0) { box.innerHTML = '<div class="no-data">暂无数据</div>'; return; }
			let html = "<table class='scoreboard-table'><thead><tr>";
			for (let i = 0; i < data[0].length; i++) html += "<th>" + escapeHtml(data[0][i]) + "</th>";
			html += "</tr></thead><tbody>";
			for (let r = 1; r < data.length; r++) {
				html += "<tr>";
				for (let c = 0; c < data[r].length; c++) html += "<td>" + escapeHtml(data[r][c]) + "</td>";
				html += "</tr>";
			}
			html += "</tbody></table>";
			box.innerHTML = html;
		}

		/* 10. 对外接口 */
		return {
			show: () => {
				modal.style.display = "block";
				rebuildSelectors();
			},
			hide: () => { modal.style.display = "none"; }
		};
	})();

	// TODOLIST
	// 1. 部署时刻把获取提交列表url修复成部署状态
	// 2. 产生多个排名榜的页面，具体来说将玩家以及提交排上序
	// 3. 将赛季10以后的信息更新上去。确保逻辑避免将未来赛季列入榜单展示环节
	// 4. 以前的静态榜单是一track一个scorer的，或者一个scorer能够对应到一次提交的。现在是否还适用？一个赛季的一个scorer追溯到一次提交是理想的设计
	// 一个scorer一个人可以映射到多个track的多次提交记录，是合理的，使用details来折叠即可。每个scorer应该记录依赖哪些track.每个scorer也应记录何时截止。
	// 5. 产生一个能够交互显示当前榜单情况的页面，并且分析出一个记分点的所有提交（每个相关track上这个用户的有效提交，包括提交本身和提交排名）
	// 6. 最终榜单
	// 7. 榜单外，外部界面显示uid和总排名和分数
	// 8. 此外，历史赛季重现功能？？？
	// 9. 测试，如何有效测试。包括作为普通用户显示自己分数栏。以及新赛季。榜单只应该显示当前和过去赛季，不能显示未来赛季
	// 10. 折线图？榜单显示页面很可能应当使用iframe比较好，渲染折线图的js只需要在iframe里加载？
	// 11. 为后续调整积分节奏准备

	// 12. 调整顺序。应当把最近的那个榜放到前面展示，目前顺序是最早的榜，意义不大
	// 13. 和另一些比赛的榜单做类比。不需要搞得很复杂，直接把一个玩家，制作玩家个人界面（是否应该做一个超链接功能），显示这个玩家的slice，每个赛季的得分
	// 14. todo avoid xss but allow designed hyperlink
	// 15. 获取榜单里的重试？榜单可能获取失败

}
}